You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProjectHelper.java 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999, 2000 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant;
  55. import java.io.*;
  56. import java.util.*;
  57. import org.xml.sax.*;
  58. import org.w3c.dom.*;
  59. import org.apache.tools.ant.taskdefs.*;
  60. import javax.xml.parsers.*;
  61. /**
  62. * Configures a Project (complete with Targets and Tasks) based on
  63. * a XML build file.
  64. *
  65. * @author duncan@x180.com
  66. */
  67. public class ProjectHelper {
  68. private static SAXParserFactory parserFactory = null;
  69. private org.xml.sax.Parser parser;
  70. private Project project;
  71. private File buildFile;
  72. private File buildFileParent;
  73. private Locator locator;
  74. /**
  75. * Configures the Project with the contents of the specified XML file.
  76. */
  77. public static void configureProject(Project project, File buildFile) throws BuildException {
  78. new ProjectHelper(project, buildFile).parse();
  79. }
  80. /**
  81. * Constructs a new Ant parser for the specified XML file.
  82. */
  83. private ProjectHelper(Project project, File buildFile) {
  84. this.project = project;
  85. this.buildFile = new File(buildFile.getAbsolutePath());
  86. buildFileParent = new File(this.buildFile.getParent());
  87. }
  88. /**
  89. * Parses the project file.
  90. */
  91. private void parse() throws BuildException {
  92. try {
  93. SAXParser saxParser = getParserFactory().newSAXParser();
  94. parser = saxParser.getParser();
  95. saxParser.parse(buildFile, new RootHandler());
  96. }
  97. catch(ParserConfigurationException exc) {
  98. throw new BuildException("Parser has not been configured correctly", exc);
  99. }
  100. catch(SAXParseException exc) {
  101. Location location =
  102. new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
  103. Throwable t = exc.getException();
  104. if (t instanceof BuildException) {
  105. BuildException be = (BuildException) t;
  106. if (be.getLocation() == Location.UNKNOWN_LOCATION) {
  107. be.setLocation(location);
  108. }
  109. throw be;
  110. }
  111. throw new BuildException(exc.getMessage(), t, location);
  112. }
  113. catch(SAXException exc) {
  114. Throwable t = exc.getException();
  115. if (t instanceof BuildException) {
  116. throw (BuildException) t;
  117. }
  118. throw new BuildException(exc.getMessage(), t);
  119. }
  120. catch(FileNotFoundException exc) {
  121. throw new BuildException(exc);
  122. }
  123. catch(IOException exc) {
  124. throw new BuildException("Error reading project file", exc);
  125. }
  126. }
  127. /**
  128. * The common superclass for all sax event handlers in Ant. Basically
  129. * throws an exception in each method, so subclasses should override
  130. * what they can handle.
  131. *
  132. * Each type of xml element (task, target, etc) in ant will
  133. * have its own subclass of AbstractHandler.
  134. *
  135. * In the constructor, this class takes over the handling of sax
  136. * events from the parent handler, and returns
  137. * control back to the parent in the endElement method.
  138. */
  139. private class AbstractHandler extends HandlerBase {
  140. protected DocumentHandler parentHandler;
  141. public AbstractHandler(DocumentHandler parentHandler) {
  142. this.parentHandler = parentHandler;
  143. // Start handling SAX events
  144. parser.setDocumentHandler(this);
  145. }
  146. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  147. throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
  148. }
  149. public void characters(char[] buf, int start, int end) throws SAXParseException {
  150. String s = new String(buf, start, end).trim();
  151. if (s.length() > 0) {
  152. throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
  153. }
  154. }
  155. public void endElement(String name) throws SAXException {
  156. // Let parent resume handling SAX events
  157. parser.setDocumentHandler(parentHandler);
  158. }
  159. }
  160. /**
  161. * Handler for the root element. It's only child must be the "project" element.
  162. */
  163. private class RootHandler extends HandlerBase {
  164. /**
  165. * resolve file: URIs as relative to the build file.
  166. */
  167. public InputSource resolveEntity(String publicId,
  168. String systemId) {
  169. if (systemId.startsWith("file:")) {
  170. String path = systemId.substring(5);
  171. File file = new File(path);
  172. if (!file.isAbsolute()) {
  173. file = new File(buildFileParent, path);
  174. }
  175. try {
  176. return new InputSource(new FileInputStream(file));
  177. } catch (FileNotFoundException fne) {
  178. project.log(file.getAbsolutePath()+" could not be found",
  179. Project.MSG_WARN);
  180. }
  181. }
  182. // use default if not file or file not found
  183. return null;
  184. }
  185. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  186. if (tag.equals("project")) {
  187. new ProjectHandler(this).init(tag, attrs);
  188. } else {
  189. throw new SAXParseException("Config file is not of expected XML type", locator);
  190. }
  191. }
  192. public void setDocumentLocator(Locator locator) {
  193. ProjectHelper.this.locator = locator;
  194. }
  195. }
  196. /**
  197. * Handler for the top level "project" element.
  198. */
  199. private class ProjectHandler extends AbstractHandler {
  200. /**
  201. * Tasks not living in a target need special processing
  202. * in endElement (this is the right place to call execute).
  203. */
  204. protected TaskHandler childHandler = null;
  205. public ProjectHandler(DocumentHandler parentHandler) {
  206. super(parentHandler);
  207. }
  208. public void init(String tag, AttributeList attrs) throws SAXParseException {
  209. String def = null;
  210. String name = null;
  211. String id = null;
  212. String baseDir = null;
  213. for (int i = 0; i < attrs.getLength(); i++) {
  214. String key = attrs.getName(i);
  215. String value = attrs.getValue(i);
  216. if (key.equals("default")) {
  217. def = value;
  218. } else if (key.equals("name")) {
  219. name = value;
  220. } else if (key.equals("id")) {
  221. id = value;
  222. } else if (key.equals("basedir")) {
  223. baseDir = value;
  224. } else {
  225. throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator);
  226. }
  227. }
  228. project.setDefaultTarget(def);
  229. if (name != null) {
  230. project.setName(name);
  231. project.addReference(name, project);
  232. }
  233. if (id != null) project.addReference(id, project);
  234. if (project.getProperty("basedir") != null) {
  235. project.setBasedir(project.getProperty("basedir"));
  236. } else {
  237. if (baseDir == null) {
  238. project.setBasedir(buildFileParent.getAbsolutePath());
  239. } else {
  240. // check whether the user has specified an absolute path
  241. if ((new File(baseDir)).isAbsolute()) {
  242. project.setBasedir(baseDir);
  243. } else {
  244. project.setBasedir((new File(buildFileParent, baseDir)).getAbsolutePath());
  245. }
  246. }
  247. }
  248. }
  249. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  250. if (name.equals("taskdef")) {
  251. handleTaskdef(name, attrs);
  252. } else if (name.equals("property")) {
  253. handleProperty(name, attrs);
  254. } else if (name.equals("target")) {
  255. handleTarget(name, attrs);
  256. } else if (project.getDataTypeDefinitions().get(name) != null) {
  257. handleDataType(name, attrs);
  258. } else {
  259. throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
  260. }
  261. }
  262. private void handleTaskdef(String name, AttributeList attrs) throws SAXParseException {
  263. childHandler = new TaskHandler(this, null);
  264. childHandler.init(name, attrs);
  265. }
  266. private void handleProperty(String name, AttributeList attrs) throws SAXParseException {
  267. childHandler = new TaskHandler(this, null);
  268. childHandler.init(name, attrs);
  269. }
  270. private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
  271. new TargetHandler(this).init(tag, attrs);
  272. }
  273. private void handleDataType(String name, AttributeList attrs) throws SAXParseException {
  274. new DataTypeHandler(this).init(name, attrs);
  275. }
  276. public void endElement(String name) throws SAXException {
  277. if (childHandler != null) {
  278. childHandler.finished();
  279. childHandler = null;
  280. }
  281. super.endElement(name);
  282. }
  283. }
  284. /**
  285. * Handler for "target" elements.
  286. */
  287. private class TargetHandler extends AbstractHandler {
  288. private Target target;
  289. public TargetHandler(DocumentHandler parentHandler) {
  290. super(parentHandler);
  291. }
  292. public void init(String tag, AttributeList attrs) throws SAXParseException {
  293. String name = null;
  294. String depends = "";
  295. String ifCond = null;
  296. String unlessCond = null;
  297. String id = null;
  298. String description = null;
  299. for (int i = 0; i < attrs.getLength(); i++) {
  300. String key = attrs.getName(i);
  301. String value = attrs.getValue(i);
  302. if (key.equals("name")) {
  303. name = value;
  304. } else if (key.equals("depends")) {
  305. depends = value;
  306. } else if (key.equals("if")) {
  307. ifCond = value;
  308. } else if (key.equals("unless")) {
  309. unlessCond = value;
  310. } else if (key.equals("id")) {
  311. id = value;
  312. } else if (key.equals("description")) {
  313. description = value;
  314. } else {
  315. throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
  316. }
  317. }
  318. if (name == null) {
  319. throw new SAXParseException("target element appears without a name attribute", locator);
  320. }
  321. target = new Target();
  322. target.setName(name);
  323. target.setIf(ifCond);
  324. target.setUnless(unlessCond);
  325. target.setDescription(description);
  326. project.addTarget(name, target);
  327. if (id != null && !id.equals(""))
  328. project.addReference(id, target);
  329. // take care of dependencies
  330. if (depends.length() > 0) {
  331. StringTokenizer tok =
  332. new StringTokenizer(depends, ",", false);
  333. while (tok.hasMoreTokens()) {
  334. target.addDependency(tok.nextToken().trim());
  335. }
  336. }
  337. }
  338. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  339. new TaskHandler(this, target).init(name, attrs);
  340. }
  341. }
  342. /**
  343. * Handler for all task elements.
  344. */
  345. private class TaskHandler extends AbstractHandler {
  346. private Target target;
  347. private Task task;
  348. private RuntimeConfigurable wrapper = null;
  349. public TaskHandler(DocumentHandler parentHandler, Target target) {
  350. super(parentHandler);
  351. this.target = target;
  352. }
  353. public void init(String tag, AttributeList attrs) throws SAXParseException {
  354. try {
  355. task = project.createTask(tag);
  356. } catch (BuildException e) {
  357. // swallow here, will be thrown again in
  358. // UnknownElement.maybeConfigure if the problem persists.
  359. }
  360. if (task == null) {
  361. task = new UnknownElement(tag);
  362. task.setProject(project);
  363. }
  364. task.setLocation(new Location(buildFile.toString(), locator.getLineNumber(), locator.getColumnNumber()));
  365. configureId(task, attrs);
  366. // Top level tasks don't have associated targets
  367. if (target != null) {
  368. task.setOwningTarget(target);
  369. target.addTask(task);
  370. task.init();
  371. wrapper = task.getRuntimeConfigurableWrapper();
  372. wrapper.setAttributes(attrs);
  373. } else {
  374. task.init();
  375. configure(task, attrs, project);
  376. }
  377. }
  378. public void finished() {
  379. if (task != null && target == null) {
  380. task.execute();
  381. }
  382. }
  383. public void characters(char[] buf, int start, int end) throws SAXParseException {
  384. if (wrapper == null) {
  385. try {
  386. addText(task, buf, start, end);
  387. } catch (BuildException exc) {
  388. throw new SAXParseException(exc.getMessage(), locator, exc);
  389. }
  390. } else {
  391. wrapper.addText(buf, start, end);
  392. }
  393. }
  394. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  395. new NestedElementHandler(this, task, wrapper).init(name, attrs);
  396. }
  397. }
  398. /**
  399. * Handler for all nested properties.
  400. */
  401. private class NestedElementHandler extends AbstractHandler {
  402. private Object target;
  403. private Object child;
  404. private RuntimeConfigurable parentWrapper;
  405. private RuntimeConfigurable childWrapper = null;
  406. public NestedElementHandler(DocumentHandler parentHandler,
  407. Object target,
  408. RuntimeConfigurable parentWrapper) {
  409. super(parentHandler);
  410. if (target instanceof TaskAdapter) {
  411. this.target = ((TaskAdapter) target).getProxy();
  412. } else {
  413. this.target = target;
  414. }
  415. this.parentWrapper = parentWrapper;
  416. }
  417. public void init(String propType, AttributeList attrs) throws SAXParseException {
  418. Class targetClass = target.getClass();
  419. IntrospectionHelper ih =
  420. IntrospectionHelper.getHelper(targetClass);
  421. try {
  422. if (target instanceof UnknownElement) {
  423. child = new UnknownElement(propType.toLowerCase());
  424. ((UnknownElement) target).addChild((UnknownElement) child);
  425. } else {
  426. child = ih.createElement(target, propType.toLowerCase());
  427. }
  428. configureId(child, attrs);
  429. if (parentWrapper != null) {
  430. childWrapper = new RuntimeConfigurable(child);
  431. childWrapper.setAttributes(attrs);
  432. parentWrapper.addChild(childWrapper);
  433. } else {
  434. configure(child, attrs, project);
  435. }
  436. } catch (BuildException exc) {
  437. throw new SAXParseException(exc.getMessage(), locator, exc);
  438. }
  439. }
  440. public void characters(char[] buf, int start, int end) throws SAXParseException {
  441. if (parentWrapper == null) {
  442. try {
  443. addText(child, buf, start, end);
  444. } catch (BuildException exc) {
  445. throw new SAXParseException(exc.getMessage(), locator, exc);
  446. }
  447. } else {
  448. childWrapper.addText(buf, start, end);
  449. }
  450. }
  451. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  452. new NestedElementHandler(this, child, childWrapper).init(name, attrs);
  453. }
  454. }
  455. /**
  456. * Handler for all data types at global level.
  457. */
  458. private class DataTypeHandler extends AbstractHandler {
  459. private Object element;
  460. public DataTypeHandler(DocumentHandler parentHandler) {
  461. super(parentHandler);
  462. }
  463. public void init(String propType, AttributeList attrs) throws SAXParseException {
  464. try {
  465. element = project.createDataType(propType);
  466. if (element == null) {
  467. throw new BuildException("Unknown data type "+propType);
  468. }
  469. configureId(element, attrs);
  470. configure(element, attrs, project);
  471. } catch (BuildException exc) {
  472. throw new SAXParseException(exc.getMessage(), locator, exc);
  473. }
  474. }
  475. public void characters(char[] buf, int start, int end) throws SAXParseException {
  476. try {
  477. addText(element, buf, start, end);
  478. } catch (BuildException exc) {
  479. throw new SAXParseException(exc.getMessage(), locator, exc);
  480. }
  481. }
  482. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  483. new NestedElementHandler(this, element, null).init(name, attrs);
  484. }
  485. }
  486. public static void configure(Object target, AttributeList attrs,
  487. Project project) throws BuildException {
  488. if( target instanceof TaskAdapter )
  489. target=((TaskAdapter)target).getProxy();
  490. IntrospectionHelper ih =
  491. IntrospectionHelper.getHelper(target.getClass());
  492. for (int i = 0; i < attrs.getLength(); i++) {
  493. // reflect these into the target
  494. String value=replaceProperties(attrs.getValue(i),
  495. project.getProperties() );
  496. try {
  497. ih.setAttribute(project, target,
  498. attrs.getName(i).toLowerCase(), value);
  499. } catch (BuildException be) {
  500. // id attribute must be set externally
  501. if (!attrs.getName(i).equals("id")) {
  502. throw be;
  503. }
  504. }
  505. }
  506. }
  507. /**
  508. * Adds the content of #PCDATA sections to an element.
  509. */
  510. public static void addText(Object target, char[] buf, int start, int end)
  511. throws BuildException {
  512. addText(target, new String(buf, start, end));
  513. }
  514. /**
  515. * Adds the content of #PCDATA sections to an element.
  516. */
  517. public static void addText(Object target, String text)
  518. throws BuildException {
  519. if (text == null || text.trim().length() == 0) {
  520. return;
  521. }
  522. if(target instanceof TaskAdapter)
  523. target = ((TaskAdapter) target).getProxy();
  524. IntrospectionHelper.getHelper(target.getClass()).addText(target, text);
  525. }
  526. /** Replace ${NAME} with the property value
  527. */
  528. public static String replaceProperties( String value, Hashtable keys )
  529. throws BuildException
  530. {
  531. // XXX use Map instead of proj, it's too heavy
  532. // XXX need to replace this code with something better.
  533. StringBuffer sb=new StringBuffer();
  534. int i=0;
  535. int prev=0;
  536. // assert value!=nil
  537. int pos;
  538. while( (pos=value.indexOf( "$", prev )) >= 0 ) {
  539. if(pos>0) {
  540. sb.append( value.substring( prev, pos ) );
  541. }
  542. if( pos == (value.length() - 1)) {
  543. sb.append('$');
  544. prev = pos + 1;
  545. }
  546. else if (value.charAt( pos + 1 ) != '{' ) {
  547. sb.append( value.charAt( pos + 1 ) );
  548. prev=pos+2; // XXX
  549. } else {
  550. int endName=value.indexOf( '}', pos );
  551. if( endName < 0 ) {
  552. throw new BuildException("Syntax error in prop: " +
  553. value );
  554. }
  555. String n=value.substring( pos+2, endName );
  556. String v= (keys.containsKey(n)) ? (String) keys.get( n )
  557. : "${"+n+"}";
  558. //System.out.println("N: " + n + " " + " V:" + v);
  559. sb.append( v );
  560. prev=endName+1;
  561. }
  562. }
  563. if( prev < value.length() ) sb.append( value.substring( prev ) );
  564. // System.out.println("After replace: " + sb.toString());
  565. // System.out.println("Before replace: " + value);
  566. return sb.toString();
  567. }
  568. private static SAXParserFactory getParserFactory() {
  569. if (parserFactory == null) {
  570. parserFactory = SAXParserFactory.newInstance();
  571. }
  572. return parserFactory;
  573. }
  574. /**
  575. * Scan AttributeList for the id attribute and maybe add a
  576. * reference to project.
  577. *
  578. * <p>Moved out of {@link #configure configure} to make it happen
  579. * at parser time.</p>
  580. */
  581. private void configureId(Object target, AttributeList attr) {
  582. String id = attr.getValue("id");
  583. if (id != null) {
  584. project.addReference(id, target);
  585. }
  586. }
  587. }