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.

ProjectHelperImpl.java 46 kB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 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", "Ant", 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.helper;
  55. import java.io.File;
  56. import java.io.FileInputStream;
  57. import java.io.FileNotFoundException;
  58. import java.io.IOException;
  59. import java.io.UnsupportedEncodingException;
  60. import java.util.Locale;
  61. import org.xml.sax.Locator;
  62. import org.xml.sax.InputSource;
  63. import org.xml.sax.HandlerBase;
  64. import org.xml.sax.SAXException;
  65. import org.xml.sax.SAXParseException;
  66. import org.xml.sax.DocumentHandler;
  67. import org.xml.sax.AttributeList;
  68. import org.xml.sax.helpers.XMLReaderAdapter;
  69. import org.apache.tools.ant.ProjectHelper;
  70. import org.apache.tools.ant.UnknownElement;
  71. import org.apache.tools.ant.Project;
  72. import org.apache.tools.ant.BuildException;
  73. import org.apache.tools.ant.Target;
  74. import org.apache.tools.ant.Task;
  75. import org.apache.tools.ant.RuntimeConfigurable;
  76. import org.apache.tools.ant.IntrospectionHelper;
  77. import org.apache.tools.ant.TaskContainer;
  78. import org.apache.tools.ant.Location;
  79. import org.apache.tools.ant.TaskAdapter;
  80. import org.apache.tools.ant.util.JAXPUtils;
  81. /**
  82. * Original helper.
  83. *
  84. * @author duncan@x180.com
  85. */
  86. public class ProjectHelperImpl extends ProjectHelper {
  87. /**
  88. * SAX 1 style parser used to parse the given file. This may
  89. * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
  90. */
  91. private org.xml.sax.Parser parser;
  92. /** The project to configure. */
  93. private Project project;
  94. /** The configuration file to parse. */
  95. private File buildFile;
  96. /**
  97. * Parent directory of the build file. Used for resolving entities
  98. * and setting the project's base directory.
  99. */
  100. private File buildFileParent;
  101. /**
  102. * Locator for the configuration file parser.
  103. * Used for giving locations of errors etc.
  104. */
  105. private Locator locator;
  106. /**
  107. * Parses the project file, configuring the project as it goes.
  108. *
  109. * @param project project instance to be configured.
  110. * @param source the source from which the project is read.
  111. * @exception BuildException if the configuration is invalid or cannot
  112. * be read.
  113. */
  114. public void parse(Project project, Object source) throws BuildException {
  115. if (!(source instanceof File)) {
  116. throw new BuildException("Only File source supported by default plugin");
  117. }
  118. File buildFile = (File) source;
  119. FileInputStream inputStream = null;
  120. InputSource inputSource = null;
  121. this.project = project;
  122. this.buildFile = new File(buildFile.getAbsolutePath());
  123. buildFileParent = new File(this.buildFile.getParent());
  124. try {
  125. try {
  126. parser = JAXPUtils.getParser();
  127. } catch (BuildException e) {
  128. parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
  129. }
  130. String uri = "file:" + buildFile.getAbsolutePath().replace('\\', '/');
  131. for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
  132. uri = uri.substring(0, index) + "%23" + uri.substring(index + 1);
  133. }
  134. inputStream = new FileInputStream(buildFile);
  135. inputSource = new InputSource(inputStream);
  136. inputSource.setSystemId(uri);
  137. project.log("parsing buildfile " + buildFile + " with URI = "
  138. + uri, Project.MSG_VERBOSE);
  139. HandlerBase hb = new RootHandler(this);
  140. parser.setDocumentHandler(hb);
  141. parser.setEntityResolver(hb);
  142. parser.setErrorHandler(hb);
  143. parser.setDTDHandler(hb);
  144. parser.parse(inputSource);
  145. } catch (SAXParseException exc) {
  146. Location location =
  147. new Location(exc.getSystemId(), exc.getLineNumber(),
  148. exc.getColumnNumber());
  149. Throwable t = exc.getException();
  150. if (t instanceof BuildException) {
  151. BuildException be = (BuildException) t;
  152. if (be.getLocation() == Location.UNKNOWN_LOCATION) {
  153. be.setLocation(location);
  154. }
  155. throw be;
  156. }
  157. throw new BuildException(exc.getMessage(), t, location);
  158. } catch (SAXException exc) {
  159. Throwable t = exc.getException();
  160. if (t instanceof BuildException) {
  161. throw (BuildException) t;
  162. }
  163. throw new BuildException(exc.getMessage(), t);
  164. } catch (FileNotFoundException exc) {
  165. throw new BuildException(exc);
  166. } catch (UnsupportedEncodingException exc) {
  167. throw new BuildException("Encoding of project file is invalid.",
  168. exc);
  169. } catch (IOException exc) {
  170. throw new BuildException("Error reading project file: "
  171. + exc.getMessage(), exc);
  172. } finally {
  173. if (inputStream != null) {
  174. try {
  175. inputStream.close();
  176. } catch (IOException ioe) {
  177. // ignore this
  178. }
  179. }
  180. }
  181. }
  182. /**
  183. * The common superclass for all SAX event handlers used to parse
  184. * the configuration file. Each method just throws an exception,
  185. * so subclasses should override what they can handle.
  186. *
  187. * Each type of XML element (task, target, etc.) in Ant has
  188. * a specific subclass.
  189. *
  190. * In the constructor, this class takes over the handling of SAX
  191. * events from the parent handler and returns
  192. * control back to the parent in the endElement method.
  193. */
  194. static class AbstractHandler extends HandlerBase {
  195. /**
  196. * Previous handler for the document.
  197. * When the next element is finished, control returns
  198. * to this handler.
  199. */
  200. protected DocumentHandler parentHandler;
  201. /** Helper impl. With non-static internal classes, the compiler will generate
  202. this automatically - but this will fail with some compilers ( reporting
  203. "Expecting to find object/array on stack" ). If we pass it
  204. explicitely it'll work with more compilers.
  205. */
  206. ProjectHelperImpl helperImpl;
  207. /**
  208. * Creates a handler and sets the parser to use it
  209. * for the current element.
  210. *
  211. * @param helperImpl the ProjectHelperImpl instance associated
  212. * with this handler.
  213. *
  214. * @param parentHandler The handler which should be restored to the
  215. * parser at the end of the element.
  216. * Must not be <code>null</code>.
  217. */
  218. public AbstractHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
  219. this.parentHandler = parentHandler;
  220. this.helperImpl = helperImpl;
  221. // Start handling SAX events
  222. helperImpl.parser.setDocumentHandler(this);
  223. }
  224. /**
  225. * Handles the start of an element. This base implementation just
  226. * throws an exception.
  227. *
  228. * @param tag The name of the element being started.
  229. * Will not be <code>null</code>.
  230. * @param attrs Attributes of the element being started.
  231. * Will not be <code>null</code>.
  232. *
  233. * @exception SAXParseException if this method is not overridden, or in
  234. * case of error in an overridden version
  235. */
  236. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  237. throw new SAXParseException("Unexpected element \"" + tag + "\"", helperImpl.locator);
  238. }
  239. /**
  240. * Handles text within an element. This base implementation just
  241. * throws an exception.
  242. *
  243. * @param buf A character array of the text within the element.
  244. * Will not be <code>null</code>.
  245. * @param start The start element in the array.
  246. * @param count The number of characters to read from the array.
  247. *
  248. * @exception SAXParseException if this method is not overridden, or in
  249. * case of error in an overridden version
  250. */
  251. public void characters(char[] buf, int start, int count) throws SAXParseException {
  252. String s = new String(buf, start, count).trim();
  253. if (s.length() > 0) {
  254. throw new SAXParseException("Unexpected text \"" + s + "\"", helperImpl.locator);
  255. }
  256. }
  257. /**
  258. * Called when this element and all elements nested into it have been
  259. * handled.
  260. */
  261. protected void finished() {}
  262. /**
  263. * Handles the end of an element. Any required clean-up is performed
  264. * by the finished() method and then the original handler is restored to
  265. * the parser.
  266. *
  267. * @param name The name of the element which is ending.
  268. * Will not be <code>null</code>.
  269. *
  270. * @exception SAXException in case of error (not thrown in
  271. * this implementation)
  272. *
  273. * @see #finished()
  274. */
  275. public void endElement(String name) throws SAXException {
  276. finished();
  277. // Let parent resume handling SAX events
  278. helperImpl.parser.setDocumentHandler(parentHandler);
  279. }
  280. }
  281. /**
  282. * Handler for the root element. Its only child must be the "project" element.
  283. */
  284. static class RootHandler extends HandlerBase {
  285. ProjectHelperImpl helperImpl;
  286. public RootHandler(ProjectHelperImpl helperImpl) {
  287. this.helperImpl = helperImpl;
  288. }
  289. /**
  290. * Resolves file: URIs relative to the build file.
  291. *
  292. * @param publicId The public identifer, or <code>null</code>
  293. * if none is available. Ignored in this
  294. * implementation.
  295. * @param systemId The system identifier provided in the XML
  296. * document. Will not be <code>null</code>.
  297. */
  298. public InputSource resolveEntity(String publicId,
  299. String systemId) {
  300. helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
  301. if (systemId.startsWith("file:")) {
  302. String path = systemId.substring(5);
  303. int index = path.indexOf("file:");
  304. // we only have to handle these for backward compatibility
  305. // since they are in the FAQ.
  306. while (index != -1) {
  307. path = path.substring(0, index) + path.substring(index + 5);
  308. index = path.indexOf("file:");
  309. }
  310. String entitySystemId = path;
  311. index = path.indexOf("%23");
  312. // convert these to #
  313. while (index != -1) {
  314. path = path.substring(0, index) + "#" + path.substring(index + 3);
  315. index = path.indexOf("%23");
  316. }
  317. File file = new File(path);
  318. if (!file.isAbsolute()) {
  319. file = new File(helperImpl.buildFileParent, path);
  320. }
  321. try {
  322. InputSource inputSource = new InputSource(new FileInputStream(file));
  323. inputSource.setSystemId("file:" + entitySystemId);
  324. return inputSource;
  325. } catch (FileNotFoundException fne) {
  326. helperImpl.project.log(file.getAbsolutePath() + " could not be found",
  327. Project.MSG_WARN);
  328. }
  329. }
  330. // use default if not file or file not found
  331. return null;
  332. }
  333. /**
  334. * Handles the start of a project element. A project handler is created
  335. * and initialised with the element name and attributes.
  336. *
  337. * @param tag The name of the element being started.
  338. * Will not be <code>null</code>.
  339. * @param attrs Attributes of the element being started.
  340. * Will not be <code>null</code>.
  341. *
  342. * @exception SAXParseException if the tag given is not
  343. * <code>"project"</code>
  344. */
  345. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  346. if (tag.equals("project")) {
  347. new ProjectHandler(helperImpl, this).init(tag, attrs);
  348. } else {
  349. throw new SAXParseException("Config file is not of expected XML type", helperImpl.locator);
  350. }
  351. }
  352. /**
  353. * Sets the locator in the project helper for future reference.
  354. *
  355. * @param locator The locator used by the parser.
  356. * Will not be <code>null</code>.
  357. */
  358. public void setDocumentLocator(Locator locator) {
  359. helperImpl.locator = locator;
  360. }
  361. }
  362. /**
  363. * Handler for the top level "project" element.
  364. */
  365. static class ProjectHandler extends AbstractHandler {
  366. /**
  367. * Constructor which just delegates to the superconstructor.
  368. *
  369. * @param parentHandler The handler which should be restored to the
  370. * parser at the end of the element.
  371. * Must not be <code>null</code>.
  372. */
  373. public ProjectHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
  374. super(helperImpl, parentHandler);
  375. }
  376. /**
  377. * Initialisation routine called after handler creation
  378. * with the element name and attributes. The attributes which
  379. * this handler can deal with are: <code>"default"</code>,
  380. * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
  381. *
  382. * @param tag Name of the element which caused this handler
  383. * to be created. Should not be <code>null</code>.
  384. * Ignored in this implementation.
  385. * @param attrs Attributes of the element which caused this
  386. * handler to be created. Must not be <code>null</code>.
  387. *
  388. * @exception SAXParseException if an unexpected attribute is
  389. * encountered or if the <code>"default"</code> attribute
  390. * is missing.
  391. */
  392. public void init(String tag, AttributeList attrs) throws SAXParseException {
  393. String def = null;
  394. String name = null;
  395. String id = null;
  396. String baseDir = null;
  397. for (int i = 0; i < attrs.getLength(); i++) {
  398. String key = attrs.getName(i);
  399. String value = attrs.getValue(i);
  400. if (key.equals("default")) {
  401. def = value;
  402. } else if (key.equals("name")) {
  403. name = value;
  404. } else if (key.equals("id")) {
  405. id = value;
  406. } else if (key.equals("basedir")) {
  407. baseDir = value;
  408. } else {
  409. throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"",
  410. helperImpl.locator);
  411. }
  412. }
  413. if (def != null) {
  414. helperImpl.project.setDefaultTarget(def);
  415. }
  416. if (name != null) {
  417. helperImpl.project.setName(name);
  418. helperImpl.project.addReference(name, helperImpl.project);
  419. }
  420. if (id != null) {
  421. helperImpl.project.addReference(id, helperImpl.project);
  422. }
  423. if (helperImpl.project.getProperty("basedir") != null) {
  424. helperImpl.project.setBasedir(helperImpl.project.getProperty("basedir"));
  425. } else {
  426. if (baseDir == null) {
  427. helperImpl.project.setBasedir(helperImpl.buildFileParent.getAbsolutePath());
  428. } else {
  429. // check whether the user has specified an absolute path
  430. if ((new File(baseDir)).isAbsolute()) {
  431. helperImpl.project.setBasedir(baseDir);
  432. } else {
  433. helperImpl.project.setBaseDir(helperImpl.project.resolveFile(baseDir,
  434. helperImpl.buildFileParent));
  435. }
  436. }
  437. }
  438. }
  439. /**
  440. * Handles the start of a top-level element within the project. An
  441. * appropriate handler is created and initialised with the details
  442. * of the element.
  443. *
  444. * @param name The name of the element being started.
  445. * Will not be <code>null</code>.
  446. * @param attrs Attributes of the element being started.
  447. * Will not be <code>null</code>.
  448. *
  449. * @exception SAXParseException if the tag given is not
  450. * <code>"taskdef"</code>, <code>"typedef"</code>,
  451. * <code>"property"</code>, <code>"target"</code>
  452. * or a data type definition
  453. */
  454. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  455. if (name.equals("target")) {
  456. handleTarget(name, attrs);
  457. } else if (helperImpl.project.getDataTypeDefinitions().get(name) != null) {
  458. handleDataType(name, attrs);
  459. } else if (helperImpl.project.getTaskDefinitions().get(name) != null) {
  460. handleTask(name, attrs);
  461. } else {
  462. throw new SAXParseException("Unexpected element \"" + name + "\"", helperImpl.locator);
  463. }
  464. }
  465. /**
  466. * Handles a task by creating a task handler and initialising
  467. * is with the details of the element.
  468. *
  469. * @param name The name of the element to be handled.
  470. * Will not be <code>null</code>.
  471. * @param attrs Attributes of the element to be handled.
  472. * Will not be <code>null</code>.
  473. *
  474. * @exception SAXParseException if an error occurs when initialising
  475. * the task handler
  476. *
  477. */
  478. private void handleTask(String name, AttributeList attrs) throws SAXParseException {
  479. (new TaskHandler(helperImpl, this, null, null, null)).init(name, attrs);
  480. }
  481. /**
  482. * Handles a target defintion element by creating a target handler
  483. * and initialising is with the details of the element.
  484. *
  485. * @param tag The name of the element to be handled.
  486. * Will not be <code>null</code>.
  487. * @param attrs Attributes of the element to be handled.
  488. * Will not be <code>null</code>.
  489. *
  490. * @exception SAXParseException if an error occurs initialising
  491. * the handler
  492. */
  493. private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
  494. new TargetHandler(helperImpl, this).init(tag, attrs);
  495. }
  496. /**
  497. * Handles a data type defintion element by creating a data type
  498. * handler and initialising is with the details of the element.
  499. *
  500. * @param name The name of the element to be handled.
  501. * Will not be <code>null</code>.
  502. * @param attrs Attributes of the element to be handled.
  503. * Will not be <code>null</code>.
  504. *
  505. * @exception SAXParseException if an error occurs initialising
  506. * the handler
  507. */
  508. private void handleDataType(String name, AttributeList attrs) throws SAXParseException {
  509. new DataTypeHandler(helperImpl, this).init(name, attrs);
  510. }
  511. }
  512. /**
  513. * Handler for "target" elements.
  514. */
  515. static class TargetHandler extends AbstractHandler {
  516. private Target target;
  517. /**
  518. * Constructor which just delegates to the superconstructor.
  519. *
  520. * @param parentHandler The handler which should be restored to the
  521. * parser at the end of the element.
  522. * Must not be <code>null</code>.
  523. */
  524. public TargetHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
  525. super(helperImpl, parentHandler);
  526. }
  527. /**
  528. * Initialisation routine called after handler creation
  529. * with the element name and attributes. The attributes which
  530. * this handler can deal with are: <code>"name"</code>,
  531. * <code>"depends"</code>, <code>"if"</code>,
  532. * <code>"unless"</code>, <code>"id"</code> and
  533. * <code>"description"</code>.
  534. *
  535. * @param tag Name of the element which caused this handler
  536. * to be created. Should not be <code>null</code>.
  537. * Ignored in this implementation.
  538. * @param attrs Attributes of the element which caused this
  539. * handler to be created. Must not be <code>null</code>.
  540. *
  541. * @exception SAXParseException if an unexpected attribute is encountered
  542. * or if the <code>"name"</code> attribute is missing.
  543. */
  544. public void init(String tag, AttributeList attrs) throws SAXParseException {
  545. String name = null;
  546. String depends = "";
  547. String ifCond = null;
  548. String unlessCond = null;
  549. String id = null;
  550. String description = null;
  551. for (int i = 0; i < attrs.getLength(); i++) {
  552. String key = attrs.getName(i);
  553. String value = attrs.getValue(i);
  554. if (key.equals("name")) {
  555. name = value;
  556. } else if (key.equals("depends")) {
  557. depends = value;
  558. } else if (key.equals("if")) {
  559. ifCond = value;
  560. } else if (key.equals("unless")) {
  561. unlessCond = value;
  562. } else if (key.equals("id")) {
  563. id = value;
  564. } else if (key.equals("description")) {
  565. description = value;
  566. } else {
  567. throw new SAXParseException("Unexpected attribute \"" + key + "\"", helperImpl.locator);
  568. }
  569. }
  570. if (name == null) {
  571. throw new SAXParseException("target element appears without a name attribute",
  572. helperImpl.locator);
  573. }
  574. target = new Target();
  575. target.setName(name);
  576. target.setIf(ifCond);
  577. target.setUnless(unlessCond);
  578. target.setDescription(description);
  579. helperImpl.project.addTarget(name, target);
  580. if (id != null && !id.equals("")) {
  581. helperImpl.project.addReference(id, target);
  582. }
  583. // take care of dependencies
  584. if (depends.length() > 0) {
  585. target.setDepends(depends);
  586. }
  587. }
  588. /**
  589. * Handles the start of an element within a target.
  590. *
  591. * @param name The name of the element being started.
  592. * Will not be <code>null</code>.
  593. * @param attrs Attributes of the element being started.
  594. * Will not be <code>null</code>.
  595. *
  596. * @exception SAXParseException if an error occurs when initialising
  597. * the appropriate child handler
  598. */
  599. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  600. if (helperImpl.project.getDataTypeDefinitions().get(name) != null) {
  601. new DataTypeHandler(helperImpl, this, target).init(name, attrs);
  602. } else {
  603. new TaskHandler(helperImpl, this, target, null, target).init(name, attrs);
  604. }
  605. }
  606. }
  607. /**
  608. * Handler for all task elements.
  609. */
  610. static class TaskHandler extends AbstractHandler {
  611. /** Containing target, if any. */
  612. private Target target;
  613. /**
  614. * Container for the task, if any. If target is
  615. * non-<code>null</code>, this must be too.
  616. */
  617. private TaskContainer container;
  618. /**
  619. * Task created by this handler.
  620. */
  621. private Task task;
  622. /**
  623. * Wrapper for the parent element, if any. The wrapper for this
  624. * element will be added to this wrapper as a child.
  625. */
  626. private RuntimeConfigurable parentWrapper;
  627. /**
  628. * Wrapper for this element which takes care of actually configuring
  629. * the element, if this element is contained within a target.
  630. * Otherwise the configuration is performed with the configure method.
  631. * @see ProjectHelper#configure(Object,AttributeList,Project)
  632. */
  633. private RuntimeConfigurable wrapper = null;
  634. /**
  635. * Constructor.
  636. *
  637. * @param parentHandler The handler which should be restored to the
  638. * parser at the end of the element.
  639. * Must not be <code>null</code>.
  640. *
  641. * @param container Container for the element.
  642. * May be <code>null</code> if the target is
  643. * <code>null</code> as well. If the
  644. * target is <code>null</code>, this parameter
  645. * is effectively ignored.
  646. *
  647. * @param parentWrapper Wrapper for the parent element, if any.
  648. * May be <code>null</code>. If the
  649. * target is <code>null</code>, this parameter
  650. * is effectively ignored.
  651. *
  652. * @param target Target this element is part of.
  653. * May be <code>null</code>.
  654. */
  655. public TaskHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
  656. TaskContainer container, RuntimeConfigurable parentWrapper, Target target) {
  657. super(helperImpl, parentHandler);
  658. this.container = container;
  659. this.parentWrapper = parentWrapper;
  660. this.target = target;
  661. }
  662. /**
  663. * Initialisation routine called after handler creation
  664. * with the element name and attributes. This configures
  665. * the element with its attributes and sets it up with
  666. * its parent container (if any). Nested elements are then
  667. * added later as the parser encounters them.
  668. *
  669. * @param tag Name of the element which caused this handler
  670. * to be created. Must not be <code>null</code>.
  671. *
  672. * @param attrs Attributes of the element which caused this
  673. * handler to be created. Must not be <code>null</code>.
  674. *
  675. * @exception SAXParseException in case of error (not thrown in
  676. * this implementation)
  677. */
  678. public void init(String tag, AttributeList attrs) throws SAXParseException {
  679. try {
  680. task = helperImpl.project.createTask(tag);
  681. } catch (BuildException e) {
  682. // swallow here, will be thrown again in
  683. // UnknownElement.maybeConfigure if the problem persists.
  684. }
  685. if (task == null) {
  686. task = new UnknownElement(tag);
  687. task.setProject(helperImpl.project);
  688. //XXX task.setTaskType(tag);
  689. task.setTaskName(tag);
  690. }
  691. task.setLocation(new Location(helperImpl.locator.getSystemId(), helperImpl.locator.getLineNumber(),
  692. helperImpl.locator.getColumnNumber()));
  693. helperImpl.configureId(task, attrs);
  694. // Top level tasks don't have associated targets
  695. if (target != null) {
  696. task.setOwningTarget(target);
  697. container.addTask(task);
  698. task.init();
  699. wrapper = task.getRuntimeConfigurableWrapper();
  700. wrapper.setAttributes(attrs);
  701. if (parentWrapper != null) {
  702. parentWrapper.addChild(wrapper);
  703. }
  704. } else {
  705. task.init();
  706. configure(task, attrs, helperImpl.project);
  707. }
  708. }
  709. /**
  710. * Executes the task if it is a top-level one.
  711. */
  712. protected void finished() {
  713. if (task != null && target == null) {
  714. task.execute();
  715. }
  716. }
  717. /**
  718. * Adds text to the task, using the wrapper if one is
  719. * available (in other words if the task is within a target)
  720. * or using addText otherwise.
  721. *
  722. * @param buf A character array of the text within the element.
  723. * Will not be <code>null</code>.
  724. * @param start The start element in the array.
  725. * @param count The number of characters to read from the array.
  726. *
  727. * @exception SAXParseException if the element doesn't support text
  728. *
  729. * @see ProjectHelper#addText(Project,Object,char[],int,int)
  730. */
  731. public void characters(char[] buf, int start, int count) throws SAXParseException {
  732. if (wrapper == null) {
  733. try {
  734. ProjectHelper.addText(helperImpl.project, task, buf, start, count);
  735. } catch (BuildException exc) {
  736. throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
  737. }
  738. } else {
  739. wrapper.addText(buf, start, count);
  740. }
  741. }
  742. /**
  743. * Handles the start of an element within a target. Task containers
  744. * will always use another task handler, and all other tasks
  745. * will always use a nested element handler.
  746. *
  747. * @param name The name of the element being started.
  748. * Will not be <code>null</code>.
  749. * @param attrs Attributes of the element being started.
  750. * Will not be <code>null</code>.
  751. *
  752. * @exception SAXParseException if an error occurs when initialising
  753. * the appropriate child handler
  754. */
  755. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  756. if (task instanceof TaskContainer) {
  757. // task can contain other tasks - no other nested elements possible
  758. new TaskHandler(helperImpl, this, (TaskContainer) task,
  759. wrapper, target).init(name, attrs);
  760. } else {
  761. new NestedElementHandler(helperImpl, this, task,
  762. wrapper, target).init(name, attrs);
  763. }
  764. }
  765. }
  766. /**
  767. * Handler for all nested properties.
  768. */
  769. static class NestedElementHandler extends AbstractHandler {
  770. /** Parent object (task/data type/etc). */
  771. private Object parent;
  772. /** The nested element itself. */
  773. private Object child;
  774. /**
  775. * Wrapper for the parent element, if any. The wrapper for this
  776. * element will be added to this wrapper as a child.
  777. */
  778. private RuntimeConfigurable parentWrapper;
  779. /**
  780. * Wrapper for this element which takes care of actually configuring
  781. * the element, if a parent wrapper is provided.
  782. * Otherwise the configuration is performed with the configure method.
  783. * @see ProjectHelper#configure(Object,AttributeList,Project)
  784. */
  785. private RuntimeConfigurable childWrapper = null;
  786. /** Target this element is part of, if any. */
  787. private Target target;
  788. /**
  789. * Constructor.
  790. *
  791. * @param parentHandler The handler which should be restored to the
  792. * parser at the end of the element.
  793. * Must not be <code>null</code>.
  794. *
  795. * @param parent Parent of this element (task/data type/etc).
  796. * Must not be <code>null</code>.
  797. *
  798. * @param parentWrapper Wrapper for the parent element, if any.
  799. * May be <code>null</code>.
  800. *
  801. * @param target Target this element is part of.
  802. * May be <code>null</code>.
  803. */
  804. public NestedElementHandler(ProjectHelperImpl helperImpl,
  805. DocumentHandler parentHandler,
  806. Object parent,
  807. RuntimeConfigurable parentWrapper,
  808. Target target) {
  809. super(helperImpl, parentHandler);
  810. if (parent instanceof TaskAdapter) {
  811. this.parent = ((TaskAdapter) parent).getProxy();
  812. } else {
  813. this.parent = parent;
  814. }
  815. this.parentWrapper = parentWrapper;
  816. this.target = target;
  817. }
  818. /**
  819. * Initialisation routine called after handler creation
  820. * with the element name and attributes. This configures
  821. * the element with its attributes and sets it up with
  822. * its parent container (if any). Nested elements are then
  823. * added later as the parser encounters them.
  824. *
  825. * @param propType Name of the element which caused this handler
  826. * to be created. Must not be <code>null</code>.
  827. *
  828. * @param attrs Attributes of the element which caused this
  829. * handler to be created. Must not be <code>null</code>.
  830. *
  831. * @exception SAXParseException in case of error, such as a
  832. * BuildException being thrown during configuration.
  833. */
  834. public void init(String propType, AttributeList attrs) throws SAXParseException {
  835. Class parentClass = parent.getClass();
  836. IntrospectionHelper ih =
  837. IntrospectionHelper.getHelper(parentClass);
  838. try {
  839. String elementName = propType.toLowerCase(Locale.US);
  840. if (parent instanceof UnknownElement) {
  841. UnknownElement uc = new UnknownElement(elementName);
  842. uc.setProject(helperImpl.project);
  843. ((UnknownElement) parent).addChild(uc);
  844. child = uc;
  845. } else {
  846. child = ih.createElement(helperImpl.project, parent, elementName);
  847. }
  848. helperImpl.configureId(child, attrs);
  849. if (parentWrapper != null) {
  850. childWrapper = new RuntimeConfigurable(child, propType);
  851. childWrapper.setAttributes(attrs);
  852. parentWrapper.addChild(childWrapper);
  853. } else {
  854. configure(child, attrs, helperImpl.project);
  855. ih.storeElement(helperImpl.project, parent, child, elementName);
  856. }
  857. } catch (BuildException exc) {
  858. throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
  859. }
  860. }
  861. /**
  862. * Adds text to the element, using the wrapper if one is
  863. * available or using addText otherwise.
  864. *
  865. * @param buf A character array of the text within the element.
  866. * Will not be <code>null</code>.
  867. * @param start The start element in the array.
  868. * @param count The number of characters to read from the array.
  869. *
  870. * @exception SAXParseException if the element doesn't support text
  871. *
  872. * @see ProjectHelper#addText(Project,Object,char[],int,int)
  873. */
  874. public void characters(char[] buf, int start, int count) throws SAXParseException {
  875. if (parentWrapper == null) {
  876. try {
  877. ProjectHelper.addText(helperImpl.project, child, buf, start, count);
  878. } catch (BuildException exc) {
  879. throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
  880. }
  881. } else {
  882. childWrapper.addText(buf, start, count);
  883. }
  884. }
  885. /**
  886. * Handles the start of an element within this one. Task containers
  887. * will always use a task handler, and all other elements
  888. * will always use another nested element handler.
  889. *
  890. * @param name The name of the element being started.
  891. * Will not be <code>null</code>.
  892. * @param attrs Attributes of the element being started.
  893. * Will not be <code>null</code>.
  894. *
  895. * @exception SAXParseException if an error occurs when initialising
  896. * the appropriate child handler
  897. */
  898. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  899. if (child instanceof TaskContainer) {
  900. // taskcontainer nested element can contain other tasks - no other
  901. // nested elements possible
  902. new TaskHandler(helperImpl, this, (TaskContainer) child,
  903. childWrapper, target).init(name, attrs);
  904. } else {
  905. new NestedElementHandler(helperImpl, this, child,
  906. childWrapper, target).init(name, attrs);
  907. }
  908. }
  909. }
  910. /**
  911. * Handler for all data types directly subordinate to project or target.
  912. */
  913. static class DataTypeHandler extends AbstractHandler {
  914. /** Parent target, if any. */
  915. private Target target;
  916. /** The element being configured. */
  917. private Object element;
  918. /** Wrapper for this element, if it's part of a target. */
  919. private RuntimeConfigurable wrapper = null;
  920. /**
  921. * Constructor with no target specified.
  922. *
  923. * @param parentHandler The handler which should be restored to the
  924. * parser at the end of the element.
  925. * Must not be <code>null</code>.
  926. */
  927. public DataTypeHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
  928. this(helperImpl, parentHandler, null);
  929. }
  930. /**
  931. * Constructor with a target specified.
  932. *
  933. * @param parentHandler The handler which should be restored to the
  934. * parser at the end of the element.
  935. * Must not be <code>null</code>.
  936. *
  937. * @param target The parent target of this element.
  938. * May be <code>null</code>.
  939. */
  940. public DataTypeHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler, Target target) {
  941. super(helperImpl, parentHandler);
  942. this.target = target;
  943. }
  944. /**
  945. * Initialisation routine called after handler creation
  946. * with the element name and attributes. This configures
  947. * the element with its attributes and sets it up with
  948. * its parent container (if any). Nested elements are then
  949. * added later as the parser encounters them.
  950. *
  951. * @param propType Name of the element which caused this handler
  952. * to be created. Must not be <code>null</code>.
  953. *
  954. * @param attrs Attributes of the element which caused this
  955. * handler to be created. Must not be <code>null</code>.
  956. *
  957. * @exception SAXParseException in case of error, such as a
  958. * BuildException being thrown during configuration.
  959. */
  960. public void init(String propType, AttributeList attrs) throws SAXParseException {
  961. try {
  962. element = helperImpl.project.createDataType(propType);
  963. if (element == null) {
  964. throw new BuildException("Unknown data type " + propType);
  965. }
  966. if (target != null) {
  967. wrapper = new RuntimeConfigurable(element, propType);
  968. wrapper.setAttributes(attrs);
  969. target.addDataType(wrapper);
  970. } else {
  971. configure(element, attrs, helperImpl.project);
  972. helperImpl.configureId(element, attrs);
  973. }
  974. } catch (BuildException exc) {
  975. throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
  976. }
  977. }
  978. // XXX: (Jon Skeet) Any reason why this doesn't use the wrapper
  979. // if one is available, whereas NestedElementHandler.characters does?
  980. /**
  981. * Adds text to the element.
  982. *
  983. * @param buf A character array of the text within the element.
  984. * Will not be <code>null</code>.
  985. * @param start The start element in the array.
  986. * @param count The number of characters to read from the array.
  987. *
  988. * @exception SAXParseException if the element doesn't support text
  989. *
  990. * @see ProjectHelper#addText(Project,Object,char[],int,int)
  991. */
  992. public void characters(char[] buf, int start, int count) throws SAXParseException {
  993. try {
  994. ProjectHelper.addText(helperImpl.project, element, buf, start, count);
  995. } catch (BuildException exc) {
  996. throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
  997. }
  998. }
  999. /**
  1000. * Handles the start of an element within this one.
  1001. * This will always use a nested element handler.
  1002. *
  1003. * @param name The name of the element being started.
  1004. * Will not be <code>null</code>.
  1005. * @param attrs Attributes of the element being started.
  1006. * Will not be <code>null</code>.
  1007. *
  1008. * @exception SAXParseException if an error occurs when initialising
  1009. * the child handler
  1010. */
  1011. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  1012. new NestedElementHandler(helperImpl, this, element, wrapper, target).init(name, attrs);
  1013. }
  1014. }
  1015. /**
  1016. * Scans an attribute list for the <code>id</code> attribute and
  1017. * stores a reference to the target object in the project if an
  1018. * id is found.
  1019. * <p>
  1020. * This method was moved out of the configure method to allow
  1021. * it to be executed at parse time.
  1022. *
  1023. * @see #configure(Object,AttributeList,Project)
  1024. */
  1025. private void configureId(Object target, AttributeList attr) {
  1026. String id = attr.getValue("id");
  1027. if (id != null) {
  1028. project.addReference(id, target);
  1029. }
  1030. }
  1031. }