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

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