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 46 KiB

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