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 41 kB

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