You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bo...@locus.apache.org on 2000/07/11 13:14:51 UTC

cvs commit: jakarta-ant/src/main/org/apache/tools/ant/taskdefs Ant.java Patch.java

bodewig     00/07/11 04:14:51

  Modified:    docs     index.html
               src/main/org/apache/tools/ant BuildException.java
                        ProjectHelper.java
               src/main/org/apache/tools/ant/taskdefs Ant.java Patch.java
  Added:       src/main/org/apache/tools/ant IntrospectionHelper.java
  Log:
  Rewritten the introspection part of ProjectHelper.
  
  1. Moved all introspection activities to a helper class.
  2. setter methods can now use parameters of a far richer set of
     types. See the modified patch task as an example.
  3. Nested Elements can be created by the task with createElement() or
     by IntrospectionHelper if the task implements void addElement(ObjectType).
  4. Documented addText(String) and nested elements in the "Writing Your
     own Task" section and changed the documentation for the setter methods.
  5. Added getLocation method to BuildException.
  6. Changed the return type of Ant.createProperty from Task to Property
     - as this is what gets returned anyway.
  
  Revision  Changes    Path
  1.41      +16 -3     jakarta-ant/docs/index.html
  
  Index: index.html
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/docs/index.html,v
  retrieving revision 1.40
  retrieving revision 1.41
  diff -u -r1.40 -r1.41
  --- index.html	2000/07/08 12:33:25	1.40
  +++ index.html	2000/07/11 11:14:45	1.41
  @@ -3143,11 +3143,24 @@
   <p>It is very easy to write your own task:</p>
   <ol>
     <li>Create a Java class that extends <code>org.apache.tools.ant.Task</code>.</li>
  -  <li>For each attribute, write a setter method. The setter method must be a <code>public
  -    void</code> method that takes one <code>String</code> as an argument. The
  +  <li>For each attribute, write a setter method. The setter method must be a 
  +    <code>public void</code> method that takes a single argument. The
       name of the method must begin with &quot;set&quot;, followed by the
       attribute name, with the first character in uppercase, and the rest in
  -    lowercase.</li>
  +    lowercase. The type of the attribute can be <code>String</code>, any 
  +    primitive type, <code>Class</code>, <code>File</code> (in which case the 
  +    value of the attribute is interpreted relative to the project's basedir) 
  +    or any other type that has a constructor with a single <code>String</code>
  +    argument</li>
  +  <li>If the task should support character data, write a <code>public void 
  +    addText(String)</code> method.</li>
  +  <li>For each nested element, write a create or add method. A create method 
  +    must be a <code>public</code> method that takes no arguments and returns 
  +    an Object type. The name of the create method must begin with 
  +    &quot;create&quot;, followed by the element name. An add method must be 
  +    a <code>public void</code> method that takes a single argument of an 
  +    Object type with a no argument constructor. The name of the add method 
  +    must begin with &quot;add&quot;, followed by the element name.
     <li>Write a <code>public void execute</code> method, with no arguments, that
       throws a <code>BuildException</code>. This method implements the task
       itself.</li>
  
  
  
  1.6       +7 -0      jakarta-ant/src/main/org/apache/tools/ant/BuildException.java
  
  Index: BuildException.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/BuildException.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- BuildException.java	2000/06/18 02:22:23	1.5
  +++ BuildException.java	2000/07/11 11:14:46	1.6
  @@ -152,4 +152,11 @@
       public void setLocation(Location location) {
           this.location = location;
       }
  +
  +    /**
  +     * Returns the file location where the error occured.
  +     */
  +    public Location getLocation() {
  +        return location;
  +    }
   }
  
  
  
  1.18      +54 -79    jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java
  
  Index: ProjectHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- ProjectHelper.java	2000/07/10 11:04:17	1.17
  +++ ProjectHelper.java	2000/07/11 11:14:46	1.18
  @@ -54,9 +54,7 @@
   
   package org.apache.tools.ant;
   
  -import java.beans.*;
   import java.io.*;
  -import java.lang.reflect.*;
   import java.util.*;
   import org.xml.sax.*;
   import org.w3c.dom.*;
  @@ -109,10 +107,24 @@
           catch(SAXParseException exc) {
               Location location =
                   new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
  -            throw new BuildException(exc.getMessage(), exc.getException(), location);
  +
  +            Throwable t = exc.getException();
  +            if (t instanceof BuildException) {
  +                BuildException be = (BuildException) t;
  +                if (be.getLocation() == Location.UNKNOWN_LOCATION) {
  +                    be.setLocation(location);
  +                }
  +                throw be;
  +            }
  +            
  +            throw new BuildException(exc.getMessage(), t, location);
           }
           catch(SAXException exc) {
  -            throw new BuildException(exc.getMessage(), exc.getException());
  +            Throwable t = exc.getException();
  +            if (t instanceof BuildException) {
  +                throw (BuildException) t;
  +            }
  +            throw new BuildException(exc.getMessage(), t);
           }
           catch(FileNotFoundException exc) {
               throw new BuildException("File \"" + buildFile.toString() + "\" not found");
  @@ -342,15 +354,13 @@
               String text = new String(buf, start, end).trim();
               if (text.length() == 0) return;
   
  +            IntrospectionHelper ih = 
  +                IntrospectionHelper.getHelper(task.getClass());
  +
               try {
  -                Method addProp = task.getClass().getMethod("addText", new Class[]{String.class});
  -                Object child = addProp.invoke(task, new Object[] {text});
  -            } catch(NoSuchMethodException exc) {
  -                throw new SAXParseException(task.getClass() + " does not support nested text elements", locator);
  -            } catch(InvocationTargetException exc) {
  -                throw new SAXParseException("Error invoking \"addText\" method", locator, exc);
  -            } catch(IllegalAccessException exc) {
  -                throw new SAXParseException("Unable to access \"addText\" method", locator, exc);
  +                ih.addText(task, text);
  +            } catch (BuildException exc) {
  +                throw new SAXParseException(exc.getMessage(), locator, exc);
               }
           }
   
  @@ -376,19 +386,28 @@
   
           public void init(String propType, AttributeList attrs) throws SAXParseException {
               Class targetClass = target.getClass();
  -
  -            String methodName = "create" + Character.toUpperCase(propType.charAt(0)) + propType.substring(1);
  +            IntrospectionHelper ih = 
  +                IntrospectionHelper.getHelper(targetClass);
   
               try {
  -                Method addProp = targetClass.getMethod(methodName, new Class[]{});
  -                child = addProp.invoke(target, new Object[] {});
  +                child = ih.createElement(target, propType.toLowerCase());
                   configure(child, attrs);
  -            } catch(NoSuchMethodException exc) {
  -                throw new SAXParseException(targetClass + " does not support nested " + propType + " properties", locator);
  -            } catch(InvocationTargetException exc) {
  -                throw new SAXParseException(exc.getMessage(), locator);
  -            } catch(IllegalAccessException exc) {
  -                throw new SAXParseException(exc.getMessage(), locator);
  +            } catch (BuildException exc) {
  +                throw new SAXParseException(exc.getMessage(), locator, exc);
  +            }
  +        }
  +
  +        public void characters(char[] buf, int start, int end) throws SAXParseException {
  +            String text = new String(buf, start, end).trim();
  +            if (text.length() == 0) return;
  +
  +            IntrospectionHelper ih = 
  +                IntrospectionHelper.getHelper(child.getClass());
  +
  +            try {
  +                ih.addText(child, text);
  +            } catch (BuildException exc) {
  +                throw new SAXParseException(exc.getMessage(), locator, exc);
               }
           }
   
  @@ -401,70 +420,26 @@
           if( target instanceof TaskAdapter )
               target=((TaskAdapter)target).getProxy();
   
  -        // XXX
  -        // instead of doing this introspection each time around, I
  -        // should have a helper class to keep this info around for
  -        // each kind of class
  -
  -        Hashtable propertySetters = new Hashtable();
  -        BeanInfo beanInfo;
  -        try {
  -            beanInfo = Introspector.getBeanInfo(target.getClass());
  -        } catch (IntrospectionException ie) {
  -            String msg = "Can't introspect class: " + target.getClass();
  -            throw new BuildException(msg);
  -        }
  -
  -        PropertyDescriptor[] pda = beanInfo.getPropertyDescriptors();
  -        for (int i = 0; i < pda.length; i++) {
  -            PropertyDescriptor pd = pda[i];
  -            String property = pd.getName();
  -            Method setMethod = pd.getWriteMethod();
  -            if (setMethod != null) {
  -
  -                // make sure that there's only 1 param and that it
  -                // takes a String object, all other setMethods need
  -                // to get screened out
  -
  -                Class[] ma =setMethod.getParameterTypes();
  -                if (ma.length == 1) {
  -                    Class c = ma[0];
  -                    if (c.getName().equals("java.lang.String")) {
  -                        propertySetters.put(property, setMethod);
  -                    }
  -                }
  -            }
  -        }
  +        IntrospectionHelper ih = 
  +            IntrospectionHelper.getHelper(target.getClass());
   
           for (int i = 0; i < attrs.getLength(); i++) {
               // reflect these into the target
  +            String value=replaceProperties(attrs.getValue(i), 
  +                                           project.getProperties() );
  +            try {
  +                ih.setAttribute(project, target, 
  +                                attrs.getName(i).toLowerCase(), value);
   
  -            Method setMethod = (Method)propertySetters.get(attrs.getName(i));
  -            if (setMethod == null) {
  +            } catch (BuildException be) {
                   if (attrs.getName(i).equals("id")) {
                       project.addReference(attrs.getValue(i), target);
  -                    continue;
  +                } else {
  +                    be.setLocation(new Location(buildFile.toString(), 
  +                                                locator.getLineNumber(), 
  +                                                locator.getColumnNumber()));
  +                    throw be;
                   }
  -
  -                String msg = "Class " + target.getClass() +
  -                    " doesn't support the \"" + attrs.getName(i) + "\" property";
  -                throw new BuildException(msg);
  -            }
  -
  -            String value=replaceProperties(attrs.getValue(i), project.getProperties() );
  -            try {
  -                setMethod.invoke(target, new String[] {value});
  -            } catch (IllegalAccessException iae) {
  -                String msg = "Error setting value for attrib: " +
  -                    attrs.getName(i);
  -                iae.printStackTrace();
  -                throw new BuildException(msg);
  -            } catch (InvocationTargetException ie) {
  -                String msg = "Error setting value for attrib: " +
  -                    attrs.getName(i) + " in " + target.getClass().getName();
  -                ie.printStackTrace();
  -                ie.getTargetException().printStackTrace();
  -                throw new BuildException(msg);
               }
           }
       }
  
  
  
  1.1                  jakarta-ant/src/main/org/apache/tools/ant/IntrospectionHelper.java
  
  Index: IntrospectionHelper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.tools.ant;
  
  import java.lang.reflect.*;
  import java.io.File;
  import java.util.*;
  
  /**
   * Helper class that collects the methods a task or nested element
   * holds to set attributes, create nested elements or hold PCDATA
   * elements.
   *
   * @author Stefan Bodewig <a href="mailto:stefan.bodewig@megabit.net">stefan.bodewig@megabit.net</a> 
   */
  public class IntrospectionHelper  {
  
      /**
       * holds the types of the attributes that could be set.
       */
      private Hashtable attributeTypes;
  
      /**
       * holds the attribute setter methods.
       */
      private Hashtable attributeSetters;
  
      /**
       * Holds the types of nested elements that could be created.
       */
      private Hashtable nestedTypes;
  
      /**
       * Holds methods to create nested elements.
       */
      private Hashtable nestedCreators;
  
      /**
       * The method to add PCDATA stuff.
       */
      private Method addText = null;
  
      /**
       * The Class that's been introspected.
       */
      private Class bean;
  
      /**
       * instances we've already created
       */
      private static Hashtable helpers = new Hashtable();
  
      private IntrospectionHelper(final Class bean) {
          attributeTypes = new Hashtable();
          attributeSetters = new Hashtable();
          nestedTypes = new Hashtable();
          nestedCreators = new Hashtable();
          this.bean = bean;
  
          Method[] methods = bean.getMethods();
          for (int i=0; i<methods.length; i++) {
              final Method m = methods[i];
              final String name = m.getName();
              Class returnType = m.getReturnType();
              Class[] args = m.getParameterTypes();
  
              if ("setLocation".equals(name) || "setDescription".equals(name)) {
                  continue;
              }
              
              if ("addText".equals(name)
                  && java.lang.Void.TYPE.equals(returnType)
                  && args.length == 1
                  && java.lang.String.class.equals(args[0])) {
  
                  addText = methods[i];
  
              } else if (name.startsWith("set")
                         && java.lang.Void.TYPE.equals(returnType)
                         && args.length == 1
                         && !args[0].isArray()) {
  
                  String propName = getPropertyName(name, "set");
                  AttributeSetter as = createAttributeSetter(m, args[0]);
                  if (as != null) {
                      attributeTypes.put(propName, args[0]);
                      attributeSetters.put(propName, as);
                  }
  
              } else if (name.startsWith("create")
                         && !returnType.isArray()
                         && !returnType.isPrimitive()
                         && args.length == 0) {
  
                  String propName = getPropertyName(name, "create");
                  nestedTypes.put(propName, returnType);
                  nestedCreators.put(propName, new NestedCreator() {
  
                          public Object create(Object parent) 
                              throws InvocationTargetException, 
                              IllegalAccessException {
  
                              return m.invoke(parent, new Object[] {});
                          }
  
                      });
                  
              } else if (name.startsWith("add")
                         && java.lang.Void.TYPE.equals(returnType)
                         && args.length == 1
                         && !args[0].isArray()
                         && !args[0].isPrimitive()) {
                   
                  try {
                      final Constructor c = 
                          args[0].getConstructor(new Class[] {});
                      String propName = getPropertyName(name, "add");
                      nestedTypes.put(propName, args[0]);
                      nestedCreators.put(propName, new NestedCreator() {
  
                              public Object create(Object parent) 
                                  throws InvocationTargetException, IllegalAccessException, InstantiationException {
                                  
                                  Object o = c.newInstance(new Object[] {});
                                  m.invoke(parent, new Object[] {o});
                                  return o;
                              }
  
                          });
                  } catch (NoSuchMethodException nse) {
                  }
                      
              }
          }
      }
      
      /**
       * Factory method for helper objects.
       */
      public synchronized static IntrospectionHelper getHelper(Class c) {
          IntrospectionHelper ih = (IntrospectionHelper) helpers.get(c);
          if (ih == null) {
              ih = new IntrospectionHelper(c);
              helpers.put(c, ih);
          }
          return ih;
      }
  
      /**
       * Sets the named attribute.
       */
      public void setAttribute(Project p, Object element, String attributeName, 
                               String value)
          throws BuildException {
          AttributeSetter as = (AttributeSetter) attributeSetters.get(attributeName);
          if (as == null) {
              String msg = "Class " + element.getClass() +
                  " doesn't support the \"" + attributeName + "\" attribute";
              throw new BuildException(msg);
          }
          try {
              as.set(p, element, value);
          } catch (IllegalAccessException ie) {
              // impossible as getMethods should only return public methods
              throw new BuildException(ie);
          } catch (InvocationTargetException ite) {
              Throwable t = ite.getTargetException();
              if (t instanceof BuildException) {
                  throw (BuildException) t;
              }
              throw new BuildException(t);
          }
      }
  
      /**
       * Adds PCDATA areas.
       */
      public void addText(Object element, String text) {
          if (addText == null) {
              String msg = "Class " + element.getClass() +
                  " doesn't support nested text elements";
              throw new BuildException(msg);
          }
          try {
              addText.invoke(element, new String[] {text});
          } catch (IllegalAccessException ie) {
              // impossible as getMethods should only return public methods
              throw new BuildException(ie);
          } catch (InvocationTargetException ite) {
              Throwable t = ite.getTargetException();
              if (t instanceof BuildException) {
                  throw (BuildException) t;
              }
              throw new BuildException(t);
          }
      }
  
      /**
       * Creates a named nested element.
       */
      public Object createElement(Object element, String elementName) 
          throws BuildException {
          NestedCreator nc = (NestedCreator) nestedCreators.get(elementName);
          if (nc == null) {
              String msg = "Class " + element.getClass() +
                  " doesn't support the nested \"" + elementName + "\" element";
              throw new BuildException(msg);
          }
          try {
              return nc.create(element);
          } catch (IllegalAccessException ie) {
              // impossible as getMethods should only return public methods
              throw new BuildException(ie);
          } catch (InstantiationException ine) {
              // impossible as getMethods should only return public methods
              throw new BuildException(ine);
          } catch (InvocationTargetException ite) {
              Throwable t = ite.getTargetException();
              if (t instanceof BuildException) {
                  throw (BuildException) t;
              }
              throw new BuildException(t);
          }
      }
  
      /**
       * returns the type of a named nested element.
       */
      public Class getElementType(String elementName) 
          throws BuildException {
          Class nt = (Class) nestedTypes.get(elementName);
          if (nt == null) {
              String msg = "Class " + bean +
                  " doesn't support the nested \"" + elementName + "\" element";
              throw new BuildException(msg);
          }
          return nt;
      }
  
      /**
       * returns the type of a named attribute.
       */
      public Class getAttributeType(String attributeName) 
          throws BuildException {
          Class at = (Class) attributeTypes.get(attributeName);
          if (at == null) {
              String msg = "Class " + bean +
                  " doesn't support the \"" + attributeName + "\" attribute";
              throw new BuildException(msg);
          }
          return at;
      }
  
      /**
       * Does the introspected class support PCDATA?
       */
      public boolean supportsCharacters() {
          return addText != null;
      }
  
      /**
       * Return all attribues supported by the introspected class.
       */
      public Enumeration getAttributes() {
          return attributeSetters.keys();
      }
  
      /**
       * Return all nested elements supported by the introspected class.
       */
      public Enumeration getNestedElements() {
          return nestedTypes.keys();
      }
  
      /**
       * Create a proper implementation of AttributeSetter for the given
       * attribute type.  
       */
      private AttributeSetter createAttributeSetter(final Method m,
                                                    final Class arg) {
  
          // simplest case - setAttribute expects String
          if (java.lang.String.class.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new String[] {value});
                      }
                  };
  
          // now for the primitive types, use their wrappers
          } else if (java.lang.Character.class.equals(arg)
                     || java.lang.Character.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Character[] {new Character(value.charAt(0))});
                      }
  
                  };
          } else if (java.lang.Byte.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Byte[] {new Byte(value)});
                      }
  
                  };
          } else if (java.lang.Short.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Short[] {new Short(value)});
                      }
  
                  };
          } else if (java.lang.Integer.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Integer[] {new Integer(value)});
                      }
  
                  };
          } else if (java.lang.Long.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Long[] {new Long(value)});
                      }
  
                  };
          } else if (java.lang.Float.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Float[] {new Float(value)});
                      }
  
                  };
          } else if (java.lang.Double.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new Double[] {new Double(value)});
                      }
  
                  };
  
          // boolean gets an extra treatment, because we have a nice method 
          // in Project
          } else if (java.lang.Boolean.class.equals(arg) 
                     || java.lang.Boolean.TYPE.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, 
                                   new Boolean[] {new Boolean(Project.toBoolean(value))});
                      }
  
                  };
  
          // Class doesn't have a String constructor but a decent factory method
          } else if (java.lang.Class.class.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException, BuildException {
                          try {
                              m.invoke(parent, new Class[] {Class.forName(value)});
                          } catch (ClassNotFoundException ce) {
                              throw new BuildException(ce);
                          }
                      }
                  };
  
          // resolve relative paths through Project
          } else if (java.io.File.class.equals(arg)) {
              return new AttributeSetter() {
                      public void set(Project p, Object parent, String value) 
                          throws InvocationTargetException, IllegalAccessException {
                          m.invoke(parent, new File[] {p.resolveFile(value)});
                      }
  
                  };
  
          // worst case. look for a public String constructor and use it
          } else {
  
              try {
                  final Constructor c = 
                      arg.getConstructor(new Class[] {java.lang.String.class});
  
                  return new AttributeSetter() {
                          public void set(Project p, Object parent, 
                                          String value) 
                              throws InvocationTargetException, IllegalAccessException, BuildException {
                              try {
                                  m.invoke(parent, new Object[] {c.newInstance(new String[] {value})});
                              } catch (InstantiationException ie) {
                                  throw new BuildException(ie);
                              }
                          }
                      };
                  
              } catch (NoSuchMethodException nme) {
              }
          }
          
          return null;
      }
  
      /**
       * extract the name of a property from a method name - subtracting
       * a given prefix.  
       */
      private String getPropertyName(String methodName, String prefix) {
          int start = prefix.length();
          return methodName.substring(start).toLowerCase();
      }
  
      private interface NestedCreator {
          public Object create(Object parent) 
              throws InvocationTargetException, IllegalAccessException, InstantiationException;
      }
      private interface AttributeSetter {
          public void set(Project p, Object parent, String value)
              throws InvocationTargetException, IllegalAccessException, 
                     BuildException;
      }
  }
  
  
  
  1.9       +1 -1      jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Ant.java
  
  Index: Ant.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- Ant.java	2000/07/06 16:48:13	1.8
  +++ Ant.java	2000/07/11 11:14:48	1.9
  @@ -162,7 +162,7 @@
       }
   
       // XXX replace with createProperty!!
  -    public Task createProperty() {
  +    public Property createProperty() {
   	Property p=(Property)p1.createTask("property");
   	p.setUserProperty(true);
   	properties.addElement( p );
  
  
  
  1.2       +28 -15    jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Patch.java
  
  Index: Patch.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Patch.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Patch.java	2000/06/23 14:38:19	1.1
  +++ Patch.java	2000/07/11 11:14:48	1.2
  @@ -75,29 +75,29 @@
       /**
        * The file to patch.
        */
  -    public void setOriginalfile(String file) {
  -        originalFile = project.resolveFile(file);
  +    public void setOriginalfile(File file) {
  +        originalFile = file;
       }
   
       /**
        * The file containing the diff output.
        */
  -    public void setPatchfile(String file) {
  -        patchFile = project.resolveFile(file);
  +    public void setPatchfile(File file) {
  +        patchFile = file;
       }
   
       /**
        * Shall patch write backups.
        */
  -    public void setBackups(String backups) {
  -        backup = Project.toBoolean(backups);
  +    public void setBackups(boolean backups) {
  +        backup = backups;
       }
   
       /**
        * Ignore whitespace differences.
        */
  -    public void setIgnorewhitespace(String ignore) {
  -        ignoreWhitespace = Project.toBoolean(ignore);
  +    public void setIgnorewhitespace(boolean ignore) {
  +        ignoreWhitespace = ignore;
       }
   
       /**
  @@ -106,28 +106,41 @@
        *
        * <p>patch's <i>-p</i> option.
        */
  -    public void setStrip(String num) {
  -        strip = Integer.parseInt(num);
  +    public void setStrip(int num) throws BuildException {
  +        if (strip < 0) {
  +            throw new BuildException("strip has to be >= 0", location);
  +        }
  +        strip = num;
       }
   
       /**
        * Work silently unless an error occurs.
        */
  -    public void setQuiet(String q) {
  -        quiet = Project.toBoolean(q);
  +    public void setQuiet(boolean q) {
  +        quiet = q;
       }
   
       /**
        * Assume patch was created with old and new files swapped.
        */
  -    public void setReverse(String r) {
  -        reverse = Project.toBoolean(r);
  +    public void setReverse(boolean r) {
  +        reverse = r;
       }
   
  +    public final void setCommand(String command) throws BuildException {
  +        throw new BuildException("Cannot set attribute command in patch task",
  +                                 location);
  +    }
  +
       public void execute() throws BuildException {
           if (patchFile == null) {
  -            throw new BuildException("patchfile argument is required");
  +            throw new BuildException("patchfile argument is required", 
  +                                     location);
           } 
  +        if (!patchFile.exists()) {
  +            throw new BuildException("patchfile "+patchFile+" doesn\'t exist", 
  +                                     location);
  +        }
           
           StringBuffer command = new StringBuffer("patch -i "+patchFile+" ");