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