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