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
18package org.tp23.antinstaller.antmod;
19
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.IOException;
24import java.io.InputStream;
25import java.io.UnsupportedEncodingException;
26import java.net.URL;
27import java.util.Hashtable;
28import java.util.Stack;
29
30import org.apache.tools.ant.BuildException;
31import org.apache.tools.ant.Location;
32import org.apache.tools.ant.Project;
33import org.apache.tools.ant.ProjectHelper;
34import org.apache.tools.ant.RuntimeConfigurable;
35import org.apache.tools.ant.Target;
36import org.apache.tools.ant.Task;
37import org.apache.tools.ant.UnknownElement;
38import org.apache.tools.ant.helper.AntXMLContext;
39import org.apache.tools.ant.util.FileUtils;
40import org.apache.tools.ant.util.JAXPUtils;
41import org.xml.sax.Attributes;
42import org.xml.sax.InputSource;
43import org.xml.sax.Locator;
44import org.xml.sax.SAXException;
45import org.xml.sax.SAXParseException;
46import org.xml.sax.XMLReader;
47import org.xml.sax.helpers.DefaultHandler;
48
49/**
50 * Sax2 based project reader
51 * 
52* This file has been modified by Paul Hinds for Antinstaller and is not the same
53* as the ProjectHelper2 delivered with Ant 1.6 although almost identical
54 */
55public class ProjectHelper3 extends ProjectHelper {
56    /* Stateless */
57
58    // singletons - since all state is in the context
59    private static AntHandler elementHandler = new ElementHandler();
60    private static AntHandler targetHandler = new TargetHandler();
61    private static AntHandler mainHandler = new MainHandler();
62    private static AntHandler projectHandler = new ProjectHandler();
63
64    /**
65     * helper for path -> URI and URI -> path conversions.
66     */
67    private static FileUtils fu = FileUtils.newFileUtils();
68
69    /**
70     * Parse an unknown element from a url
71     *
72     * @param project the current project
73     * @param source  the url containing the task
74     * @return a configured task
75     * @exception BuildException if an error occurs
76     */
77    public UnknownElement parseUnknownElement(Project project, URL source)
78        throws BuildException {
79        Target dummyTarget = new Target();
80        dummyTarget.setProject(project);
81
82        AntXMLContext context = new AntXMLContext(project);
83        context.addTarget(dummyTarget);
84        context.setImplicitTarget(dummyTarget);
85
86        parse(context.getProject(), source,
87              new RootHandler(context, elementHandler));
88        Task[] tasks = dummyTarget.getTasks();
89        if (tasks.length != 1) {
90            throw new BuildException("No tasks defined");
91        }
92        return (UnknownElement) tasks[0];
93    }
94    /**
95     * Parse a source xml input.
96     *
97     * @param project the current project
98     * @param source  the xml source
99     * @exception BuildException if an error occurs
00     */
01    public void parse(Project project, Object source)
02            throws BuildException {
03        getImportStack().addElement(source);
04        //System.out.println("Adding " + source);
05        AntXMLContext context = null;
06        context = (AntXMLContext) project.getReference("ant.parsing.context");
07//        System.out.println("Parsing " + getImportStack().size() + " " +
08//                context+ " " + getImportStack() );
09        if (context == null) {
10            context = new AntXMLContext(project);
11            project.addReference("ant.parsing.context", context);
12            project.addReference("ant.targets", context.getTargets());
13        }
14
15        if (getImportStack().size() > 1) {
16            // we are in an imported file.
17            context.setIgnoreProjectTag(true);
18            Target currentTarget = context.getCurrentTarget();
19            try {
20                Target newCurrent = new Target();
21                newCurrent.setProject(project);
22                newCurrent.setName("");
23                context.setCurrentTarget(newCurrent);
24                parse(project, source, new RootHandler(context, mainHandler));
25                newCurrent.execute();
26            } finally {
27                context.setCurrentTarget(currentTarget);
28            }
29        } else {
30            // top level file
31            parse(project, source, new RootHandler(context, mainHandler));
32            // Execute the top-level target
33            context.getImplicitTarget().execute();
34        }
35    }
36
37    /**
38     * Parses the project file, configuring the project as it goes.
39     *
40     * @param project the current project
41     * @param source  the xml source
42     * @param handler the root handler to use (contains the current context)
43     * @exception BuildException if the configuration is invalid or cannot
44     *                           be read
45     */
46    public void parse(Project project, Object source, RootHandler handler)
47            throws BuildException {
48
49        AntXMLContext context = handler.context;
50
51        File buildFile = null;
52        URL  url = null;
53        String buildFileName = null;
54
55        if (source instanceof File) {
56            buildFile = (File) source;
57            buildFile = fu.normalize(buildFile.getAbsolutePath());
58            context.setBuildFile(buildFile);
59            buildFileName = buildFile.toString();
60//         } else if (source instanceof InputStream ) {
61        } else if (source instanceof URL) {
62//          These commented out code is the only difference
63//          between this class and ProjectHelper2
64//            if (handler.getCurrentAntHandler() != elementHandler) {
65//                throw new BuildException(
66//                    "Source " + source.getClass().getName()
67//                    + " not supported by this plugin for "
68//                    + " non task xml");
69//            }
70            url = (URL) source;
71            buildFileName = url.toString();
72//         } else if (source instanceof InputSource ) {
73        } else {
74            throw new BuildException("Source " + source.getClass().getName()
75                                     + " not supported by this plugin");
76        }
77
78        InputStream inputStream = null;
79        InputSource inputSource = null;
80
81
82        try {
83            /**
84             * SAX 2 style parser used to parse the given file.
85             */
86            XMLReader parser = JAXPUtils.getNamespaceXMLReader();
87
88            String uri = null;
89            if (buildFile != null) {
90                uri = fu.toURI(buildFile.getAbsolutePath());
91                inputStream = new FileInputStream(buildFile);
92            } else {
93                inputStream = url.openStream();
94                uri = url.toString(); // ?? OK ??
95            }
96
97            inputSource = new InputSource(inputStream);
98            if (uri != null) {
99                inputSource.setSystemId(uri);
00            }
01            project.log("parsing buildfile " + buildFileName
02                        + " with URI = " + uri, Project.MSG_VERBOSE);
03
04            DefaultHandler hb = handler;
05
06            parser.setContentHandler(hb);
07            parser.setEntityResolver(hb);
08            parser.setErrorHandler(hb);
09            parser.setDTDHandler(hb);
10            parser.parse(inputSource);
11        } catch (SAXParseException exc) {
12            Location location = new Location(exc.getSystemId(),
13                exc.getLineNumber(), exc.getColumnNumber());
14
15            Throwable t = exc.getException();
16            if (t instanceof BuildException) {
17                BuildException be = (BuildException) t;
18                if (be.getLocation() == Location.UNKNOWN_LOCATION) {
19                    be.setLocation(location);
20                }
21                throw be;
22            }
23
24            throw new BuildException(exc.getMessage(), t, location);
25        } catch (SAXException exc) {
26            Throwable t = exc.getException();
27            if (t instanceof BuildException) {
28                throw (BuildException) t;
29            }
30            throw new BuildException(exc.getMessage(), t);
31        } catch (FileNotFoundException exc) {
32            throw new BuildException(exc);
33        } catch (UnsupportedEncodingException exc) {
34              throw new BuildException("Encoding of project file "
35                                       + buildFileName + " is invalid.",
36                                       exc);
37        } catch (IOException exc) {
38            throw new BuildException("Error reading project file "
39                                     + buildFileName + ": " + exc.getMessage(),
40                                     exc);
41        } finally {
42            if (inputStream != null) {
43                try {
44                    inputStream.close();
45                } catch (IOException ioe) {
46                    // ignore this
47                }
48            }
49        }
50    }
51
52    /**
53     * The common superclass for all SAX event handlers used to parse
54     * the configuration file.
55     *
56     * The context will hold all state information. At each time
57     * there is one active handler for the current element. It can
58     * use onStartChild() to set an alternate handler for the child.
59     */
60    public static class AntHandler  {
61        /**
62         * Handles the start of an element. This base implementation does
63         * nothing.
64         *
65         * @param uri the namespace URI for the tag
66         * @param tag The name of the element being started.
67         *            Will not be <code>null</code>.
68         * @param qname The qualified name of the element.
69         * @param attrs Attributes of the element being started.
70         *              Will not be <code>null</code>.
71         * @param context The context that this element is in.
72         *
73         * @exception SAXParseException if this method is not overridden, or in
74         *                              case of error in an overridden version
75         */
76        public void onStartElement(String uri, String tag, String qname,
77                                   Attributes attrs,
78                                   AntXMLContext context)
79            throws SAXParseException {
80        }
81
82        /**
83         * Handles the start of an element. This base implementation just
84         * throws an exception - you must override this method if you expect
85         * child elements.
86         *
87         * @param uri The namespace uri for this element.
88         * @param tag The name of the element being started.
89         *            Will not be <code>null</code>.
90         * @param qname The qualified name for this element.
91         * @param attrs Attributes of the element being started.
92         *              Will not be <code>null</code>.
93         * @param context The current context.
94         * @return a handler (in the derived classes)
95         *
96         * @exception SAXParseException if this method is not overridden, or in
97         *                              case of error in an overridden version
98         */
99        public AntHandler onStartChild(String uri, String tag, String qname,
00                                       Attributes attrs,
01                                       AntXMLContext context)
02            throws SAXParseException {
03            throw new SAXParseException("Unexpected element \"" + qname
04                + " \"", context.getLocator());
05        }
06
07        /**
08         * Handle the end of a element.
09         *
10         * @param uri the namespace uri of the element
11         * @param tag the tag of the element
12         * @param qname the qualified name of the element
13         * @param context the current context
14         * @exception SAXParseException if an error occurs
15         */
16        public void onEndChild(String uri, String tag, String qname,
17                                     AntXMLContext context)
18            throws SAXParseException {
19        }
20
21        /**
22         * This method is called when this element and all elements nested into it have been
23         * handled. I.e., this happens at the &lt;/end_tag_of_the_element&gt;.
24         * @param uri the namespace uri for this element
25         * @param tag the element name
26         * @param context the current context
27         */
28        public void onEndElement(String uri, String tag,
29                                 AntXMLContext context) {
30        }
31
32        /**
33         * Handles text within an element. This base implementation just
34         * throws an exception, you must override it if you expect content.
35         *
36         * @param buf A character array of the text within the element.
37         *            Will not be <code>null</code>.
38         * @param start The start element in the array.
39         * @param count The number of characters to read from the array.
40         * @param context The current context.
41         *
42         * @exception SAXParseException if this method is not overridden, or in
43         *                              case of error in an overridden version
44         */
45        public void characters(char[] buf, int start, int count, AntXMLContext context)
46            throws SAXParseException {
47            String s = new String(buf, start, count).trim();
48
49            if (s.length() > 0) {
50                throw new SAXParseException("Unexpected text \"" + s
51                    + "\"", context.getLocator());
52            }
53        }
54
55        /**
56         * Will be called every time a namespace is reached.
57         * It'll verify if the ns was processed, and if not load the task
58         * definitions.
59         * @param uri The namespace uri.
60         */
61        protected void checkNamespace(String uri) {
62
63        }
64    }
65
66    /**
67     * Handler for ant processing. Uses a stack of AntHandlers to
68     * implement each element ( the original parser used a recursive behavior,
69     * with the implicit execution stack )
70     */
71    public static class RootHandler extends DefaultHandler {
72        private Stack antHandlers = new Stack();
73        private AntHandler currentHandler = null;
74        private AntXMLContext context;
75
76        /**
77         * Creates a new RootHandler instance.
78         *
79         * @param context The context for the handler.
80         * @param rootHandler The handler for the root element.
81         */
82        public RootHandler(AntXMLContext context, AntHandler rootHandler) {
83            currentHandler = rootHandler;
84            antHandlers.push(currentHandler);
85            this.context = context;
86        }
87
88        /**
89         * Returns the current ant handler object.
90         * @return the current ant handler.
91         */
92        public AntHandler getCurrentAntHandler() {
93            return currentHandler;
94        }
95
96        /**
97         * Resolves file: URIs relative to the build file.
98         *
99         * @param publicId The public identifier, or <code>null</code>
00         *                 if none is available. Ignored in this
01         *                 implementation.
02         * @param systemId The system identifier provided in the XML
03         *                 document. Will not be <code>null</code>.
04         * @return an inputsource for this identifier
05         */
06        public InputSource resolveEntity(String publicId,
07                                         String systemId) {
08
09            context.getProject().log("resolving systemId: "
10                + systemId, Project.MSG_VERBOSE);
11
12            if (systemId.startsWith("file:")) {
13                String path = fu.fromURI(systemId);
14
15                File file = new File(path);
16                if (!file.isAbsolute()) {
17                    file = fu.resolveFile(context.getBuildFileParent(), path);
18                }
19                try {
20                    InputSource inputSource =
21                            new InputSource(new FileInputStream(file));
22                    inputSource.setSystemId(fu.toURI(file.getAbsolutePath()));
23                    return inputSource;
24                } catch (FileNotFoundException fne) {
25                    context.getProject().log(file.getAbsolutePath()
26                        + " could not be found", Project.MSG_WARN);
27                }
28
29            }
30            // use default if not file or file not found
31            return null;
32        }
33
34        /**
35         * Handles the start of a project element. A project handler is created
36         * and initialised with the element name and attributes.
37         *
38         * @param uri The namespace uri for this element.
39         * @param tag The name of the element being started.
40         *            Will not be <code>null</code>.
41         * @param qname The qualified name for this element.
42         * @param attrs Attributes of the element being started.
43         *              Will not be <code>null</code>.
44         *
45         * @exception org.xml.sax.SAXParseException if the tag given is not
46         *                              <code>"project"</code>
47         */
48        public void startElement(String uri, String tag, String qname, Attributes attrs)
49            throws SAXParseException {
50            AntHandler next
51                = currentHandler.onStartChild(uri, tag, qname, attrs, context);
52            antHandlers.push(currentHandler);
53            currentHandler = next;
54            currentHandler.onStartElement(uri, tag, qname, attrs, context);
55        }
56
57        /**
58         * Sets the locator in the project helper for future reference.
59         *
60         * @param locator The locator used by the parser.
61         *                Will not be <code>null</code>.
62         */
63        public void setDocumentLocator(Locator locator) {
64            context.setLocator(locator);
65        }
66
67        /**
68         * Handles the end of an element. Any required clean-up is performed
69         * by the onEndElement() method and then the original handler
70         * is restored to the parser.
71         *
72         * @param uri  The namespace URI for this element.
73         * @param name The name of the element which is ending.
74         *             Will not be <code>null</code>.
75         * @param qName The qualified name for this element.
76         *
77         * @exception SAXException in case of error (not thrown in
78         *                         this implementation)
79         *
80         */
81        public void endElement(String uri, String name, String qName) throws SAXException {
82            currentHandler.onEndElement(uri, name, context);
83            AntHandler prev = (AntHandler) antHandlers.pop();
84            currentHandler = prev;
85            if (currentHandler != null) {
86                currentHandler.onEndChild(uri, name, qName, context);
87            }
88        }
89
90        /**
91         * Handle text within an element, calls currentHandler.characters.
92         *
93         * @param buf  A character array of the test.
94         * @param start The start offset in the array.
95         * @param count The number of characters to read.
96         * @exception SAXParseException if an error occurs
97         */
98        public void characters(char[] buf, int start, int count)
99            throws SAXParseException {
00            currentHandler.characters(buf, start, count, context);
01        }
02
03        /**
04         * Start a namespace prefix to uri mapping
05         *
06         * @param prefix the namespace prefix
07         * @param uri the namespace uri
08         */
09        public void startPrefixMapping(String prefix, String uri) {
10            context.startPrefixMapping(prefix, uri);
11        }
12
13        /**
14         * End a namepace prefix to uri mapping
15         *
16         * @param prefix the prefix that is not mapped anymore
17         */
18        public void endPrefixMapping(String prefix) {
19            context.endPrefixMapping(prefix);
20        }
21    }
22
23    /**
24     * The main handler - it handles the &lt;project&gt; tag.
25     *
26     * @see AntHandler
27     */
28    public static class MainHandler extends AntHandler {
29
30        /**
31         * Handle the project tag
32         *
33         * @param uri The namespace uri.
34         * @param name The element tag.
35         * @param qname The element qualified name.
36         * @param attrs The attributes of the element.
37         * @param context The current context.
38         * @return The project handler that handles subelements of project
39         * @exception SAXParseException if the qualified name is not "project".
40         */
41        public AntHandler onStartChild(String uri, String name, String qname,
42                                       Attributes attrs,
43                                       AntXMLContext context)
44            throws SAXParseException {
45            if (name.equals("project")
46                && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
47                return ProjectHelper3.projectHandler;
48            } else {
49//                 if (context.importlevel > 0) {
50//                     // we are in an imported file. Allow top-level <target>.
51//                     if (qname.equals( "target" ) )
52//                         return ProjectHelper3.targetHandler;
53//                 }
54                throw new SAXParseException("Unexpected element \"" + qname
55                    + "\" " + name, context.getLocator());
56            }
57        }
58    }
59
60    /**
61     * Handler for the top level "project" element.
62     */
63    public static class ProjectHandler extends AntHandler {
64
65        /**
66         * Initialisation routine called after handler creation
67         * with the element name and attributes. The attributes which
68         * this handler can deal with are: <code>"default"</code>,
69         * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
70         *
71         * @param uri The namespace URI for this element.
72         * @param tag Name of the element which caused this handler
73         *            to be created. Should not be <code>null</code>.
74         *            Ignored in this implementation.
75         * @param qname The qualified name for this element.
76         * @param attrs Attributes of the element which caused this
77         *              handler to be created. Must not be <code>null</code>.
78         * @param context The current context.
79         *
80         * @exception SAXParseException if an unexpected attribute is
81         *            encountered or if the <code>"default"</code> attribute
82         *            is missing.
83         */
84        public void onStartElement(String uri, String tag, String qname,
85                                   Attributes attrs,
86                                   AntXMLContext context)
87            throws SAXParseException {
88            String id = null;
89            String baseDir = null;
90            boolean nameAttributeSet = false;
91
92            Project project = context.getProject();
93
94            /** XXX I really don't like this - the XML processor is still
95             * too 'involved' in the processing. A better solution (IMO)
96             * would be to create UE for Project and Target too, and
97             * then process the tree and have Project/Target deal with
98             * its attributes ( similar with Description ).
99             *
00             * If we eventually switch to ( or add support for ) DOM,
01             * things will work smoothly - UE can be avoided almost completely
02             * ( it could still be created on demand, for backward compatibility )
03             */
04
05            for (int i = 0; i < attrs.getLength(); i++) {
06                String attrUri = attrs.getURI(i);
07                if (attrUri != null
08                    && !attrUri.equals("")
09                    && !attrUri.equals(uri)) {
10                    continue; // Ignore attributes from unknown uris
11                }
12                String key = attrs.getLocalName(i);
13                String value = attrs.getValue(i);
14
15                if (key.equals("default")) {
16                    if (value != null && !value.equals("")) {
17                        if (!context.isIgnoringProjectTag()) {
18                            project.setDefault(value);
19                        }
20                    }
21                } else if (key.equals("name")) {
22                    if (value != null) {
23                        context.setCurrentProjectName(value);
24                        nameAttributeSet = true;
25                        if (!context.isIgnoringProjectTag()) {
26                            project.setName(value);
27                            project.addReference(value, project);
28                        }
29                    }
30                } else if (key.equals("id")) {
31                    if (value != null) {
32                        // What's the difference between id and name ?
33                        if (!context.isIgnoringProjectTag()) {
34                            project.addReference(value, project);
35                        }
36                    }
37                } else if (key.equals("basedir")) {
38                    if (!context.isIgnoringProjectTag()) {
39                        baseDir = value;
40                    }
41                } else {
42                    // XXX ignore attributes in a different NS ( maybe store them ? )
43                    throw new SAXParseException("Unexpected attribute \""
44                        + attrs.getQName(i) + "\"", context.getLocator());
45                }
46            }
47
48            // XXX Move to Project ( so it is shared by all helpers )
49            String antFileProp = "ant.file." + context.getCurrentProjectName();
50            String dup = project.getProperty(antFileProp);
51            if (dup != null && nameAttributeSet) {
52                File dupFile = new File(dup);
53                if (context.isIgnoringProjectTag()
54                    && !dupFile.equals(context.getBuildFile())) {
55                    project.log("Duplicated project name in import. Project "
56                        + context.getCurrentProjectName() + " defined first in "
57                        + dup + " and again in " + context.getBuildFile(),
58                        Project.MSG_WARN);
59                }
60            }
61
62            if (context.getBuildFile() != null) {
63                project.setUserProperty("ant.file."
64                    + context.getCurrentProjectName(),
65                    context.getBuildFile().toString());
66            }
67
68            if (context.isIgnoringProjectTag()) {
69                // no further processing
70                return;
71            }
72            // set explicitly before starting ?
73            if (project.getProperty("basedir") != null) {
74                project.setBasedir(project.getProperty("basedir"));
75            } else {
76                // Default for baseDir is the location of the build file.
77                if (baseDir == null) {
78                    project.setBasedir(context.getBuildFileParent().getAbsolutePath());
79                } else {
80                    // check whether the user has specified an absolute path
81                    if ((new File(baseDir)).isAbsolute()) {
82                        project.setBasedir(baseDir);
83                    } else {
84                        project.setBaseDir(fu.resolveFile(
85                                               context.getBuildFileParent(), baseDir));
86                    }
87                }
88            }
89
90            project.addTarget("", context.getImplicitTarget());
91            context.setCurrentTarget(context.getImplicitTarget());
92        }
93
94        /**
95         * Handles the start of a top-level element within the project. An
96         * appropriate handler is created and initialised with the details
97         * of the element.
98         *
99         * @param uri The namespace URI for this element.
00         * @param name The name of the element being started.
01         *            Will not be <code>null</code>.
02         * @param qname The qualified name for this element.
03         * @param attrs Attributes of the element being started.
04         *              Will not be <code>null</code>.
05         * @param context The context for this element.
06         * @return a target or an element handler.
07         *
08         * @exception org.xml.sax.SAXParseException if the tag given is not
09         *            <code>"taskdef"</code>, <code>"typedef"</code>,
10         *            <code>"property"</code>, <code>"target"</code>
11         *            or a data type definition
12         */
13        public AntHandler onStartChild(String uri, String name, String qname,
14                                       Attributes attrs,
15                                       AntXMLContext context)
16            throws SAXParseException {
17            if (name.equals("target")
18                && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
19                return ProjectHelper3.targetHandler;
20            } else {
21                return ProjectHelper3.elementHandler;
22            }
23        }
24
25    }
26
27    /**
28     * Handler for "target" elements.
29     */
30    public static class TargetHandler extends AntHandler {
31
32        /**
33         * Initialisation routine called after handler creation
34         * with the element name and attributes. The attributes which
35         * this handler can deal with are: <code>"name"</code>,
36         * <code>"depends"</code>, <code>"if"</code>,
37         * <code>"unless"</code>, <code>"id"</code> and
38         * <code>"description"</code>.
39         *
40         * @param uri The namespace URI for this element.
41         * @param tag Name of the element which caused this handler
42         *            to be created. Should not be <code>null</code>.
43         *            Ignored in this implementation.
44         * @param qname The qualified name for this element.
45         * @param attrs Attributes of the element which caused this
46         *              handler to be created. Must not be <code>null</code>.
47         * @param context The current context.
48         *
49         * @exception SAXParseException if an unexpected attribute is encountered
50         *            or if the <code>"name"</code> attribute is missing.
51         */
52        public void onStartElement(String uri, String tag, String qname,
53                                   Attributes attrs,
54                                   AntXMLContext context)
55            throws SAXParseException {
56            String name = null;
57            String depends = "";
58
59            Project project = context.getProject();
60            Target target = new Target();
61            target.setProject(project);
62            context.addTarget(target);
63
64            for (int i = 0; i < attrs.getLength(); i++) {
65                String attrUri = attrs.getURI(i);
66                if (attrUri != null
67                    && !attrUri.equals("")
68                    && !attrUri.equals(uri)) {
69                    continue; // Ignore attributes from unknown uris
70                }
71                String key = attrs.getLocalName(i);
72                String value = attrs.getValue(i);
73
74                if (key.equals("name")) {
75                    name = value;
76                    if ("".equals(name)) {
77                        throw new BuildException("name attribute must "
78                            + "not be empty");
79                    }
80                } else if (key.equals("depends")) {
81                    depends = value;
82                } else if (key.equals("if")) {
83                    target.setIf(value);
84                } else if (key.equals("unless")) {
85                    target.setUnless(value);
86                } else if (key.equals("id")) {
87                    if (value != null && !value.equals("")) {
88                        context.getProject().addReference(value, target);
89                    }
90                } else if (key.equals("description")) {
91                    target.setDescription(value);
92                } else {
93                    throw new SAXParseException("Unexpected attribute \""
94                        + key + "\"", context.getLocator());
95                }
96            }
97
98            if (name == null) {
99                throw new SAXParseException("target element appears without "
00                    + "a name attribute", context.getLocator());
01            }
02
03            Hashtable currentTargets = project.getTargets();
04
05            // If the name has already been defined ( import for example )
06            if (currentTargets.containsKey(name)) {
07                if (!context.isIgnoringProjectTag()) {
08                    // not in a import'ed file
09                    throw new BuildException(
10                        "Duplicate target '" + name + "'",
11                        new Location(context.getLocator().getSystemId(),
12                                     context.getLocator().getLineNumber(),
13                                     context.getLocator().getColumnNumber()));
14                }
15                // Alter the name.
16                if (context.getCurrentProjectName() != null) {
17                    String newName = context.getCurrentProjectName()
18                        + "." + name;
19                    project.log("Already defined in main or a previous import, "
20                        + "define " + name + " as " + newName,
21                                Project.MSG_VERBOSE);
22                    name = newName;
23                } else {
24                    project.log("Already defined in main or a previous import, "
25                        + "ignore " + name, Project.MSG_VERBOSE);
26                    name = null;
27                }
28            }
29
30            if (name != null) {
31                target.setName(name);
32                project.addOrReplaceTarget(name, target);
33            }
34
35            // take care of dependencies
36            if (depends.length() > 0) {
37                target.setDepends(depends);
38            }
39        }
40
41        /**
42         * Handles the start of an element within a target.
43         *
44         * @param uri The namespace URI for this element.
45         * @param name The name of the element being started.
46         *            Will not be <code>null</code>.
47         * @param qname The qualified name for this element.
48         * @param attrs Attributes of the element being started.
49         *              Will not be <code>null</code>.
50         * @param context The current context.
51         * @return an element handler.
52         *
53         * @exception SAXParseException if an error occurs when initialising
54         *                              the appropriate child handler
55         */
56        public AntHandler onStartChild(String uri, String name, String qname,
57                                       Attributes attrs,
58                                       AntXMLContext context)
59            throws SAXParseException {
60            return ProjectHelper3.elementHandler;
61        }
62
63        /**
64         * Handle the end of the project, sets the current target of the
65         * context to be the implicit target.
66         *
67         * @param uri The namespace URI of the element.
68         * @param tag The name of the element.
69         * @param context The current context.
70         */
71        public void onEndElement(String uri, String tag, AntXMLContext context) {
72            context.setCurrentTarget(context.getImplicitTarget());
73        }
74    }
75
76    /**
77     * Handler for all project elements ( tasks, data types )
78     */
79    public static class ElementHandler extends AntHandler {
80
81        /**
82         * Constructor.
83         */
84        public ElementHandler() {
85        }
86
87        /**
88         * Initialisation routine called after handler creation
89         * with the element name and attributes. This configures
90         * the element with its attributes and sets it up with
91         * its parent container (if any). Nested elements are then
92         * added later as the parser encounters them.
93         *
94         * @param uri The namespace URI for this element.
95         * @param tag Name of the element which caused this handler
96         *            to be created. Must not be <code>null</code>.
97         * @param qname The qualified name for this element.
98         * @param attrs Attributes of the element which caused this
99         *              handler to be created. Must not be <code>null</code>.
00         * @param context The current context.
01         *
02         * @exception SAXParseException in case of error (not thrown in
03         *                              this implementation)
04         */
05        public void onStartElement(String uri, String tag, String qname,
06                                   Attributes attrs,
07                                   AntXMLContext context)
08            throws SAXParseException {
09            RuntimeConfigurable parentWrapper = context.currentWrapper();
10            Object parent = null;
11
12            if (parentWrapper != null) {
13                parent = parentWrapper.getProxy();
14            }
15
16            /* UnknownElement is used for tasks and data types - with
17               delayed eval */
18            UnknownElement task = new UnknownElement(tag);
19            task.setProject(context.getProject());
20            task.setNamespace(uri);
21            task.setQName(qname);
22            task.setTaskType(
23                ProjectHelper.genComponentName(task.getNamespace(), tag));
24            task.setTaskName(qname);
25
26            Location location = new Location(context.getLocator().getSystemId(),
27                    context.getLocator().getLineNumber(),
28                    context.getLocator().getColumnNumber());
29            task.setLocation(location);
30            task.setOwningTarget(context.getCurrentTarget());
31
32            context.configureId(task, attrs);
33
34            if (parent != null) {
35                // Nested element
36                ((UnknownElement) parent).addChild(task);
37            }  else {
38                // Task included in a target ( including the default one ).
39                context.getCurrentTarget().addTask(task);
40            }
41
42            // container.addTask(task);
43            // This is a nop in UE: task.init();
44
45            RuntimeConfigurable wrapper
46                = new RuntimeConfigurable(task, task.getTaskName());
47
48            for (int i = 0; i < attrs.getLength(); i++) {
49                String name = attrs.getLocalName(i);
50                String attrUri = attrs.getURI(i);
51                if (attrUri != null
52                    && !attrUri.equals("")
53                    && !attrUri.equals(uri)) {
54                    name = attrUri + ":" + attrs.getQName(i);
55                }
56                String value = attrs.getValue(i);
57                // PR: Hack for ant-type value
58                //  an ant-type is a component name which can
59                // be namespaced, need to extract the name
60                // and convert from qualified name to uri/name
61                if (ANT_TYPE.equals(name)
62                    || (ANT_CORE_URI.equals(attrUri)
63                        && ANT_TYPE.equals(attrs.getLocalName(i)))) {
64                    name = ANT_TYPE;
65                    int index = value.indexOf(":");
66                    if (index != -1) {
67                        String prefix = value.substring(0, index);
68                        String mappedUri = context.getPrefixMapping(prefix);
69                        if (mappedUri == null) {
70                            throw new BuildException(
71                                "Unable to find XML NS prefix " + prefix);
72                        }
73                        value = ProjectHelper.genComponentName(
74                            mappedUri, value.substring(index + 1));
75                    }
76                }
77                wrapper.setAttribute(name, value);
78            }
79
80            if (parentWrapper != null) {
81                parentWrapper.addChild(wrapper);
82            }
83
84            context.pushWrapper(wrapper);
85        }
86
87        /**
88         * Adds text to the task, using the wrapper
89         *
90         * @param buf A character array of the text within the element.
91         *            Will not be <code>null</code>.
92         * @param start The start element in the array.
93         * @param count The number of characters to read from the array.
94         * @param context The current context.
95         *
96         * @exception SAXParseException if the element doesn't support text
97         *
98         * @see ProjectHelper#addText(Project,java.lang.Object,char[],int,int)
99         */
00        public void characters(char[] buf, int start, int count,
01                               AntXMLContext context)
02            throws SAXParseException {
03            RuntimeConfigurable wrapper = context.currentWrapper();
04            wrapper.addText(buf, start, count);
05        }
06
07        /**
08         * Handles the start of an element within a target. Task containers
09         * will always use another task handler, and all other tasks
10         * will always use a nested element handler.
11         *
12         * @param uri The namespace URI for this element.
13         * @param tag The name of the element being started.
14         *            Will not be <code>null</code>.
15         * @param qname The qualified name for this element.
16         * @param attrs Attributes of the element being started.
17         *              Will not be <code>null</code>.
18         * @param context The current context.
19         * @return The handler for elements.
20         *
21         * @exception SAXParseException if an error occurs when initialising
22         *                              the appropriate child handler
23         */
24        public AntHandler onStartChild(String uri, String tag, String qname,
25                                       Attributes attrs,
26                                       AntXMLContext context)
27            throws SAXParseException {
28            return ProjectHelper3.elementHandler;
29        }
30
31        /**
32         * Handles the end of the element. This pops the wrapper from
33         * the context.
34         *
35         * @param uri The namespace URI for the element.
36         * @param tag The name of the element.
37         * @param context The current context.
38         */
39        public void onEndElement(String uri, String tag, AntXMLContext context) {
40            context.popWrapper();
41        }
42    }
43}
44