Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,061   Methods: 33
NCLOC: 451   Classes: 9
 
 Source file Conditionals Statements Methods TOTAL
ProjectHelperImpl.java 0% 0% 0% 0%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.helper;
 56   
 
 57   
 import java.io.File;
 58   
 import java.io.FileInputStream;
 59   
 import java.io.FileNotFoundException;
 60   
 import java.io.IOException;
 61   
 import java.io.UnsupportedEncodingException;
 62   
 import java.util.Locale;
 63   
 import org.apache.tools.ant.BuildException;
 64   
 import org.apache.tools.ant.IntrospectionHelper;
 65   
 import org.apache.tools.ant.Location;
 66   
 import org.apache.tools.ant.Project;
 67   
 import org.apache.tools.ant.ProjectHelper;
 68   
 import org.apache.tools.ant.RuntimeConfigurable;
 69   
 import org.apache.tools.ant.Target;
 70   
 import org.apache.tools.ant.Task;
 71   
 import org.apache.tools.ant.TaskAdapter;
 72   
 import org.apache.tools.ant.TaskContainer;
 73   
 import org.apache.tools.ant.UnknownElement;
 74   
 import org.apache.tools.ant.util.FileUtils;
 75   
 import org.apache.tools.ant.util.JAXPUtils;
 76   
 import org.xml.sax.AttributeList;
 77   
 import org.xml.sax.DocumentHandler;
 78   
 import org.xml.sax.HandlerBase;
 79   
 import org.xml.sax.InputSource;
 80   
 import org.xml.sax.Locator;
 81   
 import org.xml.sax.SAXException;
 82   
 import org.xml.sax.SAXParseException;
 83   
 import org.xml.sax.helpers.XMLReaderAdapter;
 84   
 
 85   
 /**
 86   
  * Original helper.
 87   
  *
 88   
  * @author duncan@x180.com
 89   
  */
 90   
 public class ProjectHelperImpl extends ProjectHelper {
 91   
 
 92   
     /**
 93   
      * SAX 1 style parser used to parse the given file. This may
 94   
      * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
 95   
      */
 96   
     private org.xml.sax.Parser parser;
 97   
 
 98   
     /** The project to configure. */
 99   
     private Project project;
 100   
     /** The configuration file to parse. */
 101   
     private File buildFile;
 102   
     /**
 103   
      * Parent directory of the build file. Used for resolving entities
 104   
      * and setting the project's base directory.
 105   
      */
 106   
     private File buildFileParent;
 107   
     /**
 108   
      * Locator for the configuration file parser.
 109   
      * Used for giving locations of errors etc.
 110   
      */
 111   
     private Locator locator;
 112   
     /**
 113   
      * Target that all other targets will depend upon implicitly.
 114   
      *
 115   
      * <p>This holds all tasks and data type definitions that have
 116   
      * been placed outside of targets.</p>
 117   
      */
 118   
     private Target implicitTarget = new Target();
 119   
     /**
 120   
      * helper for path -> URI and URI -> path conversions.
 121   
      */
 122   
     private static FileUtils fu = FileUtils.newFileUtils();
 123   
 
 124  0
     public ProjectHelperImpl() {
 125  0
         implicitTarget.setName("");
 126   
     }
 127   
 
 128   
     /**
 129   
      * Parses the project file, configuring the project as it goes.
 130   
      *
 131   
      * @param project project instance to be configured.
 132   
      * @param source the source from which the project is read.
 133   
      * @exception BuildException if the configuration is invalid or cannot
 134   
      *                           be read.
 135   
      */
 136  0
     public void parse(Project project, Object source) throws BuildException {
 137  0
         if (!(source instanceof File)) {
 138  0
             throw new BuildException("Only File source supported by default plugin");
 139   
         }
 140  0
         File buildFile = (File) source;
 141  0
         FileInputStream inputStream = null;
 142  0
         InputSource inputSource = null;
 143   
 
 144  0
         this.project = project;
 145  0
         this.buildFile = new File(buildFile.getAbsolutePath());
 146  0
         buildFileParent = new File(this.buildFile.getParent());
 147   
 
 148  0
         try {
 149  0
             try {
 150  0
                 parser = JAXPUtils.getParser();
 151   
             } catch (BuildException e) {
 152  0
                 parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
 153   
             }
 154   
 
 155   
 
 156  0
             String uri = fu.toURI(buildFile.getAbsolutePath());
 157  0
             inputStream = new FileInputStream(buildFile);
 158  0
             inputSource = new InputSource(inputStream);
 159  0
             inputSource.setSystemId(uri);
 160  0
             project.log("parsing buildfile " + buildFile + " with URI = "
 161   
                 + uri, Project.MSG_VERBOSE);
 162  0
             HandlerBase hb = new RootHandler(this);
 163  0
             parser.setDocumentHandler(hb);
 164  0
             parser.setEntityResolver(hb);
 165  0
             parser.setErrorHandler(hb);
 166  0
             parser.setDTDHandler(hb);
 167  0
             parser.parse(inputSource);
 168   
         } catch (SAXParseException exc) {
 169  0
             Location location =
 170   
                 new Location(exc.getSystemId(), exc.getLineNumber(),
 171   
                     exc.getColumnNumber());
 172   
 
 173  0
             Throwable t = exc.getException();
 174  0
             if (t instanceof BuildException) {
 175  0
                 BuildException be = (BuildException) t;
 176  0
                 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
 177  0
                     be.setLocation(location);
 178   
                 }
 179  0
                 throw be;
 180   
             }
 181   
 
 182  0
             throw new BuildException(exc.getMessage(), t, location);
 183   
         } catch (SAXException exc) {
 184  0
             Throwable t = exc.getException();
 185  0
             if (t instanceof BuildException) {
 186  0
                 throw (BuildException) t;
 187   
             }
 188  0
             throw new BuildException(exc.getMessage(), t);
 189   
         } catch (FileNotFoundException exc) {
 190  0
             throw new BuildException(exc);
 191   
         } catch (UnsupportedEncodingException exc) {
 192  0
             throw new BuildException("Encoding of project file is invalid.",
 193   
                                      exc);
 194   
         } catch (IOException exc) {
 195  0
             throw new BuildException("Error reading project file: "
 196   
                                      + exc.getMessage(), exc);
 197   
         } finally {
 198  0
             if (inputStream != null) {
 199  0
                 try {
 200  0
                     inputStream.close();
 201   
                 } catch (IOException ioe) {
 202   
                     // ignore this
 203   
                 }
 204   
             }
 205   
         }
 206   
     }
 207   
 
 208   
     /**
 209   
      * The common superclass for all SAX event handlers used to parse
 210   
      * the configuration file. Each method just throws an exception,
 211   
      * so subclasses should override what they can handle.
 212   
      *
 213   
      * Each type of XML element (task, target, etc.) in Ant has
 214   
      * a specific subclass.
 215   
      *
 216   
      * In the constructor, this class takes over the handling of SAX
 217   
      * events from the parent handler and returns
 218   
      * control back to the parent in the endElement method.
 219   
      */
 220   
     static class AbstractHandler extends HandlerBase {
 221   
 
 222   
         /**
 223   
          * Previous handler for the document.
 224   
          * When the next element is finished, control returns
 225   
          * to this handler.
 226   
          */
 227   
         protected DocumentHandler parentHandler;
 228   
 
 229   
         /** Helper impl. With non-static internal classes, the compiler will generate
 230   
             this automatically - but this will fail with some compilers ( reporting
 231   
             "Expecting to find object/array on stack" ). If we pass it
 232   
             explicitely it'll work with more compilers.
 233   
         */
 234   
         ProjectHelperImpl helperImpl;
 235   
 
 236   
         /**
 237   
          * Creates a handler and sets the parser to use it
 238   
          * for the current element.
 239   
          *
 240   
          * @param helperImpl the ProjectHelperImpl instance associated
 241   
          *                   with this handler.
 242   
          *
 243   
          * @param parentHandler The handler which should be restored to the
 244   
          *                      parser at the end of the element.
 245   
          *                      Must not be <code>null</code>.
 246   
          */
 247  0
         public AbstractHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
 248  0
             this.parentHandler = parentHandler;
 249  0
             this.helperImpl = helperImpl;
 250   
 
 251   
             // Start handling SAX events
 252  0
             helperImpl.parser.setDocumentHandler(this);
 253   
         }
 254   
 
 255   
         /**
 256   
          * Handles the start of an element. This base implementation just
 257   
          * throws an exception.
 258   
          *
 259   
          * @param tag The name of the element being started.
 260   
          *            Will not be <code>null</code>.
 261   
          * @param attrs Attributes of the element being started.
 262   
          *              Will not be <code>null</code>.
 263   
          *
 264   
          * @exception SAXParseException if this method is not overridden, or in
 265   
          *                              case of error in an overridden version
 266   
          */
 267  0
         public void startElement(String tag, AttributeList attrs) throws SAXParseException {
 268  0
             throw new SAXParseException("Unexpected element \"" + tag + "\"", helperImpl.locator);
 269   
         }
 270   
 
 271   
         /**
 272   
          * Handles text within an element. This base implementation just
 273   
          * throws an exception.
 274   
          *
 275   
          * @param buf A character array of the text within the element.
 276   
          *            Will not be <code>null</code>.
 277   
          * @param start The start element in the array.
 278   
          * @param count The number of characters to read from the array.
 279   
          *
 280   
          * @exception SAXParseException if this method is not overridden, or in
 281   
          *                              case of error in an overridden version
 282   
          */
 283  0
         public void characters(char[] buf, int start, int count) throws SAXParseException {
 284  0
             String s = new String(buf, start, count).trim();
 285   
 
 286  0
             if (s.length() > 0) {
 287  0
                 throw new SAXParseException("Unexpected text \"" + s + "\"", helperImpl.locator);
 288   
             }
 289   
         }
 290   
 
 291   
         /**
 292   
          * Handles the end of an element. Any required clean-up is performed
 293   
          * by the finished() method and then the original handler is restored to
 294   
          * the parser.
 295   
          *
 296   
          * @param name The name of the element which is ending.
 297   
          *             Will not be <code>null</code>.
 298   
          *
 299   
          * @exception SAXException in case of error (not thrown in
 300   
          *                         this implementation)
 301   
          */
 302  0
         public void endElement(String name) throws SAXException {
 303   
             // Let parent resume handling SAX events
 304  0
             helperImpl.parser.setDocumentHandler(parentHandler);
 305   
         }
 306   
     }
 307   
 
 308   
     /**
 309   
      * Handler for the root element. Its only child must be the "project" element.
 310   
      */
 311   
     static class RootHandler extends HandlerBase {
 312   
         ProjectHelperImpl helperImpl;
 313   
 
 314  0
         public RootHandler(ProjectHelperImpl helperImpl) {
 315  0
             this.helperImpl = helperImpl;
 316   
         }
 317   
 
 318   
         /**
 319   
          * Resolves file: URIs relative to the build file.
 320   
          *
 321   
          * @param publicId The public identifer, or <code>null</code>
 322   
          *                 if none is available. Ignored in this
 323   
          *                 implementation.
 324   
          * @param systemId The system identifier provided in the XML
 325   
          *                 document. Will not be <code>null</code>.
 326   
          */
 327  0
         public InputSource resolveEntity(String publicId,
 328   
                                          String systemId) {
 329   
 
 330  0
             helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
 331   
 
 332  0
             if (systemId.startsWith("file:")) {
 333  0
                 String path = fu.fromURI(systemId);
 334   
 
 335  0
                 File file = new File(path);
 336  0
                 if (!file.isAbsolute()) {
 337  0
                     file = fu.resolveFile(helperImpl.buildFileParent, path);
 338   
                 }
 339  0
                 try {
 340  0
                     InputSource inputSource = new InputSource(new FileInputStream(file));
 341  0
                     inputSource.setSystemId(fu.toURI(file.getAbsolutePath()));
 342  0
                     return inputSource;
 343   
                 } catch (FileNotFoundException fne) {
 344  0
                     helperImpl.project.log(file.getAbsolutePath() + " could not be found",
 345   
                                 Project.MSG_WARN);
 346   
                 }
 347   
             }
 348   
             // use default if not file or file not found
 349  0
             return null;
 350   
         }
 351   
 
 352   
         /**
 353   
          * Handles the start of a project element. A project handler is created
 354   
          * and initialised with the element name and attributes.
 355   
          *
 356   
          * @param tag The name of the element being started.
 357   
          *            Will not be <code>null</code>.
 358   
          * @param attrs Attributes of the element being started.
 359   
          *              Will not be <code>null</code>.
 360   
          *
 361   
          * @exception SAXParseException if the tag given is not
 362   
          *                              <code>"project"</code>
 363   
          */
 364  0
         public void startElement(String tag, AttributeList attrs) throws SAXParseException {
 365  0
             if (tag.equals("project")) {
 366  0
                 new ProjectHandler(helperImpl, this).init(tag, attrs);
 367   
             } else {
 368  0
                 throw new SAXParseException("Config file is not of expected XML type", helperImpl.locator);
 369   
             }
 370   
         }
 371   
 
 372   
         /**
 373   
          * Sets the locator in the project helper for future reference.
 374   
          *
 375   
          * @param locator The locator used by the parser.
 376   
          *                Will not be <code>null</code>.
 377   
          */
 378  0
         public void setDocumentLocator(Locator locator) {
 379  0
             helperImpl.locator = locator;
 380   
         }
 381   
     }
 382   
 
 383   
     /**
 384   
      * Handler for the top level "project" element.
 385   
      */
 386   
     static class ProjectHandler extends AbstractHandler {
 387   
 
 388   
         /**
 389   
          * Constructor which just delegates to the superconstructor.
 390   
          *
 391   
          * @param parentHandler The handler which should be restored to the
 392   
          *                      parser at the end of the element.
 393   
          *                      Must not be <code>null</code>.
 394   
          */
 395  0
         public ProjectHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
 396  0
             super(helperImpl, parentHandler);
 397   
         }
 398   
 
 399   
         /**
 400   
          * Initialisation routine called after handler creation
 401   
          * with the element name and attributes. The attributes which
 402   
          * this handler can deal with are: <code>"default"</code>,
 403   
          * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
 404   
          *
 405   
          * @param tag Name of the element which caused this handler
 406   
          *            to be created. Should not be <code>null</code>.
 407   
          *            Ignored in this implementation.
 408   
          * @param attrs Attributes of the element which caused this
 409   
          *              handler to be created. Must not be <code>null</code>.
 410   
          *
 411   
          * @exception SAXParseException if an unexpected attribute is
 412   
          *            encountered or if the <code>"default"</code> attribute
 413   
          *            is missing.
 414   
          */
 415  0
         public void init(String tag, AttributeList attrs) throws SAXParseException {
 416  0
             String def = null;
 417  0
             String name = null;
 418  0
             String id = null;
 419  0
             String baseDir = null;
 420   
 
 421  0
             for (int i = 0; i < attrs.getLength(); i++) {
 422  0
                 String key = attrs.getName(i);
 423  0
                 String value = attrs.getValue(i);
 424   
 
 425  0
                 if (key.equals("default")) {
 426  0
                     def = value;
 427  0
                 } else if (key.equals("name")) {
 428  0
                     name = value;
 429  0
                 } else if (key.equals("id")) {
 430  0
                     id = value;
 431  0
                 } else if (key.equals("basedir")) {
 432  0
                     baseDir = value;
 433   
                 } else {
 434  0
                     throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"",
 435   
                                                 helperImpl.locator);
 436   
                 }
 437   
             }
 438   
 
 439  0
             if (def != null && !def.equals("")) {
 440  0
                 helperImpl.project.setDefaultTarget(def);
 441   
             } else {
 442  0
                 throw new BuildException("The default attribute is required");
 443   
             }
 444   
 
 445  0
             if (name != null) {
 446  0
                 helperImpl.project.setName(name);
 447  0
                 helperImpl.project.addReference(name, helperImpl.project);
 448   
             }
 449   
 
 450  0
             if (id != null) {
 451  0
                 helperImpl.project.addReference(id, helperImpl.project);
 452   
             }
 453   
 
 454  0
             if (helperImpl.project.getProperty("basedir") != null) {
 455  0
                 helperImpl.project.setBasedir(helperImpl.project.getProperty("basedir"));
 456   
             } else {
 457  0
                 if (baseDir == null) {
 458  0
                     helperImpl.project.setBasedir(helperImpl.buildFileParent.getAbsolutePath());
 459   
                 } else {
 460   
                     // check whether the user has specified an absolute path
 461  0
                     if ((new File(baseDir)).isAbsolute()) {
 462  0
                         helperImpl.project.setBasedir(baseDir);
 463   
                     } else {
 464  0
                         helperImpl.project.setBaseDir(helperImpl.project.resolveFile(baseDir,
 465   
                                                                                      helperImpl.buildFileParent));
 466   
                     }
 467   
                 }
 468   
             }
 469   
 
 470  0
             helperImpl.project.addTarget("", helperImpl.implicitTarget);
 471   
         }
 472   
 
 473   
         /**
 474   
          * Handles the start of a top-level element within the project. An
 475   
          * appropriate handler is created and initialised with the details
 476   
          * of the element.
 477   
          *
 478   
          * @param name The name of the element being started.
 479   
          *            Will not be <code>null</code>.
 480   
          * @param attrs Attributes of the element being started.
 481   
          *              Will not be <code>null</code>.
 482   
          *
 483   
          * @exception SAXParseException if the tag given is not
 484   
          *            <code>"taskdef"</code>, <code>"typedef"</code>,
 485   
          *            <code>"property"</code>, <code>"target"</code>
 486   
          *            or a data type definition
 487   
          */
 488  0
         public void startElement(String name, AttributeList attrs) throws SAXParseException {
 489  0
             if (name.equals("target")) {
 490  0
                 handleTarget(name, attrs);
 491   
             } else {
 492  0
                 handleElement(helperImpl, this, helperImpl.implicitTarget, 
 493   
                               name, attrs);
 494   
             }
 495   
         }
 496   
 
 497   
         /**
 498   
          * Handles a target defintion element by creating a target handler
 499   
          * and initialising is with the details of the element.
 500   
          *
 501   
          * @param tag The name of the element to be handled.
 502   
          *            Will not be <code>null</code>.
 503   
          * @param attrs Attributes of the element to be handled.
 504   
          *              Will not be <code>null</code>.
 505   
          *
 506   
          * @exception SAXParseException if an error occurs initialising
 507   
          *                              the handler
 508   
          */
 509  0
         private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
 510  0
             new TargetHandler(helperImpl, this).init(tag, attrs);
 511   
         }
 512   
 
 513   
     }
 514   
 
 515   
     /**
 516   
      * Handler for "target" elements.
 517   
      */
 518   
     static class TargetHandler extends AbstractHandler {
 519   
         private Target target;
 520   
 
 521   
         /**
 522   
          * Constructor which just delegates to the superconstructor.
 523   
          *
 524   
          * @param parentHandler The handler which should be restored to the
 525   
          *                      parser at the end of the element.
 526   
          *                      Must not be <code>null</code>.
 527   
          */
 528  0
         public TargetHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
 529  0
             super(helperImpl, parentHandler);
 530   
         }
 531   
 
 532   
         /**
 533   
          * Initialisation routine called after handler creation
 534   
          * with the element name and attributes. The attributes which
 535   
          * this handler can deal with are: <code>"name"</code>,
 536   
          * <code>"depends"</code>, <code>"if"</code>,
 537   
          * <code>"unless"</code>, <code>"id"</code> and
 538   
          * <code>"description"</code>.
 539   
          *
 540   
          * @param tag Name of the element which caused this handler
 541   
          *            to be created. Should not be <code>null</code>.
 542   
          *            Ignored in this implementation.
 543   
          * @param attrs Attributes of the element which caused this
 544   
          *              handler to be created. Must not be <code>null</code>.
 545   
          *
 546   
          * @exception SAXParseException if an unexpected attribute is encountered
 547   
          *            or if the <code>"name"</code> attribute is missing.
 548   
          */
 549  0
         public void init(String tag, AttributeList attrs) throws SAXParseException {
 550  0
             String name = null;
 551  0
             String depends = "";
 552  0
             String ifCond = null;
 553  0
             String unlessCond = null;
 554  0
             String id = null;
 555  0
             String description = null;
 556   
 
 557  0
             for (int i = 0; i < attrs.getLength(); i++) {
 558  0
                 String key = attrs.getName(i);
 559  0
                 String value = attrs.getValue(i);
 560   
 
 561  0
                 if (key.equals("name")) {
 562  0
                     name = value;
 563  0
                     if (name.equals("")) {
 564  0
                         throw new BuildException("name attribute must not"
 565   
                                                  + " be empty",
 566   
                                                  new Location(helperImpl.locator));
 567   
                     }
 568  0
                 } else if (key.equals("depends")) {
 569  0
                     depends = value;
 570  0
                 } else if (key.equals("if")) {
 571  0
                     ifCond = value;
 572  0
                 } else if (key.equals("unless")) {
 573  0
                     unlessCond = value;
 574  0
                 } else if (key.equals("id")) {
 575  0
                     id = value;
 576  0
                 } else if (key.equals("description")) {
 577  0
                     description = value;
 578   
                 } else {
 579  0
                     throw new SAXParseException("Unexpected attribute \"" + key + "\"", helperImpl.locator);
 580   
                 }
 581   
             }
 582   
 
 583  0
             if (name == null) {
 584  0
                 throw new SAXParseException("target element appears without a name attribute",
 585   
                                             helperImpl.locator);
 586   
             }
 587   
 
 588  0
             target = new Target();
 589   
 
 590   
             // implicit target must be first on dependency list
 591  0
             target.addDependency("");
 592   
 
 593  0
             target.setName(name);
 594  0
             target.setIf(ifCond);
 595  0
             target.setUnless(unlessCond);
 596  0
             target.setDescription(description);
 597  0
             helperImpl.project.addTarget(name, target);
 598   
 
 599  0
             if (id != null && !id.equals("")) {
 600  0
                 helperImpl.project.addReference(id, target);
 601   
             }
 602   
 
 603   
             // take care of dependencies
 604   
 
 605  0
             if (depends.length() > 0) {
 606  0
                 target.setDepends(depends);
 607   
             }
 608   
         }
 609   
 
 610   
         /**
 611   
          * Handles the start of an element within a target.
 612   
          *
 613   
          * @param name The name of the element being started.
 614   
          *            Will not be <code>null</code>.
 615   
          * @param attrs Attributes of the element being started.
 616   
          *              Will not be <code>null</code>.
 617   
          *
 618   
          * @exception SAXParseException if an error occurs when initialising
 619   
          *                              the appropriate child handler
 620   
          */
 621  0
         public void startElement(String name, AttributeList attrs) throws SAXParseException {
 622  0
             handleElement(helperImpl, this, target, name, attrs);
 623   
         }
 624   
     }
 625   
 
 626   
     /**
 627   
      * Start a new DataTypeHandler if element is known to be a
 628   
      * data-type and a TaskHandler otherwise.
 629   
      *
 630   
      * <p>Factored out of TargetHandler.</p>
 631   
      *
 632   
      * @since Ant 1.6
 633   
      */
 634  0
     private static void handleElement(ProjectHelperImpl helperImpl,
 635   
                                       DocumentHandler parent,
 636   
                                       Target target, String elementName,
 637   
                                       AttributeList attrs)
 638   
         throws SAXParseException {
 639  0
         if (elementName.equals("description")) {
 640  0
             new DescriptionHandler(helperImpl, parent);
 641  0
         } else if (helperImpl.project.getDataTypeDefinitions()
 642   
                    .get(elementName) != null) {
 643  0
             new DataTypeHandler(helperImpl, parent, target)
 644   
                 .init(elementName, attrs);
 645   
         } else {
 646  0
             new TaskHandler(helperImpl, parent, target, null, target)
 647   
                 .init(elementName, attrs);
 648   
         }
 649   
     }
 650   
 
 651   
     /**
 652   
      * Handler for "description" elements.
 653   
      */
 654   
     static class DescriptionHandler extends AbstractHandler {
 655   
 
 656   
         /**
 657   
          * Constructor which just delegates to the superconstructor.
 658   
          *
 659   
          * @param parentHandler The handler which should be restored to the
 660   
          *                      parser at the end of the element.
 661   
          *                      Must not be <code>null</code>.
 662   
          */
 663  0
         public DescriptionHandler(ProjectHelperImpl helperImpl, 
 664   
                                   DocumentHandler parentHandler) {
 665  0
             super(helperImpl, parentHandler);
 666   
         }
 667   
 
 668   
         /**
 669   
          * Adds the text as description to the project.
 670   
          *
 671   
          * @param buf A character array of the text within the element.
 672   
          *            Will not be <code>null</code>.
 673   
          * @param start The start element in the array.
 674   
          * @param count The number of characters to read from the array.
 675   
          */
 676  0
         public void characters(char[] buf, int start, int count) {
 677  0
             String text = new String(buf, start, count);
 678  0
             String currentDescription = helperImpl.project.getDescription();
 679  0
             if (currentDescription == null) {
 680  0
                 helperImpl.project.setDescription(text);
 681   
             } else {
 682  0
                 helperImpl.project.setDescription(currentDescription + text);
 683   
             }
 684   
         }
 685   
 
 686   
     }
 687   
 
 688   
     /**
 689   
      * Handler for all task elements.
 690   
      */
 691   
     static class TaskHandler extends AbstractHandler {
 692   
         /** Containing target, if any. */
 693   
         private Target target;
 694   
         /**
 695   
          * Container for the task, if any. If target is
 696   
          * non-<code>null</code>, this must be too.
 697   
          */
 698   
         private TaskContainer container;
 699   
         /**
 700   
          * Task created by this handler.
 701   
          */
 702   
         private Task task;
 703   
         /**
 704   
          * Wrapper for the parent element, if any. The wrapper for this
 705   
          * element will be added to this wrapper as a child.
 706   
          */
 707   
         private RuntimeConfigurable parentWrapper;
 708   
         /**
 709   
          * Wrapper for this element which takes care of actually configuring
 710   
          * the element, if this element is contained within a target.
 711   
          * Otherwise the configuration is performed with the configure method.
 712   
          * @see ProjectHelper#configure(Object,AttributeList,Project)
 713   
          */
 714   
         private RuntimeConfigurable wrapper = null;
 715   
 
 716   
         /**
 717   
          * Constructor.
 718   
          *
 719   
          * @param parentHandler The handler which should be restored to the
 720   
          *                      parser at the end of the element.
 721   
          *                      Must not be <code>null</code>.
 722   
          *
 723   
          * @param container     Container for the element.
 724   
          *                      Must not be <code>null</code>.
 725   
          *
 726   
          * @param parentWrapper Wrapper for the parent element, if any.
 727   
          *                      May be <code>null</code>.
 728   
          *
 729   
          * @param target        Target this element is part of.
 730   
          *                      Must not be <code>null</code>.
 731   
          */
 732  0
         public TaskHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
 733   
                            TaskContainer container, RuntimeConfigurable parentWrapper, Target target) {
 734  0
             super(helperImpl, parentHandler);
 735  0
             this.container = container;
 736  0
             this.parentWrapper = parentWrapper;
 737  0
             this.target = target;
 738   
         }
 739   
 
 740   
         /**
 741   
          * Initialisation routine called after handler creation
 742   
          * with the element name and attributes. This configures
 743   
          * the element with its attributes and sets it up with
 744   
          * its parent container (if any). Nested elements are then
 745   
          * added later as the parser encounters them.
 746   
          *
 747   
          * @param tag Name of the element which caused this handler
 748   
          *            to be created. Must not be <code>null</code>.
 749   
          *
 750   
          * @param attrs Attributes of the element which caused this
 751   
          *              handler to be created. Must not be <code>null</code>.
 752   
          *
 753   
          * @exception SAXParseException in case of error (not thrown in
 754   
          *                              this implementation)
 755   
          */
 756  0
         public void init(String tag, AttributeList attrs) throws SAXParseException {
 757  0
             try {
 758  0
                 task = helperImpl.project.createTask(tag);
 759   
             } catch (BuildException e) {
 760   
                 // swallow here, will be thrown again in
 761   
                 // UnknownElement.maybeConfigure if the problem persists.
 762   
             }
 763   
 
 764  0
             if (task == null) {
 765  0
                 task = new UnknownElement(tag);
 766  0
                 task.setProject(helperImpl.project);
 767   
                 //XXX task.setTaskType(tag);
 768  0
                 task.setTaskName(tag);
 769   
             }
 770   
 
 771  0
             task.setLocation(new Location(helperImpl.locator));
 772  0
             helperImpl.configureId(task, attrs);
 773   
 
 774  0
             task.setOwningTarget(target);
 775  0
             container.addTask(task);
 776  0
             task.init();
 777  0
             wrapper = task.getRuntimeConfigurableWrapper();
 778  0
             wrapper.setAttributes(attrs);
 779  0
             if (parentWrapper != null) {
 780  0
                 parentWrapper.addChild(wrapper);
 781   
             }
 782   
         }
 783   
 
 784   
         /**
 785   
          * Adds text to the task, using the wrapper.
 786   
          *
 787   
          * @param buf A character array of the text within the element.
 788   
          *            Will not be <code>null</code>.
 789   
          * @param start The start element in the array.
 790   
          * @param count The number of characters to read from the array.
 791   
          */
 792  0
         public void characters(char[] buf, int start, int count) {
 793  0
             wrapper.addText(buf, start, count);
 794   
         }
 795   
 
 796   
         /**
 797   
          * Handles the start of an element within a target. Task containers
 798   
          * will always use another task handler, and all other tasks
 799   
          * will always use a nested element handler.
 800   
          *
 801   
          * @param name The name of the element being started.
 802   
          *            Will not be <code>null</code>.
 803   
          * @param attrs Attributes of the element being started.
 804   
          *              Will not be <code>null</code>.
 805   
          *
 806   
          * @exception SAXParseException if an error occurs when initialising
 807   
          *                              the appropriate child handler
 808   
          */
 809  0
         public void startElement(String name, AttributeList attrs) throws SAXParseException {
 810  0
             if (task instanceof TaskContainer) {
 811   
                 // task can contain other tasks - no other nested elements possible
 812  0
                 new TaskHandler(helperImpl, this, (TaskContainer) task,
 813   
                     wrapper, target).init(name, attrs);
 814   
             } else {
 815  0
                 new NestedElementHandler(helperImpl, this, task,
 816   
                     wrapper, target).init(name, attrs);
 817   
             }
 818   
         }
 819   
     }
 820   
 
 821   
     /**
 822   
      * Handler for all nested properties.
 823   
      */
 824   
     static class NestedElementHandler extends AbstractHandler {
 825   
         /** Parent object (task/data type/etc). */
 826   
         private Object parent;
 827   
         /** The nested element itself. */
 828   
         private Object child;
 829   
         /**
 830   
          * Wrapper for the parent element, if any. The wrapper for this
 831   
          * element will be added to this wrapper as a child.
 832   
          */
 833   
         private RuntimeConfigurable parentWrapper;
 834   
         /**
 835   
          * Wrapper for this element which takes care of actually configuring
 836   
          * the element, if a parent wrapper is provided.
 837   
          * Otherwise the configuration is performed with the configure method.
 838   
          * @see ProjectHelper#configure(Object,AttributeList,Project)
 839   
          */
 840   
         private RuntimeConfigurable childWrapper = null;
 841   
         /** Target this element is part of, if any. */
 842   
         private Target target;
 843   
 
 844   
         /**
 845   
          * Constructor.
 846   
          *
 847   
          * @param parentHandler The handler which should be restored to the
 848   
          *                      parser at the end of the element.
 849   
          *                      Must not be <code>null</code>.
 850   
          *
 851   
          * @param parent        Parent of this element (task/data type/etc).
 852   
          *                      Must not be <code>null</code>.
 853   
          *
 854   
          * @param parentWrapper Wrapper for the parent element, if any.
 855   
          *                      Must not be <code>null</code>.
 856   
          *
 857   
          * @param target        Target this element is part of.
 858   
          *                      Must not be <code>null</code>.
 859   
          */
 860  0
         public NestedElementHandler(ProjectHelperImpl helperImpl,
 861   
                                     DocumentHandler parentHandler,
 862   
                                     Object parent,
 863   
                                     RuntimeConfigurable parentWrapper,
 864   
                                     Target target) {
 865  0
             super(helperImpl, parentHandler);
 866   
 
 867  0
             if (parent instanceof TaskAdapter) {
 868  0
                 this.parent = ((TaskAdapter) parent).getProxy();
 869   
             } else {
 870  0
                 this.parent = parent;
 871   
             }
 872  0
             this.parentWrapper = parentWrapper;
 873  0
             this.target = target;
 874   
         }
 875   
 
 876   
         /**
 877   
          * Initialisation routine called after handler creation
 878   
          * with the element name and attributes. This configures
 879   
          * the element with its attributes and sets it up with
 880   
          * its parent container (if any). Nested elements are then
 881   
          * added later as the parser encounters them.
 882   
          *
 883   
          * @param propType Name of the element which caused this handler
 884   
          *            to be created. Must not be <code>null</code>.
 885   
          *
 886   
          * @param attrs Attributes of the element which caused this
 887   
          *              handler to be created. Must not be <code>null</code>.
 888   
          *
 889   
          * @exception SAXParseException in case of error, such as a
 890   
          *            BuildException being thrown during configuration.
 891   
          */
 892  0
         public void init(String propType, AttributeList attrs) throws SAXParseException {
 893  0
             Class parentClass = parent.getClass();
 894  0
             IntrospectionHelper ih =
 895   
                 IntrospectionHelper.getHelper(parentClass);
 896   
 
 897  0
             try {
 898  0
                 String elementName = propType.toLowerCase(Locale.US);
 899  0
                 if (parent instanceof UnknownElement) {
 900  0
                     UnknownElement uc = new UnknownElement(elementName);
 901  0
                     uc.setProject(helperImpl.project);
 902  0
                     ((UnknownElement) parent).addChild(uc);
 903  0
                     child = uc;
 904   
                 } else {
 905  0
                     child = ih.createElement(helperImpl.project, parent, elementName);
 906   
                 }
 907   
 
 908  0
                 helperImpl.configureId(child, attrs);
 909   
 
 910  0
                 childWrapper = new RuntimeConfigurable(child, propType);
 911  0
                 childWrapper.setAttributes(attrs);
 912  0
                 parentWrapper.addChild(childWrapper);
 913   
             } catch (BuildException exc) {
 914  0
                 throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
 915   
             }
 916   
         }
 917   
 
 918   
         /**
 919   
          * Adds text to the element, using the wrapper.
 920   
          *
 921   
          * @param buf A character array of the text within the element.
 922   
          *            Will not be <code>null</code>.
 923   
          * @param start The start element in the array.
 924   
          * @param count The number of characters to read from the array.
 925   
          */
 926  0
         public void characters(char[] buf, int start, int count) {
 927  0
             childWrapper.addText(buf, start, count);
 928   
         }
 929   
 
 930   
         /**
 931   
          * Handles the start of an element within this one. Task containers
 932   
          * will always use a task handler, and all other elements
 933   
          * will always use another nested element handler.
 934   
          *
 935   
          * @param name The name of the element being started.
 936   
          *            Will not be <code>null</code>.
 937   
          * @param attrs Attributes of the element being started.
 938   
          *              Will not be <code>null</code>.
 939   
          *
 940   
          * @exception SAXParseException if an error occurs when initialising
 941   
          *                              the appropriate child handler
 942   
          */
 943  0
         public void startElement(String name, AttributeList attrs) throws SAXParseException {
 944  0
             if (child instanceof TaskContainer) {
 945   
                 // taskcontainer nested element can contain other tasks - no other
 946   
                 // nested elements possible
 947  0
                 new TaskHandler(helperImpl, this, (TaskContainer) child,
 948   
                     childWrapper, target).init(name, attrs);
 949   
             } else {
 950  0
                 new NestedElementHandler(helperImpl, this, child,
 951   
                     childWrapper, target).init(name, attrs);
 952   
             }
 953   
         }
 954   
     }
 955   
 
 956   
     /**
 957   
      * Handler for all data types directly subordinate to project or target.
 958   
      */
 959   
     static class DataTypeHandler extends AbstractHandler {
 960   
         /** Parent target, if any. */
 961   
         private Target target;
 962   
         /** The element being configured. */
 963   
         private Object element;
 964   
         /** Wrapper for this element, if it's part of a target. */
 965   
         private RuntimeConfigurable wrapper = null;
 966   
 
 967   
         /**
 968   
          * Constructor with a target specified.
 969   
          *
 970   
          * @param parentHandler The handler which should be restored to the
 971   
          *                      parser at the end of the element.
 972   
          *                      Must not be <code>null</code>.
 973   
          *
 974   
          * @param target The parent target of this element.
 975   
          *               Must not be <code>null</code>.
 976   
          */
 977  0
         public DataTypeHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler, Target target) {
 978  0
             super(helperImpl, parentHandler);
 979  0
             this.target = target;
 980   
         }
 981   
 
 982   
         /**
 983   
          * Initialisation routine called after handler creation
 984   
          * with the element name and attributes. This configures
 985   
          * the element with its attributes and sets it up with
 986   
          * its parent container (if any). Nested elements are then
 987   
          * added later as the parser encounters them.
 988   
          *
 989   
          * @param propType Name of the element which caused this handler
 990   
          *            to be created. Must not be <code>null</code>.
 991   
          *
 992   
          * @param attrs Attributes of the element which caused this
 993   
          *              handler to be created. Must not be <code>null</code>.
 994   
          *
 995   
          * @exception SAXParseException in case of error, such as a
 996   
          *            BuildException being thrown during configuration.
 997   
          */
 998  0
         public void init(String propType, AttributeList attrs) throws SAXParseException {
 999  0
             try {
 1000  0
                 element = helperImpl.project.createDataType(propType);
 1001  0
                 if (element == null) {
 1002  0
                     throw new BuildException("Unknown data type " + propType);
 1003   
                 }
 1004   
 
 1005  0
                 wrapper = new RuntimeConfigurable(element, propType);
 1006  0
                 wrapper.setAttributes(attrs);
 1007  0
                 target.addDataType(wrapper);
 1008   
             } catch (BuildException exc) {
 1009  0
                 throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
 1010   
             }
 1011   
         }
 1012   
 
 1013   
         /**
 1014   
          * Adds text to the using the wrapper.
 1015   
          *
 1016   
          * @param buf A character array of the text within the element.
 1017   
          *            Will not be <code>null</code>.
 1018   
          * @param start The start element in the array.
 1019   
          * @param count The number of characters to read from the array.
 1020   
          *
 1021   
          * @see ProjectHelper#addText(Project,Object,char[],int,int)
 1022   
          */
 1023  0
         public void characters(char[] buf, int start, int count) {
 1024  0
             wrapper.addText(buf, start, count);
 1025   
         }
 1026   
 
 1027   
         /**
 1028   
          * Handles the start of an element within this one.
 1029   
          * This will always use a nested element handler.
 1030   
          *
 1031   
          * @param name The name of the element being started.
 1032   
          *            Will not be <code>null</code>.
 1033   
          * @param attrs Attributes of the element being started.
 1034   
          *              Will not be <code>null</code>.
 1035   
          *
 1036   
          * @exception SAXParseException if an error occurs when initialising
 1037   
          *                              the child handler
 1038   
          */
 1039  0
         public void startElement(String name, AttributeList attrs) throws SAXParseException {
 1040  0
             new NestedElementHandler(helperImpl, this, element, wrapper, target).init(name, attrs);
 1041   
         }
 1042   
     }
 1043   
 
 1044   
     /**
 1045   
      * Scans an attribute list for the <code>id</code> attribute and
 1046   
      * stores a reference to the target object in the project if an
 1047   
      * id is found.
 1048   
      * <p>
 1049   
      * This method was moved out of the configure method to allow
 1050   
      * it to be executed at parse time.
 1051   
      *
 1052   
      * @see #configure(Object,AttributeList,Project)
 1053   
      */
 1054  0
     private void configureId(Object target, AttributeList attr) {
 1055  0
         String id = attr.getValue("id");
 1056  0
         if (id != null) {
 1057  0
             project.addReference(id, target);
 1058   
         }
 1059   
     }
 1060   
 }
 1061