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.

ProjectHelper2.java 53 kB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.helper;
  19. import org.apache.tools.ant.BuildException;
  20. import org.apache.tools.ant.ExtensionPoint;
  21. import org.apache.tools.ant.Location;
  22. import org.apache.tools.ant.MagicNames;
  23. import org.apache.tools.ant.Project;
  24. import org.apache.tools.ant.ProjectHelper;
  25. import org.apache.tools.ant.RuntimeConfigurable;
  26. import org.apache.tools.ant.Target;
  27. import org.apache.tools.ant.Task;
  28. import org.apache.tools.ant.UnknownElement;
  29. import org.apache.tools.ant.types.Resource;
  30. import org.apache.tools.ant.types.resources.FileProvider;
  31. import org.apache.tools.ant.types.resources.URLProvider;
  32. import org.apache.tools.ant.util.FileUtils;
  33. import org.apache.tools.ant.util.JAXPUtils;
  34. import org.apache.tools.zip.ZipFile;
  35. import org.xml.sax.Attributes;
  36. import org.xml.sax.InputSource;
  37. import org.xml.sax.Locator;
  38. import org.xml.sax.SAXException;
  39. import org.xml.sax.SAXParseException;
  40. import org.xml.sax.XMLReader;
  41. import org.xml.sax.helpers.DefaultHandler;
  42. import java.io.File;
  43. import java.io.FileInputStream;
  44. import java.io.FileNotFoundException;
  45. import java.io.IOException;
  46. import java.io.InputStream;
  47. import java.io.UnsupportedEncodingException;
  48. import java.net.URL;
  49. import java.util.HashMap;
  50. import java.util.Hashtable;
  51. import java.util.Map;
  52. import java.util.Stack;
  53. /**
  54. * Sax2 based project reader
  55. *
  56. */
  57. public class ProjectHelper2 extends ProjectHelper {
  58. /** Reference holding the (ordered) target Vector */
  59. public static final String REFID_TARGETS = "ant.targets";
  60. /* Stateless */
  61. // singletons - since all state is in the context
  62. private static AntHandler elementHandler = new ElementHandler();
  63. private static AntHandler targetHandler = new TargetHandler();
  64. private static AntHandler mainHandler = new MainHandler();
  65. private static AntHandler projectHandler = new ProjectHandler();
  66. /** Specific to ProjectHelper2 so not a true Ant "magic name:" */
  67. private static final String REFID_CONTEXT = "ant.parsing.context";
  68. /**
  69. * helper for path -> URI and URI -> path conversions.
  70. */
  71. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  72. /**
  73. * Whether this instance of ProjectHelper can parse an Antlib
  74. * descriptor given by the URL and return its content as an
  75. * UnknownElement ready to be turned into an Antlib task.
  76. *
  77. * <p>This implementation returns true.</p>
  78. *
  79. * @since Ant 1.8.0
  80. */
  81. public boolean canParseAntlibDescriptor(Resource resource) {
  82. return true;
  83. }
  84. /**
  85. * Parse the given URL as an antlib descriptor and return the
  86. * content as something that can be turned into an Antlib task.
  87. *
  88. * <p>simply delegates to {@link #parseUnknownElement
  89. * parseUnknownElement} if the resource provides an URL and throws
  90. * an exception otherwise.</p>
  91. *
  92. * @since Ant 1.8.0
  93. */
  94. public UnknownElement parseAntlibDescriptor(Project containingProject,
  95. Resource resource) {
  96. URLProvider up = resource.as(URLProvider.class);
  97. if (up == null) {
  98. throw new BuildException("Unsupported resource type: " + resource);
  99. }
  100. return parseUnknownElement(containingProject, up.getURL());
  101. }
  102. /**
  103. * Parse an unknown element from a url
  104. *
  105. * @param project the current project
  106. * @param source the url containing the task
  107. * @return a configured task
  108. * @exception BuildException if an error occurs
  109. */
  110. public UnknownElement parseUnknownElement(Project project, URL source)
  111. throws BuildException {
  112. Target dummyTarget = new Target();
  113. dummyTarget.setProject(project);
  114. AntXMLContext context = new AntXMLContext(project);
  115. context.addTarget(dummyTarget);
  116. context.setImplicitTarget(dummyTarget);
  117. parse(context.getProject(), source, new RootHandler(context, elementHandler));
  118. Task[] tasks = dummyTarget.getTasks();
  119. if (tasks.length != 1) {
  120. throw new BuildException("No tasks defined");
  121. }
  122. return (UnknownElement) tasks[0];
  123. }
  124. /**
  125. * Parse a source xml input.
  126. *
  127. * @param project the current project
  128. * @param source the xml source
  129. * @exception BuildException if an error occurs
  130. */
  131. public void parse(Project project, Object source) throws BuildException {
  132. getImportStack().addElement(source);
  133. AntXMLContext context = null;
  134. context = (AntXMLContext) project.getReference(REFID_CONTEXT);
  135. if (context == null) {
  136. context = new AntXMLContext(project);
  137. project.addReference(REFID_CONTEXT, context);
  138. project.addReference(REFID_TARGETS, context.getTargets());
  139. }
  140. if (getImportStack().size() > 1) {
  141. // we are in an imported file.
  142. context.setIgnoreProjectTag(true);
  143. Target currentTarget = context.getCurrentTarget();
  144. Target currentImplicit = context.getImplicitTarget();
  145. Map<String, Target> currentTargets = context.getCurrentTargets();
  146. try {
  147. Target newCurrent = new Target();
  148. newCurrent.setProject(project);
  149. newCurrent.setName("");
  150. context.setCurrentTarget(newCurrent);
  151. context.setCurrentTargets(new HashMap<String, Target>());
  152. context.setImplicitTarget(newCurrent);
  153. parse(project, source, new RootHandler(context, mainHandler));
  154. newCurrent.execute();
  155. } finally {
  156. context.setCurrentTarget(currentTarget);
  157. context.setImplicitTarget(currentImplicit);
  158. context.setCurrentTargets(currentTargets);
  159. }
  160. } else {
  161. // top level file
  162. context.setCurrentTargets(new HashMap<String, Target>());
  163. parse(project, source, new RootHandler(context, mainHandler));
  164. // Execute the top-level target
  165. context.getImplicitTarget().execute();
  166. // resolve extensionOf attributes
  167. resolveExtensionOfAttributes(project);
  168. }
  169. }
  170. /**
  171. * Parses the project file, configuring the project as it goes.
  172. *
  173. * @param project the current project
  174. * @param source the xml source
  175. * @param handler the root handler to use (contains the current context)
  176. * @exception BuildException if the configuration is invalid or cannot
  177. * be read
  178. */
  179. public void parse(Project project, Object source, RootHandler handler) throws BuildException {
  180. AntXMLContext context = handler.context;
  181. File buildFile = null;
  182. URL url = null;
  183. String buildFileName = null;
  184. if (source instanceof File) {
  185. buildFile = (File) source;
  186. } else if (source instanceof URL) {
  187. url = (URL) source;
  188. } else if (source instanceof Resource) {
  189. FileProvider fp =
  190. ((Resource) source).as(FileProvider.class);
  191. if (fp != null) {
  192. buildFile = fp.getFile();
  193. } else {
  194. URLProvider up =
  195. ((Resource) source).as(URLProvider.class);
  196. if (up != null) {
  197. url = up.getURL();
  198. }
  199. }
  200. }
  201. if (buildFile != null) {
  202. buildFile = FILE_UTILS.normalize(buildFile.getAbsolutePath());
  203. context.setBuildFile(buildFile);
  204. buildFileName = buildFile.toString();
  205. } else if (url != null) {
  206. try {
  207. context.setBuildFile((File) null);
  208. context.setBuildFile(url);
  209. } catch (java.net.MalformedURLException ex) {
  210. throw new BuildException(ex);
  211. }
  212. buildFileName = url.toString();
  213. } else {
  214. throw new BuildException("Source " + source.getClass().getName()
  215. + " not supported by this plugin");
  216. }
  217. InputStream inputStream = null;
  218. InputSource inputSource = null;
  219. ZipFile zf = null;
  220. try {
  221. /**
  222. * SAX 2 style parser used to parse the given file.
  223. */
  224. XMLReader parser = JAXPUtils.getNamespaceXMLReader();
  225. String uri = null;
  226. if (buildFile != null) {
  227. uri = FILE_UTILS.toURI(buildFile.getAbsolutePath());
  228. inputStream = new FileInputStream(buildFile);
  229. } else {
  230. uri = url.toString();
  231. int pling = -1;
  232. if (uri.startsWith("jar:file")
  233. && (pling = uri.indexOf("!/")) > -1) {
  234. zf = new ZipFile(org.apache.tools.ant.launch.Locator
  235. .fromJarURI(uri), "UTF-8");
  236. inputStream =
  237. zf.getInputStream(zf.getEntry(uri.substring(pling + 1)));
  238. } else {
  239. inputStream = url.openStream();
  240. }
  241. }
  242. inputSource = new InputSource(inputStream);
  243. if (uri != null) {
  244. inputSource.setSystemId(uri);
  245. }
  246. project.log("parsing buildfile " + buildFileName + " with URI = "
  247. + uri + (zf != null ? " from a zip file" : ""),
  248. Project.MSG_VERBOSE);
  249. DefaultHandler hb = handler;
  250. parser.setContentHandler(hb);
  251. parser.setEntityResolver(hb);
  252. parser.setErrorHandler(hb);
  253. parser.setDTDHandler(hb);
  254. parser.parse(inputSource);
  255. } catch (SAXParseException exc) {
  256. Location location = new Location(exc.getSystemId(), exc.getLineNumber(), exc
  257. .getColumnNumber());
  258. Throwable t = exc.getException();
  259. if (t instanceof BuildException) {
  260. BuildException be = (BuildException) t;
  261. if (be.getLocation() == Location.UNKNOWN_LOCATION) {
  262. be.setLocation(location);
  263. }
  264. throw be;
  265. }
  266. throw new BuildException(exc.getMessage(), t == null ? exc : t, location);
  267. } catch (SAXException exc) {
  268. Throwable t = exc.getException();
  269. if (t instanceof BuildException) {
  270. throw (BuildException) t;
  271. }
  272. throw new BuildException(exc.getMessage(), t == null ? exc : t);
  273. } catch (FileNotFoundException exc) {
  274. throw new BuildException(exc);
  275. } catch (UnsupportedEncodingException exc) {
  276. throw new BuildException("Encoding of project file " + buildFileName + " is invalid.",
  277. exc);
  278. } catch (IOException exc) {
  279. throw new BuildException("Error reading project file " + buildFileName + ": "
  280. + exc.getMessage(), exc);
  281. } finally {
  282. FileUtils.close(inputStream);
  283. ZipFile.closeQuietly(zf);
  284. }
  285. }
  286. /**
  287. * Returns main handler
  288. * @return main handler
  289. */
  290. protected static AntHandler getMainHandler() {
  291. return mainHandler;
  292. }
  293. /**
  294. * Sets main handler
  295. * @param handler new main handler
  296. */
  297. protected static void setMainHandler(AntHandler handler) {
  298. mainHandler = handler;
  299. }
  300. /**
  301. * Returns project handler
  302. * @return project handler
  303. */
  304. protected static AntHandler getProjectHandler() {
  305. return projectHandler;
  306. }
  307. /**
  308. * Sets project handler
  309. * @param handler new project handler
  310. */
  311. protected static void setProjectHandler(AntHandler handler) {
  312. projectHandler = handler;
  313. }
  314. /**
  315. * Returns target handler
  316. * @return target handler
  317. */
  318. protected static AntHandler getTargetHandler() {
  319. return targetHandler;
  320. }
  321. /**
  322. * Sets target handler
  323. * @param handler new target handler
  324. */
  325. protected static void setTargetHandler(AntHandler handler) {
  326. targetHandler = handler;
  327. }
  328. /**
  329. * Returns element handler
  330. * @return element handler
  331. */
  332. protected static AntHandler getElementHandler() {
  333. return elementHandler;
  334. }
  335. /**
  336. * Sets element handler
  337. * @param handler new element handler
  338. */
  339. protected static void setElementHandler(AntHandler handler) {
  340. elementHandler = handler;
  341. }
  342. /**
  343. * The common superclass for all SAX event handlers used to parse
  344. * the configuration file.
  345. *
  346. * The context will hold all state information. At each time
  347. * there is one active handler for the current element. It can
  348. * use onStartChild() to set an alternate handler for the child.
  349. */
  350. public static class AntHandler {
  351. /**
  352. * Handles the start of an element. This base implementation does
  353. * nothing.
  354. *
  355. * @param uri the namespace URI for the tag
  356. * @param tag The name of the element being started.
  357. * Will not be <code>null</code>.
  358. * @param qname The qualified name of the element.
  359. * @param attrs Attributes of the element being started.
  360. * Will not be <code>null</code>.
  361. * @param context The context that this element is in.
  362. *
  363. * @exception SAXParseException if this method is not overridden, or in
  364. * case of error in an overridden version
  365. */
  366. public void onStartElement(String uri, String tag, String qname, Attributes attrs,
  367. AntXMLContext context) throws SAXParseException {
  368. }
  369. /**
  370. * Handles the start of an element. This base implementation just
  371. * throws an exception - you must override this method if you expect
  372. * child elements.
  373. *
  374. * @param uri The namespace uri for this element.
  375. * @param tag The name of the element being started.
  376. * Will not be <code>null</code>.
  377. * @param qname The qualified name for this element.
  378. * @param attrs Attributes of the element being started.
  379. * Will not be <code>null</code>.
  380. * @param context The current context.
  381. * @return a handler (in the derived classes)
  382. *
  383. * @exception SAXParseException if this method is not overridden, or in
  384. * case of error in an overridden version
  385. */
  386. public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
  387. AntXMLContext context) throws SAXParseException {
  388. throw new SAXParseException("Unexpected element \"" + qname + " \"", context
  389. .getLocator());
  390. }
  391. /**
  392. * Handle the end of a element.
  393. *
  394. * @param uri the namespace uri of the element
  395. * @param tag the tag of the element
  396. * @param qname the qualified name of the element
  397. * @param context the current context
  398. * @exception SAXParseException if an error occurs
  399. */
  400. public void onEndChild(String uri, String tag, String qname, AntXMLContext context)
  401. throws SAXParseException {
  402. }
  403. /**
  404. * This method is called when this element and all elements nested into it have been
  405. * handled. I.e., this happens at the &lt;/end_tag_of_the_element&gt;.
  406. * @param uri the namespace uri for this element
  407. * @param tag the element name
  408. * @param context the current context
  409. */
  410. public void onEndElement(String uri, String tag, AntXMLContext context) {
  411. }
  412. /**
  413. * Handles text within an element. This base implementation just
  414. * throws an exception, you must override it if you expect content.
  415. *
  416. * @param buf A character array of the text within the element.
  417. * Will not be <code>null</code>.
  418. * @param start The start element in the array.
  419. * @param count The number of characters to read from the array.
  420. * @param context The current context.
  421. *
  422. * @exception SAXParseException if this method is not overridden, or in
  423. * case of error in an overridden version
  424. */
  425. public void characters(char[] buf, int start, int count, AntXMLContext context)
  426. throws SAXParseException {
  427. String s = new String(buf, start, count).trim();
  428. if (s.length() > 0) {
  429. throw new SAXParseException("Unexpected text \"" + s + "\"", context.getLocator());
  430. }
  431. }
  432. /**
  433. * Will be called every time a namespace is reached.
  434. * It'll verify if the ns was processed, and if not load the task definitions.
  435. * @param uri The namespace uri.
  436. */
  437. protected void checkNamespace(String uri) {
  438. }
  439. }
  440. /**
  441. * Handler for ant processing. Uses a stack of AntHandlers to
  442. * implement each element ( the original parser used a recursive behavior,
  443. * with the implicit execution stack )
  444. */
  445. public static class RootHandler extends DefaultHandler {
  446. private Stack<AntHandler> antHandlers = new Stack<AntHandler>();
  447. private AntHandler currentHandler = null;
  448. private AntXMLContext context;
  449. /**
  450. * Creates a new RootHandler instance.
  451. *
  452. * @param context The context for the handler.
  453. * @param rootHandler The handler for the root element.
  454. */
  455. public RootHandler(AntXMLContext context, AntHandler rootHandler) {
  456. currentHandler = rootHandler;
  457. antHandlers.push(currentHandler);
  458. this.context = context;
  459. }
  460. /**
  461. * Returns the current ant handler object.
  462. * @return the current ant handler.
  463. */
  464. public AntHandler getCurrentAntHandler() {
  465. return currentHandler;
  466. }
  467. /**
  468. * Resolves file: URIs relative to the build file.
  469. *
  470. * @param publicId The public identifier, or <code>null</code>
  471. * if none is available. Ignored in this
  472. * implementation.
  473. * @param systemId The system identifier provided in the XML
  474. * document. Will not be <code>null</code>.
  475. * @return an inputsource for this identifier
  476. */
  477. public InputSource resolveEntity(String publicId, String systemId) {
  478. context.getProject().log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
  479. if (systemId.startsWith("file:")) {
  480. String path = FILE_UTILS.fromURI(systemId);
  481. File file = new File(path);
  482. if (!file.isAbsolute()) {
  483. file = FILE_UTILS.resolveFile(context.getBuildFileParent(), path);
  484. context.getProject().log(
  485. "Warning: '" + systemId + "' in " + context.getBuildFile()
  486. + " should be expressed simply as '" + path.replace('\\', '/')
  487. + "' for compliance with other XML tools", Project.MSG_WARN);
  488. }
  489. context.getProject().log("file=" + file, Project.MSG_DEBUG);
  490. try {
  491. InputSource inputSource = new InputSource(new FileInputStream(file));
  492. inputSource.setSystemId(FILE_UTILS.toURI(file.getAbsolutePath()));
  493. return inputSource;
  494. } catch (FileNotFoundException fne) {
  495. context.getProject().log(file.getAbsolutePath() + " could not be found",
  496. Project.MSG_WARN);
  497. }
  498. }
  499. // use default if not file or file not found
  500. context.getProject().log("could not resolve systemId", Project.MSG_DEBUG);
  501. return null;
  502. }
  503. /**
  504. * Handles the start of a project element. A project handler is created
  505. * and initialised with the element name and attributes.
  506. *
  507. * @param uri The namespace uri for this element.
  508. * @param tag The name of the element being started.
  509. * Will not be <code>null</code>.
  510. * @param qname The qualified name for this element.
  511. * @param attrs Attributes of the element being started.
  512. * Will not be <code>null</code>.
  513. *
  514. * @exception org.xml.sax.SAXParseException if the tag given is not
  515. * <code>"project"</code>
  516. */
  517. public void startElement(String uri, String tag, String qname, Attributes attrs)
  518. throws SAXParseException {
  519. AntHandler next = currentHandler.onStartChild(uri, tag, qname, attrs, context);
  520. antHandlers.push(currentHandler);
  521. currentHandler = next;
  522. currentHandler.onStartElement(uri, tag, qname, attrs, context);
  523. }
  524. /**
  525. * Sets the locator in the project helper for future reference.
  526. *
  527. * @param locator The locator used by the parser.
  528. * Will not be <code>null</code>.
  529. */
  530. public void setDocumentLocator(Locator locator) {
  531. context.setLocator(locator);
  532. }
  533. /**
  534. * Handles the end of an element. Any required clean-up is performed
  535. * by the onEndElement() method and then the original handler is restored to the parser.
  536. *
  537. * @param uri The namespace URI for this element.
  538. * @param name The name of the element which is ending.
  539. * Will not be <code>null</code>.
  540. * @param qName The qualified name for this element.
  541. *
  542. * @exception SAXException in case of error (not thrown in this implementation)
  543. */
  544. public void endElement(String uri, String name, String qName) throws SAXException {
  545. currentHandler.onEndElement(uri, name, context);
  546. AntHandler prev = (AntHandler) antHandlers.pop();
  547. currentHandler = prev;
  548. if (currentHandler != null) {
  549. currentHandler.onEndChild(uri, name, qName, context);
  550. }
  551. }
  552. /**
  553. * Handle text within an element, calls currentHandler.characters.
  554. *
  555. * @param buf A character array of the test.
  556. * @param start The start offset in the array.
  557. * @param count The number of characters to read.
  558. * @exception SAXParseException if an error occurs
  559. */
  560. public void characters(char[] buf, int start, int count) throws SAXParseException {
  561. currentHandler.characters(buf, start, count, context);
  562. }
  563. /**
  564. * Start a namespace prefix to uri mapping
  565. *
  566. * @param prefix the namespace prefix
  567. * @param uri the namespace uri
  568. */
  569. public void startPrefixMapping(String prefix, String uri) {
  570. context.startPrefixMapping(prefix, uri);
  571. }
  572. /**
  573. * End a namespace prefix to uri mapping
  574. *
  575. * @param prefix the prefix that is not mapped anymore
  576. */
  577. public void endPrefixMapping(String prefix) {
  578. context.endPrefixMapping(prefix);
  579. }
  580. }
  581. /**
  582. * The main handler - it handles the &lt;project&gt; tag.
  583. *
  584. * @see org.apache.tools.ant.helper.ProjectHelper2.AntHandler
  585. */
  586. public static class MainHandler extends AntHandler {
  587. /**
  588. * Handle the project tag
  589. *
  590. * @param uri The namespace uri.
  591. * @param name The element tag.
  592. * @param qname The element qualified name.
  593. * @param attrs The attributes of the element.
  594. * @param context The current context.
  595. * @return The project handler that handles subelements of project
  596. * @exception SAXParseException if the qualified name is not "project".
  597. */
  598. public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
  599. AntXMLContext context) throws SAXParseException {
  600. if (name.equals("project")
  601. && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
  602. return ProjectHelper2.projectHandler;
  603. }
  604. if (name.equals(qname)) {
  605. throw new SAXParseException("Unexpected element \"{" + uri
  606. + "}" + name + "\" {" + ANT_CORE_URI + "}" + name, context.getLocator());
  607. }
  608. throw new SAXParseException("Unexpected element \"" + qname
  609. + "\" " + name, context.getLocator());
  610. }
  611. }
  612. /**
  613. * Handler for the top level "project" element.
  614. */
  615. public static class ProjectHandler extends AntHandler {
  616. /**
  617. * Initialisation routine called after handler creation
  618. * with the element name and attributes. The attributes which
  619. * this handler can deal with are: <code>"default"</code>,
  620. * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
  621. *
  622. * @param uri The namespace URI for this element.
  623. * @param tag Name of the element which caused this handler
  624. * to be created. Should not be <code>null</code>.
  625. * Ignored in this implementation.
  626. * @param qname The qualified name for this element.
  627. * @param attrs Attributes of the element which caused this
  628. * handler to be created. Must not be <code>null</code>.
  629. * @param context The current context.
  630. *
  631. * @exception SAXParseException if an unexpected attribute is
  632. * encountered or if the <code>"default"</code> attribute
  633. * is missing.
  634. */
  635. public void onStartElement(String uri, String tag, String qname, Attributes attrs,
  636. AntXMLContext context) throws SAXParseException {
  637. String baseDir = null;
  638. boolean nameAttributeSet = false;
  639. Project project = context.getProject();
  640. // Set the location of the implicit target associated with the project tag
  641. context.getImplicitTarget().setLocation(new Location(context.getLocator()));
  642. /** XXX I really don't like this - the XML processor is still
  643. * too 'involved' in the processing. A better solution (IMO)
  644. * would be to create UE for Project and Target too, and
  645. * then process the tree and have Project/Target deal with
  646. * its attributes ( similar with Description ).
  647. *
  648. * If we eventually switch to ( or add support for ) DOM,
  649. * things will work smoothly - UE can be avoided almost completely
  650. * ( it could still be created on demand, for backward compatibility )
  651. */
  652. for (int i = 0; i < attrs.getLength(); i++) {
  653. String attrUri = attrs.getURI(i);
  654. if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
  655. continue; // Ignore attributes from unknown uris
  656. }
  657. String key = attrs.getLocalName(i);
  658. String value = attrs.getValue(i);
  659. if (key.equals("default")) {
  660. if (value != null && !value.equals("")) {
  661. if (!context.isIgnoringProjectTag()) {
  662. project.setDefault(value);
  663. }
  664. }
  665. } else if (key.equals("name")) {
  666. if (value != null) {
  667. context.setCurrentProjectName(value);
  668. nameAttributeSet = true;
  669. if (!context.isIgnoringProjectTag()) {
  670. project.setName(value);
  671. project.addReference(value, project);
  672. } else if (isInIncludeMode()) {
  673. if (!"".equals(value) && getCurrentTargetPrefix()!= null && getCurrentTargetPrefix().endsWith(ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX)) {
  674. String newTargetPrefix = getCurrentTargetPrefix().replace(ProjectHelper.USE_PROJECT_NAME_AS_TARGET_PREFIX, value);
  675. // help nested include tasks
  676. setCurrentTargetPrefix(newTargetPrefix);
  677. }
  678. }
  679. }
  680. } else if (key.equals("id")) {
  681. if (value != null) {
  682. // What's the difference between id and name ?
  683. if (!context.isIgnoringProjectTag()) {
  684. project.addReference(value, project);
  685. }
  686. }
  687. } else if (key.equals("basedir")) {
  688. if (!context.isIgnoringProjectTag()) {
  689. baseDir = value;
  690. }
  691. } else {
  692. // XXX ignore attributes in a different NS ( maybe store them ? )
  693. throw new SAXParseException("Unexpected attribute \"" + attrs.getQName(i)
  694. + "\"", context.getLocator());
  695. }
  696. }
  697. // XXX Move to Project ( so it is shared by all helpers )
  698. String antFileProp =
  699. MagicNames.ANT_FILE + "." + context.getCurrentProjectName();
  700. String dup = project.getProperty(antFileProp);
  701. String typeProp =
  702. MagicNames.ANT_FILE_TYPE + "." + context.getCurrentProjectName();
  703. String dupType = project.getProperty(typeProp);
  704. if (dup != null && nameAttributeSet) {
  705. Object dupFile = null;
  706. Object contextFile = null;
  707. if (MagicNames.ANT_FILE_TYPE_URL.equals(dupType)) {
  708. try {
  709. dupFile = new URL(dup);
  710. } catch (java.net.MalformedURLException mue) {
  711. throw new BuildException("failed to parse "
  712. + dup + " as URL while looking"
  713. + " at a duplicate project"
  714. + " name.", mue);
  715. }
  716. contextFile = context.getBuildFileURL();
  717. } else {
  718. dupFile = new File(dup);
  719. contextFile = context.getBuildFile();
  720. }
  721. if (context.isIgnoringProjectTag() && !dupFile.equals(contextFile)) {
  722. project.log("Duplicated project name in import. Project "
  723. + context.getCurrentProjectName() + " defined first in " + dup
  724. + " and again in " + contextFile, Project.MSG_WARN);
  725. }
  726. }
  727. if (nameAttributeSet) {
  728. if (context.getBuildFile() != null) {
  729. project.setUserProperty(antFileProp,
  730. context.getBuildFile().toString());
  731. project.setUserProperty(typeProp,
  732. MagicNames.ANT_FILE_TYPE_FILE);
  733. } else if (context.getBuildFileURL() != null) {
  734. project.setUserProperty(antFileProp,
  735. context.getBuildFileURL().toString());
  736. project.setUserProperty(typeProp,
  737. MagicNames.ANT_FILE_TYPE_URL);
  738. }
  739. }
  740. if (context.isIgnoringProjectTag()) {
  741. // no further processing
  742. return;
  743. }
  744. // set explicitly before starting ?
  745. if (project.getProperty("basedir") != null) {
  746. project.setBasedir(project.getProperty("basedir"));
  747. } else {
  748. // Default for baseDir is the location of the build file.
  749. if (baseDir == null) {
  750. project.setBasedir(context.getBuildFileParent().getAbsolutePath());
  751. } else {
  752. // check whether the user has specified an absolute path
  753. if ((new File(baseDir)).isAbsolute()) {
  754. project.setBasedir(baseDir);
  755. } else {
  756. project.setBaseDir(FILE_UTILS.resolveFile(context.getBuildFileParent(),
  757. baseDir));
  758. }
  759. }
  760. }
  761. project.addTarget("", context.getImplicitTarget());
  762. context.setCurrentTarget(context.getImplicitTarget());
  763. }
  764. /**
  765. * Handles the start of a top-level element within the project. An
  766. * appropriate handler is created and initialised with the details
  767. * of the element.
  768. *
  769. * @param uri The namespace URI for this element.
  770. * @param name The name of the element being started.
  771. * Will not be <code>null</code>.
  772. * @param qname The qualified name for this element.
  773. * @param attrs Attributes of the element being started.
  774. * Will not be <code>null</code>.
  775. * @param context The context for this element.
  776. * @return a target or an element handler.
  777. *
  778. * @exception org.xml.sax.SAXParseException if the tag given is not
  779. * <code>"taskdef"</code>, <code>"typedef"</code>,
  780. * <code>"property"</code>, <code>"target"</code>,
  781. * <code>"extension-point"</code>
  782. * or a data type definition
  783. */
  784. public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
  785. AntXMLContext context) throws SAXParseException {
  786. return (name.equals("target") || name.equals("extension-point"))
  787. && (uri.equals("") || uri.equals(ANT_CORE_URI))
  788. ? ProjectHelper2.targetHandler : ProjectHelper2.elementHandler;
  789. }
  790. }
  791. /**
  792. * Handler for "target" and "extension-point" elements.
  793. */
  794. public static class TargetHandler extends AntHandler {
  795. /**
  796. * Initialisation routine called after handler creation
  797. * with the element name and attributes. The attributes which
  798. * this handler can deal with are: <code>"name"</code>,
  799. * <code>"depends"</code>, <code>"if"</code>,
  800. * <code>"unless"</code>, <code>"id"</code> and
  801. * <code>"description"</code>.
  802. *
  803. * @param uri The namespace URI for this element.
  804. * @param tag Name of the element which caused this handler
  805. * to be created. Should not be <code>null</code>.
  806. * Ignored in this implementation.
  807. * @param qname The qualified name for this element.
  808. * @param attrs Attributes of the element which caused this
  809. * handler to be created. Must not be <code>null</code>.
  810. * @param context The current context.
  811. *
  812. * @exception SAXParseException if an unexpected attribute is encountered
  813. * or if the <code>"name"</code> attribute is missing.
  814. */
  815. public void onStartElement(String uri, String tag, String qname, Attributes attrs,
  816. AntXMLContext context) throws SAXParseException {
  817. String name = null;
  818. String depends = "";
  819. String extensionPoint = null;
  820. OnMissingExtensionPoint extensionPointMissing = null;
  821. Project project = context.getProject();
  822. Target target = "target".equals(tag)
  823. ? new Target() : new ExtensionPoint();
  824. target.setProject(project);
  825. target.setLocation(new Location(context.getLocator()));
  826. context.addTarget(target);
  827. for (int i = 0; i < attrs.getLength(); i++) {
  828. String attrUri = attrs.getURI(i);
  829. if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
  830. continue; // Ignore attributes from unknown uris
  831. }
  832. String key = attrs.getLocalName(i);
  833. String value = attrs.getValue(i);
  834. if (key.equals("name")) {
  835. name = value;
  836. if ("".equals(name)) {
  837. throw new BuildException("name attribute must " + "not be empty");
  838. }
  839. } else if (key.equals("depends")) {
  840. depends = value;
  841. } else if (key.equals("if")) {
  842. target.setIf(value);
  843. } else if (key.equals("unless")) {
  844. target.setUnless(value);
  845. } else if (key.equals("id")) {
  846. if (value != null && !value.equals("")) {
  847. context.getProject().addReference(value, target);
  848. }
  849. } else if (key.equals("description")) {
  850. target.setDescription(value);
  851. } else if (key.equals("extensionOf")) {
  852. extensionPoint = value;
  853. } else if (key.equals("onMissingExtensionPoint")) {
  854. try {
  855. extensionPointMissing = OnMissingExtensionPoint.valueOf(value);
  856. } catch (IllegalArgumentException e) {
  857. throw new BuildException("Invalid onMissingExtensionPoint " + value);
  858. }
  859. } else {
  860. throw new SAXParseException("Unexpected attribute \"" + key + "\"", context
  861. .getLocator());
  862. }
  863. }
  864. if (name == null) {
  865. throw new SAXParseException("target element appears without a name attribute",
  866. context.getLocator());
  867. }
  868. String prefix = null;
  869. boolean isInIncludeMode =
  870. context.isIgnoringProjectTag() && isInIncludeMode();
  871. String sep = getCurrentPrefixSeparator();
  872. if (isInIncludeMode) {
  873. prefix = getTargetPrefix(context);
  874. if (prefix == null) {
  875. throw new BuildException("can't include build file "
  876. + context.getBuildFileURL()
  877. + ", no as attribute has been given"
  878. + " and the project tag doesn't"
  879. + " specify a name attribute");
  880. }
  881. name = prefix + sep + name;
  882. }
  883. // Check if this target is in the current build file
  884. if (context.getCurrentTargets().get(name) != null) {
  885. throw new BuildException("Duplicate target '" + name + "'",
  886. target.getLocation());
  887. }
  888. Hashtable<String, Target> projectTargets = project.getTargets();
  889. boolean usedTarget = false;
  890. // If the name has not already been defined define it
  891. if (projectTargets.containsKey(name)) {
  892. project.log("Already defined in main or a previous import, ignore " + name,
  893. Project.MSG_VERBOSE);
  894. } else {
  895. target.setName(name);
  896. context.getCurrentTargets().put(name, target);
  897. project.addOrReplaceTarget(name, target);
  898. usedTarget = true;
  899. }
  900. if (depends.length() > 0) {
  901. if (!isInIncludeMode) {
  902. target.setDepends(depends);
  903. } else {
  904. for (String string : Target.parseDepends(depends, name, "depends")) {
  905. target.addDependency(prefix + sep + string);
  906. }
  907. }
  908. }
  909. if (!isInIncludeMode && context.isIgnoringProjectTag()
  910. && (prefix = getTargetPrefix(context)) != null) {
  911. // In an imported file (and not completely
  912. // ignoring the project tag or having a preconfigured prefix)
  913. String newName = prefix + sep + name;
  914. Target newTarget = target;
  915. if (usedTarget) {
  916. newTarget = "target".equals(tag)
  917. ? new Target(target) : new ExtensionPoint(target);
  918. }
  919. newTarget.setName(newName);
  920. context.getCurrentTargets().put(newName, newTarget);
  921. project.addOrReplaceTarget(newName, newTarget);
  922. }
  923. if (extensionPointMissing != null && extensionPoint == null) {
  924. throw new BuildException("onMissingExtensionPoint attribute cannot " +
  925. "be specified unless extensionOf is specified",
  926. target.getLocation());
  927. }
  928. if (extensionPoint != null) {
  929. ProjectHelper helper =
  930. (ProjectHelper) context.getProject().
  931. getReference(ProjectHelper.PROJECTHELPER_REFERENCE);
  932. for (String extPointName : Target.parseDepends(extensionPoint, name, "extensionOf")) {
  933. if (extensionPointMissing == null) {
  934. extensionPointMissing = OnMissingExtensionPoint.FAIL;
  935. }
  936. // defer extensionpoint resolution until the full
  937. // import stack has been processed
  938. if (isInIncludeMode()) {
  939. // if in include mode, provide prefix we're including by
  940. // so that we can try and resolve extension point from
  941. // the local file first
  942. helper.getExtensionStack().add(
  943. new String[] {extPointName, target.getName(),
  944. extensionPointMissing.name(), prefix + sep});
  945. } else {
  946. helper.getExtensionStack().add(
  947. new String[] {extPointName, target.getName(),
  948. extensionPointMissing.name()});
  949. }
  950. }
  951. }
  952. }
  953. private String getTargetPrefix(AntXMLContext context) {
  954. String configuredValue = getCurrentTargetPrefix();
  955. if (configuredValue != null && configuredValue.length() == 0) {
  956. configuredValue = null;
  957. }
  958. if (configuredValue != null) {
  959. return configuredValue;
  960. }
  961. String projectName = context.getCurrentProjectName();
  962. if ("".equals(projectName)) {
  963. projectName = null;
  964. }
  965. return projectName;
  966. }
  967. /**
  968. * Handles the start of an element within a target.
  969. *
  970. * @param uri The namespace URI for this element.
  971. * @param name The name of the element being started.
  972. * Will not be <code>null</code>.
  973. * @param qname The qualified name for this element.
  974. * @param attrs Attributes of the element being started.
  975. * Will not be <code>null</code>.
  976. * @param context The current context.
  977. * @return an element handler.
  978. *
  979. * @exception SAXParseException if an error occurs when initialising
  980. * the appropriate child handler
  981. */
  982. public AntHandler onStartChild(String uri, String name, String qname, Attributes attrs,
  983. AntXMLContext context) throws SAXParseException {
  984. return ProjectHelper2.elementHandler;
  985. }
  986. /**
  987. * Handle the end of the project, sets the current target of the
  988. * context to be the implicit target.
  989. *
  990. * @param uri The namespace URI of the element.
  991. * @param tag The name of the element.
  992. * @param context The current context.
  993. */
  994. public void onEndElement(String uri, String tag, AntXMLContext context) {
  995. context.setCurrentTarget(context.getImplicitTarget());
  996. }
  997. }
  998. /**
  999. * Handler for all project elements ( tasks, data types )
  1000. */
  1001. public static class ElementHandler extends AntHandler {
  1002. /**
  1003. * Constructor.
  1004. */
  1005. public ElementHandler() {
  1006. }
  1007. /**
  1008. * Initialisation routine called after handler creation
  1009. * with the element name and attributes. This configures
  1010. * the element with its attributes and sets it up with
  1011. * its parent container (if any). Nested elements are then
  1012. * added later as the parser encounters them.
  1013. *
  1014. * @param uri The namespace URI for this element.
  1015. * @param tag Name of the element which caused this handler
  1016. * to be created. Must not be <code>null</code>.
  1017. * @param qname The qualified name for this element.
  1018. * @param attrs Attributes of the element which caused this
  1019. * handler to be created. Must not be <code>null</code>.
  1020. * @param context The current context.
  1021. *
  1022. * @exception SAXParseException in case of error (not thrown in
  1023. * this implementation)
  1024. */
  1025. public void onStartElement(String uri, String tag, String qname, Attributes attrs,
  1026. AntXMLContext context) throws SAXParseException {
  1027. RuntimeConfigurable parentWrapper = context.currentWrapper();
  1028. Object parent = null;
  1029. if (parentWrapper != null) {
  1030. parent = parentWrapper.getProxy();
  1031. }
  1032. /* UnknownElement is used for tasks and data types - with
  1033. delayed eval */
  1034. UnknownElement task = new UnknownElement(tag);
  1035. task.setProject(context.getProject());
  1036. task.setNamespace(uri);
  1037. task.setQName(qname);
  1038. task.setTaskType(ProjectHelper.genComponentName(task.getNamespace(), tag));
  1039. task.setTaskName(qname);
  1040. Location location = new Location(context.getLocator().getSystemId(), context
  1041. .getLocator().getLineNumber(), context.getLocator().getColumnNumber());
  1042. task.setLocation(location);
  1043. task.setOwningTarget(context.getCurrentTarget());
  1044. if (parent != null) {
  1045. // Nested element
  1046. ((UnknownElement) parent).addChild(task);
  1047. } else {
  1048. // Task included in a target ( including the default one ).
  1049. context.getCurrentTarget().addTask(task);
  1050. }
  1051. context.configureId(task, attrs);
  1052. // container.addTask(task);
  1053. // This is a nop in UE: task.init();
  1054. RuntimeConfigurable wrapper = new RuntimeConfigurable(task, task.getTaskName());
  1055. for (int i = 0; i < attrs.getLength(); i++) {
  1056. String name = attrs.getLocalName(i);
  1057. String attrUri = attrs.getURI(i);
  1058. if (attrUri != null && !attrUri.equals("") && !attrUri.equals(uri)) {
  1059. name = attrUri + ":" + attrs.getQName(i);
  1060. }
  1061. String value = attrs.getValue(i);
  1062. // PR: Hack for ant-type value
  1063. // an ant-type is a component name which can
  1064. // be namespaced, need to extract the name
  1065. // and convert from qualified name to uri/name
  1066. if (ANT_TYPE.equals(name)
  1067. || (ANT_CORE_URI.equals(attrUri)
  1068. && ANT_TYPE.equals(attrs.getLocalName(i)))) {
  1069. name = ANT_TYPE;
  1070. int index = value.indexOf(":");
  1071. if (index >= 0) {
  1072. String prefix = value.substring(0, index);
  1073. String mappedUri = context.getPrefixMapping(prefix);
  1074. if (mappedUri == null) {
  1075. throw new BuildException("Unable to find XML NS prefix \"" + prefix
  1076. + "\"");
  1077. }
  1078. value = ProjectHelper.genComponentName(mappedUri, value
  1079. .substring(index + 1));
  1080. }
  1081. }
  1082. wrapper.setAttribute(name, value);
  1083. }
  1084. if (parentWrapper != null) {
  1085. parentWrapper.addChild(wrapper);
  1086. }
  1087. context.pushWrapper(wrapper);
  1088. }
  1089. /**
  1090. * Adds text to the task, using the wrapper
  1091. *
  1092. * @param buf A character array of the text within the element.
  1093. * Will not be <code>null</code>.
  1094. * @param start The start element in the array.
  1095. * @param count The number of characters to read from the array.
  1096. * @param context The current context.
  1097. *
  1098. * @exception SAXParseException if the element doesn't support text
  1099. *
  1100. * @see ProjectHelper#addText(Project,java.lang.Object,char[],int,int)
  1101. */
  1102. public void characters(char[] buf, int start, int count,
  1103. AntXMLContext context) throws SAXParseException {
  1104. RuntimeConfigurable wrapper = context.currentWrapper();
  1105. wrapper.addText(buf, start, count);
  1106. }
  1107. /**
  1108. * Handles the start of an element within a target. Task containers
  1109. * will always use another task handler, and all other tasks
  1110. * will always use a nested element handler.
  1111. *
  1112. * @param uri The namespace URI for this element.
  1113. * @param tag The name of the element being started.
  1114. * Will not be <code>null</code>.
  1115. * @param qname The qualified name for this element.
  1116. * @param attrs Attributes of the element being started.
  1117. * Will not be <code>null</code>.
  1118. * @param context The current context.
  1119. * @return The handler for elements.
  1120. *
  1121. * @exception SAXParseException if an error occurs when initialising
  1122. * the appropriate child handler
  1123. */
  1124. public AntHandler onStartChild(String uri, String tag, String qname, Attributes attrs,
  1125. AntXMLContext context) throws SAXParseException {
  1126. return ProjectHelper2.elementHandler;
  1127. }
  1128. /**
  1129. * Handles the end of the element. This pops the wrapper from
  1130. * the context.
  1131. *
  1132. * @param uri The namespace URI for the element.
  1133. * @param tag The name of the element.
  1134. * @param context The current context.
  1135. */
  1136. public void onEndElement(String uri, String tag, AntXMLContext context) {
  1137. context.popWrapper();
  1138. }
  1139. }
  1140. }