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.

ProjectHelperImpl2.java 45 KiB

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