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