Submitted by: Jose Alberto Fernandez <j_a_fernandez@yahoo.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271407 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -0,0 +1,2 @@ | |||||
| build | |||||
| dist | |||||
| @@ -6,6 +6,7 @@ | |||||
| <property name='build' location='build' /> | <property name='build' location='build' /> | ||||
| <property name='dist' location='dist' /> | <property name='dist' location='dist' /> | ||||
| <property name='classes' location='${build}/classes' /> | <property name='classes' location='${build}/classes' /> | ||||
| <property name='testcases' location='src/testcases' /> | |||||
| <property name="debug" value="true" /> | <property name="debug" value="true" /> | ||||
| <property name="deprecation" value="false" /> | <property name="deprecation" value="false" /> | ||||
| @@ -19,6 +20,8 @@ | |||||
| <fileset dir='${orig-classes}'> | <fileset dir='${orig-classes}'> | ||||
| <include name='**' /> | <include name='**' /> | ||||
| <exclude name='org/apache/tools/ant/Project.class' /> | <exclude name='org/apache/tools/ant/Project.class' /> | ||||
| <exclude name='org/apache/tools/ant/ProjectHelper.class' /> | |||||
| <exclude name='org/apache/tools/ant/IntrospectionHelper.class' /> | |||||
| <exclude name='org/apache/tools/ant/TaskAdapter.class' /> | <exclude name='org/apache/tools/ant/TaskAdapter.class' /> | ||||
| <exclude name='org/apache/tools/ant/taskdefs/Ant.class' /> | <exclude name='org/apache/tools/ant/taskdefs/Ant.class' /> | ||||
| </fileset> | </fileset> | ||||
| @@ -54,6 +57,12 @@ | |||||
| <delete dir='${build}' /> | <delete dir='${build}' /> | ||||
| </target> | </target> | ||||
| <target name='test'> | |||||
| <ant dir='${testcases}' inheritAll='false'/> | |||||
| <ant dir='${testcases}' | |||||
| antfile='${testcases}/case.xml' inheritAll='false'/> | |||||
| </target> | |||||
| <target name='cleanall' depends='clean'> | <target name='cleanall' depends='clean'> | ||||
| <delete dir='${dist}' /> | <delete dir='${dist}' /> | ||||
| </target> | </target> | ||||
| @@ -0,0 +1,706 @@ | |||||
| /* | |||||
| * The Apache Software License, Version 1.1 | |||||
| * | |||||
| * Copyright (c) 2000-2001 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", "Ant", 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 org.apache.tools.ant.types.Path; | |||||
| import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
| import java.lang.reflect.Method; | |||||
| import java.lang.reflect.InvocationTargetException; | |||||
| import java.lang.reflect.Constructor; | |||||
| import java.io.File; | |||||
| import java.util.Enumeration; | |||||
| import java.util.Hashtable; | |||||
| import java.util.Locale; | |||||
| /** | |||||
| * Helper class that collects the methods a task or nested element | |||||
| * holds to set attributes, create nested elements or hold PCDATA | |||||
| * elements. | |||||
| * | |||||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
| */ | |||||
| public class IntrospectionHelper implements BuildListener { | |||||
| /** | |||||
| * 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; | |||||
| /** | |||||
| * Holds methods to store configured nested elements. | |||||
| */ | |||||
| private Hashtable nestedStorers; | |||||
| /** | |||||
| * 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(); | |||||
| nestedStorers = 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(); | |||||
| // not really user settable properties on tasks | |||||
| if (org.apache.tools.ant.Task.class.isAssignableFrom(bean) | |||||
| && args.length == 1 && | |||||
| ( | |||||
| ( | |||||
| "setLocation".equals(name) && org.apache.tools.ant.Location.class.equals(args[0]) | |||||
| ) || ( | |||||
| "setTaskType".equals(name) && java.lang.String.class.equals(args[0]) | |||||
| ) | |||||
| )) { | |||||
| continue; | |||||
| } | |||||
| // hide addTask for TaskContainers | |||||
| // if (org.apache.tools.ant.TaskContainer.class.isAssignableFrom(bean) | |||||
| // && args.length == 1 && "addTask".equals(name) | |||||
| // && org.apache.tools.ant.Task.class.equals(args[0])) { | |||||
| // 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"); | |||||
| if (attributeSetters.get(propName) != null) { | |||||
| if (java.lang.String.class.equals(args[0])) { | |||||
| /* | |||||
| Ignore method m, as there is an overloaded | |||||
| form of this method that takes in a | |||||
| non-string argument, which gains higher | |||||
| priority. | |||||
| */ | |||||
| continue; | |||||
| } | |||||
| /* | |||||
| If the argument is not a String, and if there | |||||
| is an overloaded form of this method already defined, | |||||
| we just override that with the new one. | |||||
| This mechanism does not guarantee any specific order | |||||
| in which the methods will be selected: so any code | |||||
| that depends on the order in which "set" methods have | |||||
| been defined, is not guaranteed to be selected in any | |||||
| particular order. | |||||
| */ | |||||
| } | |||||
| 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[] {}); | |||||
| } | |||||
| }); | |||||
| nestedStorers.remove(propName); | |||||
| } else if (name.startsWith("addConfigured") | |||||
| && java.lang.Void.TYPE.equals(returnType) | |||||
| && args.length == 1 | |||||
| && !java.lang.String.class.equals(args[0]) | |||||
| && !args[0].isArray() | |||||
| && !args[0].isPrimitive()) { | |||||
| try { | |||||
| final Constructor c = | |||||
| args[0].getConstructor(new Class[] {}); | |||||
| String propName = getPropertyName(name, "addConfigured"); | |||||
| 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[] {}); | |||||
| return o; | |||||
| } | |||||
| }); | |||||
| nestedStorers.put(propName, new NestedStorer() { | |||||
| public void store(Object parent, Object child) | |||||
| throws InvocationTargetException, | |||||
| IllegalAccessException, | |||||
| InstantiationException { | |||||
| m.invoke(parent, new Object[] {child}); | |||||
| } | |||||
| }); | |||||
| } catch (NoSuchMethodException nse) { | |||||
| } | |||||
| } else if (name.startsWith("add") | |||||
| && java.lang.Void.TYPE.equals(returnType) | |||||
| && args.length == 1 | |||||
| && !java.lang.String.class.equals(args[0]) | |||||
| && !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; | |||||
| } | |||||
| }); | |||||
| nestedStorers.remove(name); | |||||
| } catch (NoSuchMethodException nse) { | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Factory method for helper objects. | |||||
| */ | |||||
| public static synchronized 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 = getElementName(p, element) + | |||||
| //String msg = "Class " + element.getClass().getName() + | |||||
| " 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(Project project, Object element, String text) { | |||||
| if (addText == null) { | |||||
| // Element doesn't handle text content | |||||
| if ( text.trim().length() == 0 ) { | |||||
| // Only whitespace - ignore | |||||
| return; | |||||
| } | |||||
| else { | |||||
| // Not whitespace - fail | |||||
| String msg = getElementName(project, element) + | |||||
| " doesn't support nested text data."; | |||||
| 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(Project project, Object element, String elementName) | |||||
| throws BuildException { | |||||
| try { | |||||
| // First check if there are any roles supported by this class | |||||
| Object nestedElement = project.createInRole(element, elementName); | |||||
| if (nestedElement == null) { | |||||
| NestedCreator nc = | |||||
| (NestedCreator) nestedCreators.get(elementName); | |||||
| if (nc == null) { | |||||
| String msg = getElementName(project, element) + | |||||
| " doesn't support the nested \"" + elementName + | |||||
| "\" element."; | |||||
| throw new BuildException(msg); | |||||
| } | |||||
| nestedElement = nc.create(element); | |||||
| } | |||||
| if (nestedElement instanceof ProjectComponent) { | |||||
| ((ProjectComponent) nestedElement).setProject(project); | |||||
| } | |||||
| return nestedElement; | |||||
| } 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); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Creates a named nested element. | |||||
| */ | |||||
| public void storeElement(Project project, Object element, Object child, String elementName) | |||||
| throws BuildException { | |||||
| if (elementName == null) { | |||||
| return; | |||||
| } | |||||
| NestedStorer ns = (NestedStorer)nestedStorers.get(elementName); | |||||
| if (ns == null) { | |||||
| return; | |||||
| } | |||||
| try { | |||||
| ns.store(element, child); | |||||
| } 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.getName() + | |||||
| " 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.getName() + | |||||
| " 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)}); | |||||
| } | |||||
| }; | |||||
| // resolve relative paths through Project | |||||
| } else if (org.apache.tools.ant.types.Path.class.equals(arg)) { | |||||
| return new AttributeSetter() { | |||||
| public void set(Project p, Object parent, String value) | |||||
| throws InvocationTargetException, IllegalAccessException { | |||||
| m.invoke(parent, new Path[] {new Path(p, value)}); | |||||
| } | |||||
| }; | |||||
| // EnumeratedAttributes have their own helper class | |||||
| } else if (org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom(arg)) { | |||||
| return new AttributeSetter() { | |||||
| public void set(Project p, Object parent, String value) | |||||
| throws InvocationTargetException, IllegalAccessException, BuildException { | |||||
| try { | |||||
| org.apache.tools.ant.types.EnumeratedAttribute ea = (org.apache.tools.ant.types.EnumeratedAttribute)arg.newInstance(); | |||||
| ea.setValue(value); | |||||
| m.invoke(parent, new EnumeratedAttribute[] {ea}); | |||||
| } catch (InstantiationException ie) { | |||||
| throw new BuildException(ie); | |||||
| } | |||||
| } | |||||
| }; | |||||
| // 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 { | |||||
| Object attribute = c.newInstance(new String[] {value}); | |||||
| if (attribute instanceof ProjectComponent) { | |||||
| ((ProjectComponent) attribute).setProject(p); | |||||
| } | |||||
| m.invoke(parent, new Object[] {attribute}); | |||||
| } catch (InstantiationException ie) { | |||||
| throw new BuildException(ie); | |||||
| } | |||||
| } | |||||
| }; | |||||
| } catch (NoSuchMethodException nme) { | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| protected String getElementName(Project project, Object element) | |||||
| { | |||||
| Hashtable elements = project.getTaskDefinitions(); | |||||
| String typeName = "task"; | |||||
| if (!elements.contains( element.getClass() )) | |||||
| { | |||||
| elements = project.getDataTypeDefinitions(); | |||||
| typeName = "data type"; | |||||
| if (!elements.contains( element.getClass() )) | |||||
| { | |||||
| elements = null; | |||||
| } | |||||
| } | |||||
| if (elements != null) | |||||
| { | |||||
| Enumeration e = elements.keys(); | |||||
| while (e.hasMoreElements()) | |||||
| { | |||||
| String elementName = (String) e.nextElement(); | |||||
| Class elementClass = (Class) elements.get( elementName ); | |||||
| if ( element.getClass().equals( elementClass ) ) | |||||
| { | |||||
| return "The <" + elementName + "> " + typeName; | |||||
| } | |||||
| } | |||||
| } | |||||
| return "Class " + element.getClass().getName(); | |||||
| } | |||||
| /** | |||||
| * 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(Locale.US); | |||||
| } | |||||
| private interface NestedCreator { | |||||
| Object create(Object parent) | |||||
| throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
| } | |||||
| private interface NestedStorer { | |||||
| void store(Object parent, Object child) | |||||
| throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
| } | |||||
| private interface AttributeSetter { | |||||
| void set(Project p, Object parent, String value) | |||||
| throws InvocationTargetException, IllegalAccessException, | |||||
| BuildException; | |||||
| } | |||||
| public void buildStarted(BuildEvent event) {} | |||||
| public void buildFinished(BuildEvent event) { | |||||
| attributeTypes.clear(); | |||||
| attributeSetters.clear(); | |||||
| nestedTypes.clear(); | |||||
| nestedCreators.clear(); | |||||
| addText = null; | |||||
| helpers.clear(); | |||||
| } | |||||
| public void targetStarted(BuildEvent event) {} | |||||
| public void targetFinished(BuildEvent event) {} | |||||
| public void taskStarted(BuildEvent event) {} | |||||
| public void taskFinished(BuildEvent event) {} | |||||
| public void messageLogged(BuildEvent event) {} | |||||
| } | |||||
| @@ -63,12 +63,15 @@ import java.util.Properties; | |||||
| import java.util.Enumeration; | import java.util.Enumeration; | ||||
| import java.util.Stack; | import java.util.Stack; | ||||
| import java.lang.reflect.Modifier; | import java.lang.reflect.Modifier; | ||||
| import java.lang.reflect.Method; | |||||
| import java.lang.reflect.InvocationTargetException; | |||||
| import org.apache.tools.ant.types.DataTypeAdapterTask; | import org.apache.tools.ant.types.DataTypeAdapterTask; | ||||
| import org.apache.tools.ant.types.FilterSet; | import org.apache.tools.ant.types.FilterSet; | ||||
| import org.apache.tools.ant.types.FilterSetCollection; | import org.apache.tools.ant.types.FilterSetCollection; | ||||
| import org.apache.tools.ant.util.FileUtils; | import org.apache.tools.ant.util.FileUtils; | ||||
| import org.apache.tools.ant.types.Path; | |||||
| /** | /** | ||||
| * Central representation of an Ant project. This class defines a | * Central representation of an Ant project. This class defines a | ||||
| @@ -90,6 +93,9 @@ public class Project { | |||||
| public final static int MSG_VERBOSE = 3; | public final static int MSG_VERBOSE = 3; | ||||
| public final static int MSG_DEBUG = 4; | public final static int MSG_DEBUG = 4; | ||||
| public final static String TASK_ROLE = "task"; | |||||
| public final static String DATATYPE_ROLE = "datatype"; | |||||
| // private set of constants to represent the state | // private set of constants to represent the state | ||||
| // of a DFS of the Target dependencies | // of a DFS of the Target dependencies | ||||
| private final static String VISITING = "VISITING"; | private final static String VISITING = "VISITING"; | ||||
| @@ -165,30 +171,26 @@ public class Project { | |||||
| fileUtils = FileUtils.newFileUtils(); | fileUtils = FileUtils.newFileUtils(); | ||||
| symbols = new SymbolTable(); | symbols = new SymbolTable(); | ||||
| symbols.setProject(this); | symbols.setProject(this); | ||||
| loadDefinitions(); | |||||
| } | } | ||||
| /** | /** | ||||
| * create a new ant project that inherits from caler project | * create a new ant project that inherits from caler project | ||||
| * @param p the calling project | * @param p the calling project | ||||
| */ | */ | ||||
| public Project(Project p) { | |||||
| private Project(Project p) { | |||||
| fileUtils = FileUtils.newFileUtils(); | fileUtils = FileUtils.newFileUtils(); | ||||
| symbols = new SymbolTable(p); | |||||
| symbols = new SymbolTable(p.getSymbols()); | |||||
| symbols.setProject(this); | symbols.setProject(this); | ||||
| } | } | ||||
| /** | /** | ||||
| * Initialise the project. | |||||
| * | |||||
| * This involves setting the default task definitions and loading the | |||||
| * system properties. | |||||
| * Loads the core definitions into the Root project. | |||||
| */ | */ | ||||
| public void init() throws BuildException { | |||||
| setJavaVersionProperty(); | |||||
| // Initialize simbol table just in case | |||||
| symbols.addRole("task", TaskContainer.class, TaskAdapter.class); | |||||
| symbols.addRole("datatype", TaskContainer.class, | |||||
| private void loadDefinitions() { | |||||
| // Initialize symbol table just in case | |||||
| symbols.addRole(TASK_ROLE, TaskContainer.class, TaskAdapter.class); | |||||
| symbols.addRole(DATATYPE_ROLE, TaskContainer.class, | |||||
| DataTypeAdapterTask.class); | DataTypeAdapterTask.class); | ||||
| String defs = "/org/apache/tools/ant/taskdefs/defaults.properties"; | String defs = "/org/apache/tools/ant/taskdefs/defaults.properties"; | ||||
| @@ -248,7 +250,23 @@ public class Project { | |||||
| } catch (IOException ioe) { | } catch (IOException ioe) { | ||||
| throw new BuildException("Can't load default datatype list"); | throw new BuildException("Can't load default datatype list"); | ||||
| } | } | ||||
| } | |||||
| /** | |||||
| * Creates a subproject of the current project. | |||||
| */ | |||||
| public Project createSubProject() { | |||||
| return new Project(this); | |||||
| } | |||||
| /** | |||||
| * Initialise the project. | |||||
| * | |||||
| * This involves setting the default task definitions and loading the | |||||
| * system properties. | |||||
| */ | |||||
| public void init() throws BuildException { | |||||
| setJavaVersionProperty(); | |||||
| setSystemProperties(); | setSystemProperties(); | ||||
| } | } | ||||
| @@ -275,7 +293,7 @@ public class Project { | |||||
| /** | /** | ||||
| * Get the symbols associated with this project. | * Get the symbols associated with this project. | ||||
| */ | */ | ||||
| public SymbolTable getSymbols() { | |||||
| private SymbolTable getSymbols() { // Package protected on purpose | |||||
| return symbols; | return symbols; | ||||
| } | } | ||||
| @@ -618,6 +636,46 @@ public class Project { | |||||
| } | } | ||||
| } | } | ||||
| public ClassLoader addToLoader(String loader, Path path) { | |||||
| return symbols.addToLoader(loader, path); | |||||
| } | |||||
| public boolean addRoleDefinition(String role, | |||||
| Class roleClass, Class adapter) | |||||
| { | |||||
| return symbols.addRole(role, roleClass, adapter); | |||||
| } | |||||
| /** | |||||
| * test for a role name being in use already | |||||
| * | |||||
| * @param name the name to test | |||||
| * @return true if it is a task or a datatype | |||||
| */ | |||||
| public boolean isRoleDefined(String name) { | |||||
| return (symbols.getRole(name) != null); | |||||
| } | |||||
| public void addDefinitionOnRole(String role, | |||||
| String type, Class clz) | |||||
| { | |||||
| Class old = symbols.add(role, type, clz); | |||||
| // Special management for Tasks | |||||
| if (TASK_ROLE.equals(role) && null != old && !old.equals(clz)) { | |||||
| invalidateCreatedTasks(type); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * test for a name being in use already on this role | |||||
| * | |||||
| * @param name the name to test | |||||
| * @return true if it is a task or a datatype | |||||
| */ | |||||
| public boolean isDefinedOnRole(String role, String name) { | |||||
| return (symbols.get(role, name) != null); | |||||
| } | |||||
| /** | /** | ||||
| * add a new task definition, complain if there is an overwrite attempt | * add a new task definition, complain if there is an overwrite attempt | ||||
| * @param taskName name of the task | * @param taskName name of the task | ||||
| @@ -627,21 +685,14 @@ public class Project { | |||||
| */ | */ | ||||
| public void addTaskDefinition(String taskName, Class taskClass) | public void addTaskDefinition(String taskName, Class taskClass) | ||||
| throws BuildException { | throws BuildException { | ||||
| Class old = symbols.add("task", taskName, taskClass); | |||||
| if (null != old && !old.equals(taskClass)) { | |||||
| invalidateCreatedTasks(taskName); | |||||
| } | |||||
| String msg = | |||||
| " +User task: " + taskName + " " + taskClass.getName(); | |||||
| log(msg, MSG_DEBUG); | |||||
| checkTaskClass(taskClass); | |||||
| addDefinitionOnRole(TASK_ROLE, taskName, taskClass); | |||||
| } | } | ||||
| /** | /** | ||||
| * Checks a class, whether it is suitable for serving as ant task. | * Checks a class, whether it is suitable for serving as ant task. | ||||
| * @throws BuildException and logs as Project.MSG_ERR for | * @throws BuildException and logs as Project.MSG_ERR for | ||||
| * conditions, that will cause the task execution to fail. | * conditions, that will cause the task execution to fail. | ||||
| * @deprecated this is done now when added to SymbolTable | |||||
| */ | */ | ||||
| public void checkTaskClass(final Class taskClass) throws BuildException { | public void checkTaskClass(final Class taskClass) throws BuildException { | ||||
| if( !Task.class.isAssignableFrom(taskClass) ) { | if( !Task.class.isAssignableFrom(taskClass) ) { | ||||
| @@ -653,7 +704,7 @@ public class Project { | |||||
| * get the current task definition hashtable | * get the current task definition hashtable | ||||
| */ | */ | ||||
| public Hashtable getTaskDefinitions() { | public Hashtable getTaskDefinitions() { | ||||
| return symbols.getTaskDefinitions(); | |||||
| return symbols.getDefinitions(TASK_ROLE); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -662,18 +713,14 @@ public class Project { | |||||
| * @param typeClass full datatype classname | * @param typeClass full datatype classname | ||||
| */ | */ | ||||
| public void addDataTypeDefinition(String typeName, Class typeClass) { | public void addDataTypeDefinition(String typeName, Class typeClass) { | ||||
| symbols.add("datatype", typeName, typeClass); | |||||
| String msg = | |||||
| " +User datatype: " + typeName + " " + typeClass.getName(); | |||||
| log(msg, MSG_DEBUG); | |||||
| addDefinitionOnRole(DATATYPE_ROLE, typeName, typeClass); | |||||
| } | } | ||||
| /** | /** | ||||
| * get the current task definition hashtable | * get the current task definition hashtable | ||||
| */ | */ | ||||
| public Hashtable getDataTypeDefinitions() { | public Hashtable getDataTypeDefinitions() { | ||||
| return symbols.getDataTypeDefinitions(); | |||||
| return symbols.getDefinitions(DATATYPE_ROLE); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -701,7 +748,7 @@ public class Project { | |||||
| * in the project. | * in the project. | ||||
| * @see Project#addOrReplaceTarget to replace existing Targets. | * @see Project#addOrReplaceTarget to replace existing Targets. | ||||
| */ | */ | ||||
| public void addTarget(String targetName, Target target) | |||||
| public void addTarget(String targetName, Target target) | |||||
| throws BuildException { | throws BuildException { | ||||
| if (targets.get(targetName) != null) { | if (targets.get(targetName) != null) { | ||||
| throw new BuildException("Duplicate target: `"+targetName+"'"); | throw new BuildException("Duplicate target: `"+targetName+"'"); | ||||
| @@ -737,6 +784,88 @@ public class Project { | |||||
| return targets; | return targets; | ||||
| } | } | ||||
| /** | |||||
| * Create a new element instance on a Role | |||||
| * @param role name of the role to use | |||||
| * @param type name of the element to create | |||||
| * @return null if element unknown on this role | |||||
| */ | |||||
| public Object createForRole(String role, String type) { | |||||
| SymbolTable.Factory f = symbols.get(role, type); | |||||
| if (f == null) return null; | |||||
| try { | |||||
| Object o = f.create(this); | |||||
| // Do special book keeping for ProjectComponents | |||||
| if ( o instanceof ProjectComponent ) { | |||||
| ((ProjectComponent)o).setProject(this); | |||||
| if (o instanceof Task) { | |||||
| Task task = (Task) o; | |||||
| task.setTaskType(type); | |||||
| // set default value, can be changed by the user | |||||
| task.setTaskName(type); | |||||
| addCreatedTask(type, task); | |||||
| } | |||||
| } | |||||
| String msg = " +" + role + ": " + type; | |||||
| log (msg, MSG_DEBUG); | |||||
| return o; | |||||
| } | |||||
| catch (Throwable t) { | |||||
| String msg = "Could not create " + role + " of type: " | |||||
| + type + " due to " + t; | |||||
| throw new BuildException(msg, t); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * | |||||
| */ | |||||
| public Object createInRole(Object container, String type) { | |||||
| Class clz = container.getClass(); | |||||
| String roles[] = symbols.findRoles(clz); | |||||
| Object theOne = null; | |||||
| Method add = null; | |||||
| for(int i = 0; i < roles.length; i++) { | |||||
| Object o = createForRole(roles[i], type); | |||||
| if (o != null) { | |||||
| if (theOne != null) { | |||||
| String msg = "Element " + type + | |||||
| " is ambiguous for container " + clz.getName(); | |||||
| if (theOne instanceof RoleAdapter) | |||||
| theOne = ((RoleAdapter)theOne).getProxy(); | |||||
| if (o instanceof RoleAdapter) | |||||
| o = ((RoleAdapter)o).getProxy(); | |||||
| log(msg, MSG_ERR); | |||||
| log("cannot distinguish between " + | |||||
| theOne.getClass().getName() + | |||||
| " and " + o.getClass().getName(), MSG_ERR); | |||||
| throw new BuildException(msg); | |||||
| } | |||||
| theOne = o; | |||||
| add = symbols.getRole(roles[i]).getInterfaceMethod(); | |||||
| } | |||||
| } | |||||
| if (theOne != null) { | |||||
| try { | |||||
| add.invoke(container, new Object[]{theOne}); | |||||
| } | |||||
| catch(InvocationTargetException ite) { | |||||
| if (ite.getTargetException() instanceof BuildException) { | |||||
| throw (BuildException)ite.getTargetException(); | |||||
| } | |||||
| throw new BuildException(ite.getTargetException()); | |||||
| } | |||||
| catch(Exception e) { | |||||
| throw new BuildException(e); | |||||
| } | |||||
| } | |||||
| return theOne; | |||||
| } | |||||
| /** | /** | ||||
| * create a new task instance | * create a new task instance | ||||
| * @param taskType name of the task | * @param taskType name of the task | ||||
| @@ -744,39 +873,7 @@ public class Project { | |||||
| * @return null if the task name is unknown | * @return null if the task name is unknown | ||||
| */ | */ | ||||
| public Task createTask(String taskType) throws BuildException { | public Task createTask(String taskType) throws BuildException { | ||||
| Class c = symbols.get("task", taskType); | |||||
| if (c == null) { | |||||
| return null; | |||||
| } | |||||
| try { | |||||
| Object o = c.newInstance(); | |||||
| Task task = null; | |||||
| if( o instanceof Task ) { | |||||
| task=(Task)o; | |||||
| } else { | |||||
| // "Generic" Bean - use the setter pattern | |||||
| // and an Adapter | |||||
| TaskAdapter taskA=new TaskAdapter(); | |||||
| taskA.setProxy( o ); | |||||
| task=taskA; | |||||
| } | |||||
| task.setProject(this); | |||||
| task.setTaskType(taskType); | |||||
| // set default value, can be changed by the user | |||||
| task.setTaskName(taskType); | |||||
| String msg = " +Task: " + taskType; | |||||
| log (msg, MSG_DEBUG); | |||||
| addCreatedTask(taskType, task); | |||||
| return task; | |||||
| } catch (Throwable t) { | |||||
| String msg = "Could not create task of type: " | |||||
| + taskType + " due to " + t; | |||||
| throw new BuildException(msg, t); | |||||
| } | |||||
| return (Task) createForRole(TASK_ROLE, taskType); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -820,47 +917,11 @@ public class Project { | |||||
| * @return null if the datatype name is unknown | * @return null if the datatype name is unknown | ||||
| */ | */ | ||||
| public Object createDataType(String typeName) throws BuildException { | public Object createDataType(String typeName) throws BuildException { | ||||
| Class c = symbols.get("datatype", typeName); | |||||
| if (c == null) { | |||||
| return null; | |||||
| } | |||||
| try { | |||||
| java.lang.reflect.Constructor ctor = null; | |||||
| boolean noArg = false; | |||||
| // DataType can have a "no arg" constructor or take a single | |||||
| // Project argument. | |||||
| try { | |||||
| ctor = c.getConstructor(new Class[0]); | |||||
| noArg = true; | |||||
| } catch (NoSuchMethodException nse) { | |||||
| ctor = c.getConstructor(new Class[] {Project.class}); | |||||
| noArg = false; | |||||
| } | |||||
| Object o = null; | |||||
| if (noArg) { | |||||
| o = ctor.newInstance(new Object[0]); | |||||
| } else { | |||||
| o = ctor.newInstance(new Object[] {this}); | |||||
| } | |||||
| if (o instanceof ProjectComponent) { | |||||
| ((ProjectComponent)o).setProject(this); | |||||
| } | |||||
| String msg = " +DataType: " + typeName; | |||||
| log (msg, MSG_DEBUG); | |||||
| return o; | |||||
| } catch (java.lang.reflect.InvocationTargetException ite) { | |||||
| Throwable t = ite.getTargetException(); | |||||
| String msg = "Could not create datatype of type: " | |||||
| + typeName + " due to " + t; | |||||
| throw new BuildException(msg, t); | |||||
| } catch (Throwable t) { | |||||
| String msg = "Could not create datatype of type: " | |||||
| + typeName + " due to " + t; | |||||
| throw new BuildException(msg, t); | |||||
| } | |||||
| // This is to make the function backward compatible | |||||
| // Since we know if it returning an adapter for it | |||||
| DataTypeAdapterTask dt = | |||||
| (DataTypeAdapterTask) createForRole(DATATYPE_ROLE, typeName); | |||||
| return (dt != null? dt.getProxy() : null); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -1227,7 +1288,10 @@ public class Project { | |||||
| } | } | ||||
| public void addReference(String name, Object value) { | public void addReference(String name, Object value) { | ||||
| if (null != references.get(name)) { | |||||
| Object o = references.get(name); | |||||
| if (null != o && o != value | |||||
| && (!(o instanceof RoleAdapter) | |||||
| || ((RoleAdapter)o).getProxy() != value)) { | |||||
| log("Overriding previous definition of reference to " + name, | log("Overriding previous definition of reference to " + name, | ||||
| MSG_WARN); | MSG_WARN); | ||||
| } | } | ||||
| @@ -0,0 +1,797 @@ | |||||
| /* | |||||
| * The Apache Software License, Version 1.1 | |||||
| * | |||||
| * Copyright (c) 2000-2002 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", "Ant", 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.io.File; | |||||
| import java.io.FileInputStream; | |||||
| import java.io.FileNotFoundException; | |||||
| import java.io.IOException; | |||||
| import java.util.Hashtable; | |||||
| import java.util.Vector; | |||||
| import java.util.Enumeration; | |||||
| import java.util.Locale; | |||||
| import org.xml.sax.Locator; | |||||
| import org.xml.sax.InputSource; | |||||
| import org.xml.sax.HandlerBase; | |||||
| import org.xml.sax.SAXParseException; | |||||
| import org.xml.sax.SAXException; | |||||
| import org.xml.sax.DocumentHandler; | |||||
| import org.xml.sax.AttributeList; | |||||
| import javax.xml.parsers.SAXParserFactory; | |||||
| import javax.xml.parsers.SAXParser; | |||||
| import javax.xml.parsers.ParserConfigurationException; | |||||
| /** | |||||
| * Configures a Project (complete with Targets and Tasks) based on | |||||
| * a XML build file. | |||||
| * | |||||
| * @author duncan@x180.com | |||||
| */ | |||||
| public class ProjectHelper { | |||||
| private static SAXParserFactory parserFactory = null; | |||||
| private org.xml.sax.Parser parser; | |||||
| private Project project; | |||||
| private File buildFile; | |||||
| private File buildFileParent; | |||||
| private Locator locator; | |||||
| /** | |||||
| * Configures the Project with the contents of the specified XML file. | |||||
| */ | |||||
| public static void configureProject(Project project, File buildFile) throws BuildException { | |||||
| new ProjectHelper(project, buildFile).parse(); | |||||
| } | |||||
| /** | |||||
| * Constructs a new Ant parser for the specified XML file. | |||||
| */ | |||||
| private ProjectHelper(Project project, File buildFile) { | |||||
| this.project = project; | |||||
| this.buildFile = new File(buildFile.getAbsolutePath()); | |||||
| buildFileParent = new File(this.buildFile.getParent()); | |||||
| } | |||||
| /** | |||||
| * Parses the project file. | |||||
| */ | |||||
| private void parse() throws BuildException { | |||||
| FileInputStream inputStream = null; | |||||
| InputSource inputSource = null; | |||||
| try { | |||||
| SAXParser saxParser = getParserFactory().newSAXParser(); | |||||
| parser = saxParser.getParser(); | |||||
| String uri = "file:" + buildFile.getAbsolutePath().replace('\\', '/'); | |||||
| for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) { | |||||
| uri = uri.substring(0, index) + "%23" + uri.substring(index+1); | |||||
| } | |||||
| inputStream = new FileInputStream(buildFile); | |||||
| inputSource = new InputSource(inputStream); | |||||
| inputSource.setSystemId(uri); | |||||
| project.log("parsing buildfile " + buildFile + " with URI = " + uri, Project.MSG_VERBOSE); | |||||
| saxParser.parse(inputSource, new RootHandler()); | |||||
| } | |||||
| catch(ParserConfigurationException exc) { | |||||
| throw new BuildException("Parser has not been configured correctly", exc); | |||||
| } | |||||
| catch(SAXParseException exc) { | |||||
| Location location = | |||||
| new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber()); | |||||
| 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) { | |||||
| Throwable t = exc.getException(); | |||||
| if (t instanceof BuildException) { | |||||
| throw (BuildException) t; | |||||
| } | |||||
| throw new BuildException(exc.getMessage(), t); | |||||
| } | |||||
| catch(FileNotFoundException exc) { | |||||
| throw new BuildException(exc); | |||||
| } | |||||
| catch(IOException exc) { | |||||
| throw new BuildException("Error reading project file", exc); | |||||
| } | |||||
| finally { | |||||
| if (inputStream != null) { | |||||
| try { | |||||
| inputStream.close(); | |||||
| } | |||||
| catch (IOException ioe) { | |||||
| // ignore this | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * The common superclass for all sax event handlers in Ant. Basically | |||||
| * throws an exception in each method, so subclasses should override | |||||
| * what they can handle. | |||||
| * | |||||
| * Each type of xml element (task, target, etc) in ant will | |||||
| * have its own subclass of AbstractHandler. | |||||
| * | |||||
| * In the constructor, this class takes over the handling of sax | |||||
| * events from the parent handler, and returns | |||||
| * control back to the parent in the endElement method. | |||||
| */ | |||||
| private class AbstractHandler extends HandlerBase { | |||||
| protected DocumentHandler parentHandler; | |||||
| public AbstractHandler(DocumentHandler parentHandler) { | |||||
| this.parentHandler = parentHandler; | |||||
| // Start handling SAX events | |||||
| parser.setDocumentHandler(this); | |||||
| } | |||||
| public void startElement(String tag, AttributeList attrs) throws SAXParseException { | |||||
| throw new SAXParseException("Unexpected element \"" + tag + "\"", locator); | |||||
| } | |||||
| public void characters(char[] buf, int start, int end) throws SAXParseException { | |||||
| String s = new String(buf, start, end).trim(); | |||||
| if (s.length() > 0) { | |||||
| throw new SAXParseException("Unexpected text \"" + s + "\"", locator); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Called when this element and all elements nested into it have been | |||||
| * handled. | |||||
| */ | |||||
| protected void finished() {} | |||||
| public void endElement(String name) throws SAXException { | |||||
| finished(); | |||||
| // Let parent resume handling SAX events | |||||
| parser.setDocumentHandler(parentHandler); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Handler for the root element. It's only child must be the "project" element. | |||||
| */ | |||||
| private class RootHandler extends HandlerBase { | |||||
| /** | |||||
| * resolve file: URIs as relative to the build file. | |||||
| */ | |||||
| public InputSource resolveEntity(String publicId, | |||||
| String systemId) { | |||||
| project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE); | |||||
| if (systemId.startsWith("file:")) { | |||||
| String path = systemId.substring(5); | |||||
| int index = path.indexOf("file:"); | |||||
| // we only have to handle these for backward compatibility | |||||
| // since they are in the FAQ. | |||||
| while (index != -1) { | |||||
| path = path.substring(0, index) + path.substring(index + 5); | |||||
| index = path.indexOf("file:"); | |||||
| } | |||||
| String entitySystemId = path; | |||||
| index = path.indexOf("%23"); | |||||
| // convert these to # | |||||
| while (index != -1) { | |||||
| path = path.substring(0, index) + "#" + path.substring(index + 3); | |||||
| index = path.indexOf("%23"); | |||||
| } | |||||
| File file = new File(path); | |||||
| if (!file.isAbsolute()) { | |||||
| file = new File(buildFileParent, path); | |||||
| } | |||||
| try { | |||||
| InputSource inputSource = new InputSource(new FileInputStream(file)); | |||||
| inputSource.setSystemId("file:" + entitySystemId); | |||||
| return inputSource; | |||||
| } catch (FileNotFoundException fne) { | |||||
| project.log(file.getAbsolutePath()+" could not be found", | |||||
| Project.MSG_WARN); | |||||
| } | |||||
| } | |||||
| // use default if not file or file not found | |||||
| return null; | |||||
| } | |||||
| public void startElement(String tag, AttributeList attrs) throws SAXParseException { | |||||
| if (tag.equals("project")) { | |||||
| new ProjectHandler(this).init(tag, attrs); | |||||
| } else { | |||||
| throw new SAXParseException("Config file is not of expected XML type", locator); | |||||
| } | |||||
| } | |||||
| public void setDocumentLocator(Locator locator) { | |||||
| ProjectHelper.this.locator = locator; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Handler for the top level "project" element. | |||||
| */ | |||||
| private class ProjectHandler extends AbstractHandler { | |||||
| public ProjectHandler(DocumentHandler parentHandler) { | |||||
| super(parentHandler); | |||||
| } | |||||
| public void init(String tag, AttributeList attrs) throws SAXParseException { | |||||
| String def = null; | |||||
| String name = null; | |||||
| String id = null; | |||||
| String baseDir = null; | |||||
| for (int i = 0; i < attrs.getLength(); i++) { | |||||
| String key = attrs.getName(i); | |||||
| String value = attrs.getValue(i); | |||||
| if (key.equals("default")) { | |||||
| def = value; | |||||
| } else if (key.equals("name")) { | |||||
| name = value; | |||||
| } else if (key.equals("id")) { | |||||
| id = value; | |||||
| } else if (key.equals("basedir")) { | |||||
| baseDir = value; | |||||
| } else { | |||||
| throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator); | |||||
| } | |||||
| } | |||||
| if (def == null) { | |||||
| throw new SAXParseException("The default attribute of project is required", | |||||
| locator); | |||||
| } | |||||
| project.setDefaultTarget(def); | |||||
| if (name != null) { | |||||
| project.setName(name); | |||||
| project.addReference(name, project); | |||||
| } | |||||
| if (id != null) { | |||||
| project.addReference(id, project); | |||||
| } | |||||
| if (project.getProperty("basedir") != null) { | |||||
| project.setBasedir(project.getProperty("basedir")); | |||||
| } else { | |||||
| if (baseDir == null) { | |||||
| project.setBasedir(buildFileParent.getAbsolutePath()); | |||||
| } else { | |||||
| // check whether the user has specified an absolute path | |||||
| if ((new File(baseDir)).isAbsolute()) { | |||||
| project.setBasedir(baseDir); | |||||
| } else { | |||||
| project.setBaseDir(project.resolveFile(baseDir, buildFileParent)); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||||
| if (name.equals("taskdef")) { | |||||
| handleTopTask(name, attrs); | |||||
| } else if (name.equals("typedef")) { | |||||
| handleTopTask(name, attrs); | |||||
| } else if (name.equals("antlib")) { | |||||
| handleTopTask(name, attrs); | |||||
| } else if (name.equals("property")) { | |||||
| handleTopTask(name, attrs); | |||||
| } else if (name.equals("target")) { | |||||
| handleTarget(name, attrs); | |||||
| } else if (project.isDefinedOnRole(Project.DATATYPE_ROLE, name)) { | |||||
| handleTopTask(name, attrs); | |||||
| } else { | |||||
| throw new SAXParseException("Unexpected element \"" + name + "\"", locator); | |||||
| } | |||||
| } | |||||
| private void handleTopTask(String name, AttributeList attrs) | |||||
| throws SAXParseException { | |||||
| InmediateTarget target = new InmediateTarget(name); | |||||
| (new TaskHandler(this, target, null, target)).init(name, attrs); | |||||
| } | |||||
| private void handleTarget(String tag, AttributeList attrs) throws SAXParseException { | |||||
| new TargetHandler(this).init(tag, attrs); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Handler for "target" elements. | |||||
| */ | |||||
| private class TargetHandler extends AbstractHandler { | |||||
| private Target target; | |||||
| public TargetHandler(DocumentHandler parentHandler) { | |||||
| super(parentHandler); | |||||
| } | |||||
| public void init(String tag, AttributeList attrs) throws SAXParseException { | |||||
| String name = null; | |||||
| String depends = ""; | |||||
| String ifCond = null; | |||||
| String unlessCond = null; | |||||
| String id = null; | |||||
| String description = null; | |||||
| for (int i = 0; i < attrs.getLength(); i++) { | |||||
| String key = attrs.getName(i); | |||||
| String value = attrs.getValue(i); | |||||
| if (key.equals("name")) { | |||||
| name = value; | |||||
| } else if (key.equals("depends")) { | |||||
| depends = value; | |||||
| } else if (key.equals("if")) { | |||||
| ifCond = value; | |||||
| } else if (key.equals("unless")) { | |||||
| unlessCond = value; | |||||
| } else if (key.equals("id")) { | |||||
| id = value; | |||||
| } else if (key.equals("description")) { | |||||
| description = value; | |||||
| } else { | |||||
| throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator); | |||||
| } | |||||
| } | |||||
| if (name == null) { | |||||
| throw new SAXParseException("target element appears without a name attribute", locator); | |||||
| } | |||||
| target = new Target(); | |||||
| target.setName(name); | |||||
| target.setIf(ifCond); | |||||
| target.setUnless(unlessCond); | |||||
| target.setDescription(description); | |||||
| project.addTarget(name, target); | |||||
| if (id != null && !id.equals("")) { | |||||
| project.addReference(id, target); | |||||
| } | |||||
| // take care of dependencies | |||||
| if (depends.length() > 0) { | |||||
| target.setDepends(depends); | |||||
| } | |||||
| } | |||||
| public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||||
| new TaskHandler(this, target, null, target).init(name, attrs); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Handler for all task elements. | |||||
| */ | |||||
| private class TaskHandler extends AbstractHandler { | |||||
| private Target target; | |||||
| private TaskContainer container; | |||||
| private Task task; | |||||
| private RuntimeConfigurable parentWrapper; | |||||
| private RuntimeConfigurable wrapper = null; | |||||
| public TaskHandler(DocumentHandler parentHandler, TaskContainer container, RuntimeConfigurable parentWrapper, Target target) { | |||||
| super(parentHandler); | |||||
| this.container = container; | |||||
| this.parentWrapper = parentWrapper; | |||||
| this.target = target; | |||||
| } | |||||
| public void init(String tag, AttributeList attrs) throws SAXParseException { | |||||
| try { | |||||
| task = (Task)project.createInRole(container, tag); | |||||
| } catch (BuildException e) { | |||||
| // swallow here, will be thrown again in | |||||
| // UnknownElement.maybeConfigure if the problem persists. | |||||
| } | |||||
| if (task == null) { | |||||
| task = new UnknownElement(tag); | |||||
| task.setProject(project); | |||||
| task.setTaskType(tag); | |||||
| task.setTaskName(tag); | |||||
| container.addTask(task); | |||||
| } | |||||
| task.setLocation(new Location(buildFile.toString(), | |||||
| locator.getLineNumber(), | |||||
| locator.getColumnNumber())); | |||||
| configureId(task, attrs); | |||||
| task.setOwningTarget(target); | |||||
| task.init(); | |||||
| wrapper = task.getRuntimeConfigurableWrapper(); | |||||
| wrapper.setAttributes(attrs); | |||||
| if (parentWrapper != null) { | |||||
| parentWrapper.addChild(wrapper); | |||||
| } | |||||
| } | |||||
| protected void finished() { | |||||
| if (container instanceof InmediateTarget) { | |||||
| ((InmediateTarget)container).execute(); | |||||
| } | |||||
| } | |||||
| public void characters(char[] buf, int start, int end) throws SAXParseException { | |||||
| if (wrapper == null) { | |||||
| try { | |||||
| addText(project, task, buf, start, end); | |||||
| } catch (BuildException exc) { | |||||
| throw new SAXParseException(exc.getMessage(), locator, exc); | |||||
| } | |||||
| } else { | |||||
| wrapper.addText(buf, start, end); | |||||
| } | |||||
| } | |||||
| public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||||
| if (task instanceof TaskContainer) { | |||||
| // task can contain other tasks - no other nested elements possible | |||||
| new TaskHandler(this, (TaskContainer)task, wrapper, target).init(name, attrs); | |||||
| } | |||||
| else { | |||||
| new NestedElementHandler(this, task, wrapper, target).init(name, attrs); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Handler for all nested properties. | |||||
| */ | |||||
| private class NestedElementHandler extends AbstractHandler { | |||||
| private Object parent; | |||||
| private Object child; | |||||
| private RuntimeConfigurable parentWrapper; | |||||
| private RuntimeConfigurable childWrapper = null; | |||||
| private Target target; | |||||
| public NestedElementHandler(DocumentHandler parentHandler, | |||||
| Object parent, | |||||
| RuntimeConfigurable parentWrapper, | |||||
| Target target) { | |||||
| super(parentHandler); | |||||
| if (parent instanceof RoleAdapter) { | |||||
| this.parent = ((RoleAdapter) parent).getProxy(); | |||||
| } else { | |||||
| this.parent = parent; | |||||
| } | |||||
| this.parentWrapper = parentWrapper; | |||||
| this.target = target; | |||||
| } | |||||
| public void init(String propType, AttributeList attrs) throws SAXParseException { | |||||
| Class parentClass = parent.getClass(); | |||||
| IntrospectionHelper ih = | |||||
| IntrospectionHelper.getHelper(parentClass); | |||||
| try { | |||||
| String elementName = propType.toLowerCase(Locale.US); | |||||
| if (parent instanceof UnknownElement) { | |||||
| UnknownElement uc = new UnknownElement(elementName); | |||||
| uc.setProject(project); | |||||
| ((UnknownElement) parent).addChild(uc); | |||||
| // Set this parameters just in case is a Task | |||||
| uc.setTaskType(elementName); | |||||
| uc.setTaskName(elementName); | |||||
| child = uc; | |||||
| } else { | |||||
| child = ih.createElement(project, parent, elementName); | |||||
| } | |||||
| configureId(child, attrs); | |||||
| if (parentWrapper != null) { | |||||
| childWrapper = new RuntimeConfigurable(child, propType); | |||||
| childWrapper.setAttributes(attrs); | |||||
| parentWrapper.addChild(childWrapper); | |||||
| } else { | |||||
| configure(child, attrs, project); | |||||
| ih.storeElement(project, parent, child, elementName); | |||||
| } | |||||
| } catch (BuildException exc) { | |||||
| throw new SAXParseException(exc.getMessage(), locator, exc); | |||||
| } | |||||
| } | |||||
| public void characters(char[] buf, int start, int end) throws SAXParseException { | |||||
| if (parentWrapper == null) { | |||||
| try { | |||||
| addText(project, child, buf, start, end); | |||||
| } catch (BuildException exc) { | |||||
| throw new SAXParseException(exc.getMessage(), locator, exc); | |||||
| } | |||||
| } else { | |||||
| childWrapper.addText(buf, start, end); | |||||
| } | |||||
| } | |||||
| public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||||
| if (child instanceof TaskContainer) { | |||||
| // taskcontainer nested element can contain other tasks - no other | |||||
| // nested elements possible | |||||
| new TaskHandler(this, (TaskContainer)child, childWrapper, target).init(name, attrs); | |||||
| } | |||||
| else { | |||||
| new NestedElementHandler(this, child, childWrapper, target).init(name, attrs); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Special target type for top level Tasks and Datatypes. | |||||
| * This will allow eliminating special cases. | |||||
| */ | |||||
| private class InmediateTarget extends Target { | |||||
| /** | |||||
| * Create a target for a top level task or datatype. | |||||
| * @param name the name of the task to be run on this target. | |||||
| */ | |||||
| InmediateTarget(String name) { | |||||
| super(); | |||||
| setProject(project); | |||||
| setName("Top level " + name); | |||||
| } | |||||
| } | |||||
| public static void configure(Object target, AttributeList attrs, | |||||
| Project project) throws BuildException { | |||||
| if( target instanceof RoleAdapter ) { | |||||
| target=((RoleAdapter)target).getProxy(); | |||||
| } | |||||
| IntrospectionHelper ih = | |||||
| IntrospectionHelper.getHelper(target.getClass()); | |||||
| project.addBuildListener(ih); | |||||
| for (int i = 0; i < attrs.getLength(); i++) { | |||||
| // reflect these into the target | |||||
| String value=replaceProperties(project, attrs.getValue(i), | |||||
| project.getProperties() ); | |||||
| try { | |||||
| ih.setAttribute(project, target, | |||||
| attrs.getName(i).toLowerCase(Locale.US), value); | |||||
| } catch (BuildException be) { | |||||
| // id attribute must be set externally | |||||
| if (!attrs.getName(i).equals("id")) { | |||||
| throw be; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Adds the content of #PCDATA sections to an element. | |||||
| */ | |||||
| public static void addText(Project project, Object target, char[] buf, int start, int end) | |||||
| throws BuildException { | |||||
| addText(project, target, new String(buf, start, end)); | |||||
| } | |||||
| /** | |||||
| * Adds the content of #PCDATA sections to an element. | |||||
| */ | |||||
| public static void addText(Project project, Object target, String text) | |||||
| throws BuildException { | |||||
| if (text == null ) { | |||||
| return; | |||||
| } | |||||
| if(target instanceof RoleAdapter) { | |||||
| target = ((RoleAdapter) target).getProxy(); | |||||
| } | |||||
| IntrospectionHelper.getHelper(target.getClass()).addText(project, target, text); | |||||
| } | |||||
| /** | |||||
| * Stores a configured child element into its parent object | |||||
| */ | |||||
| public static void storeChild(Project project, Object parent, Object child, String tag) { | |||||
| IntrospectionHelper ih = IntrospectionHelper.getHelper(parent.getClass()); | |||||
| ih.storeElement(project, parent, child, tag); | |||||
| } | |||||
| /** | |||||
| * Replace ${} style constructions in the given value with the string value of | |||||
| * the corresponding data types. | |||||
| * | |||||
| * @param value the string to be scanned for property references. | |||||
| * @since 1.5 | |||||
| */ | |||||
| public static String replaceProperties(Project project, String value) | |||||
| throws BuildException { | |||||
| return project.replaceProperties(value); | |||||
| } | |||||
| /** | |||||
| * Replace ${} style constructions in the given value with the string value of | |||||
| * the corresponding data types. | |||||
| * | |||||
| * @param value the string to be scanned for property references. | |||||
| */ | |||||
| public static String replaceProperties(Project project, String value, Hashtable keys) | |||||
| throws BuildException { | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| Vector fragments = new Vector(); | |||||
| Vector propertyRefs = new Vector(); | |||||
| parsePropertyString(value, fragments, propertyRefs); | |||||
| StringBuffer sb = new StringBuffer(); | |||||
| Enumeration i = fragments.elements(); | |||||
| Enumeration j = propertyRefs.elements(); | |||||
| while (i.hasMoreElements()) { | |||||
| String fragment = (String)i.nextElement(); | |||||
| if (fragment == null) { | |||||
| String propertyName = (String)j.nextElement(); | |||||
| if (!keys.containsKey(propertyName)) { | |||||
| project.log("Property ${" + propertyName + "} has not been set", Project.MSG_VERBOSE); | |||||
| } | |||||
| fragment = (keys.containsKey(propertyName)) ? (String) keys.get(propertyName) | |||||
| : "${" + propertyName + "}"; | |||||
| } | |||||
| sb.append(fragment); | |||||
| } | |||||
| return sb.toString(); | |||||
| } | |||||
| /** | |||||
| * This method will parse a string containing ${value} style | |||||
| * property values into two lists. The first list is a collection | |||||
| * of text fragments, while the other is a set of string property names | |||||
| * null entries in the first list indicate a property reference from the | |||||
| * second list. | |||||
| */ | |||||
| public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs) | |||||
| throws BuildException { | |||||
| int prev = 0; | |||||
| int pos; | |||||
| while ((pos = value.indexOf("$", prev)) >= 0) { | |||||
| if (pos > 0) { | |||||
| fragments.addElement(value.substring(prev, pos)); | |||||
| } | |||||
| if( pos == (value.length() - 1)) { | |||||
| fragments.addElement("$"); | |||||
| prev = pos + 1; | |||||
| } | |||||
| else if (value.charAt(pos + 1) != '{' ) { | |||||
| fragments.addElement(value.substring(pos + 1, pos + 2)); | |||||
| prev = pos + 2; | |||||
| } else { | |||||
| int endName = value.indexOf('}', pos); | |||||
| if (endName < 0) { | |||||
| throw new BuildException("Syntax error in property: " | |||||
| + value ); | |||||
| } | |||||
| String propertyName = value.substring(pos + 2, endName); | |||||
| fragments.addElement(null); | |||||
| propertyRefs.addElement(propertyName); | |||||
| prev = endName + 1; | |||||
| } | |||||
| } | |||||
| if (prev < value.length()) { | |||||
| fragments.addElement(value.substring(prev)); | |||||
| } | |||||
| } | |||||
| private static SAXParserFactory getParserFactory() { | |||||
| if (parserFactory == null) { | |||||
| parserFactory = SAXParserFactory.newInstance(); | |||||
| } | |||||
| return parserFactory; | |||||
| } | |||||
| /** | |||||
| * Scan AttributeList for the id attribute and maybe add a | |||||
| * reference to project. | |||||
| * | |||||
| * <p>Moved out of {@link #configure configure} to make it happen | |||||
| * at parser time.</p> | |||||
| */ | |||||
| private void configureId(Object target, AttributeList attr) { | |||||
| String id = attr.getValue("id"); | |||||
| if (id != null) { | |||||
| if( target instanceof RoleAdapter ) { | |||||
| ((RoleAdapter)target).setId(id); | |||||
| } | |||||
| project.addReference(id, target); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -55,6 +55,11 @@ package org.apache.tools.ant; | |||||
| public interface RoleAdapter { | public interface RoleAdapter { | ||||
| /** | |||||
| * Obtain the id in case it is needed. | |||||
| */ | |||||
| public void setId(String id); | |||||
| /** | /** | ||||
| * Set the object being adapted. | * Set the object being adapted. | ||||
| * @param o the object being adapted | * @param o the object being adapted | ||||
| @@ -54,6 +54,7 @@ | |||||
| package org.apache.tools.ant; | package org.apache.tools.ant; | ||||
| import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||
| import java.lang.reflect.Constructor; | |||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.lang.reflect.Modifier; | import java.lang.reflect.Modifier; | ||||
| import java.util.*; | import java.util.*; | ||||
| @@ -96,8 +97,8 @@ public class SymbolTable { | |||||
| * from that defined in the calling Project. | * from that defined in the calling Project. | ||||
| * @param p the calling project | * @param p the calling project | ||||
| */ | */ | ||||
| public SymbolTable(Project p) { | |||||
| parentTable = p.getSymbols(); | |||||
| public SymbolTable(SymbolTable st) { | |||||
| parentTable = st; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -108,6 +109,54 @@ public class SymbolTable { | |||||
| this.project = p; | this.project = p; | ||||
| } | } | ||||
| /** | |||||
| * Get the specified loader for the project. | |||||
| * @param name the name of the loader | |||||
| * @return the corresponding ANT classloader | |||||
| */ | |||||
| private AntClassLoader getLoader(String name) { | |||||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||||
| if (cl == null && parentTable != null) { | |||||
| return parentTable.getLoader(name); | |||||
| } | |||||
| return cl; | |||||
| } | |||||
| /** | |||||
| * Add the specified class-path to a loader. | |||||
| * If the loader is defined in an ancestor project then a new | |||||
| * classloader inheritin from the one already existing | |||||
| * will be created, otherwise the path willbe added to the existing | |||||
| * ClassLoader. | |||||
| * @param name the name of the loader to use. | |||||
| * @param clspath the path to be added to the classloader | |||||
| */ | |||||
| public ClassLoader addToLoader(String name, Path clspath) { | |||||
| // Find if the loader is already defined in the current project | |||||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||||
| if (cl == null) { | |||||
| // Is it inherited from the calling project | |||||
| if (parentTable != null) { | |||||
| cl = parentTable.getLoader(name); | |||||
| } | |||||
| cl = new AntClassLoader(cl, project, clspath, true); | |||||
| loaders.put(name, cl); | |||||
| } | |||||
| else { | |||||
| // Add additional path to the existing definition | |||||
| String[] pathElements = clspath.list(); | |||||
| for (int i = 0; i < pathElements.length; ++i) { | |||||
| try { | |||||
| cl.addPathElement(pathElements[i]); | |||||
| } | |||||
| catch (BuildException e) { | |||||
| // ignore path elements invalid relative to the project | |||||
| } | |||||
| } | |||||
| } | |||||
| return cl; | |||||
| } | |||||
| /** | /** | ||||
| * Find all the roles supported by a Class | * Find all the roles supported by a Class | ||||
| * on this symbol table. | * on this symbol table. | ||||
| @@ -133,13 +182,13 @@ public class SymbolTable { | |||||
| list.addElement(role); | list.addElement(role); | ||||
| } | } | ||||
| } | } | ||||
| if (parentTable != null) findRoles(clz, list); | |||||
| if (parentTable != null) parentTable.findRoles(clz, list); | |||||
| } | } | ||||
| /** | /** | ||||
| * Get the Role definition | * Get the Role definition | ||||
| * @param role the name of the role | * @param role the name of the role | ||||
| * @return the method used to support objects on this role | |||||
| * @return the Role description | |||||
| */ | */ | ||||
| public Role getRole(String role) { | public Role getRole(String role) { | ||||
| Role r = (Role) roles.get(role); | Role r = (Role) roles.get(role); | ||||
| @@ -171,112 +220,6 @@ public class SymbolTable { | |||||
| return (old != null); | return (old != null); | ||||
| } | } | ||||
| /** | |||||
| * Verify if the interface is valid. | |||||
| * @param clz the interface to validate | |||||
| * @return the method defined by the interface | |||||
| */ | |||||
| private Method validInterface(Class clz) { | |||||
| Method m[] = clz.getDeclaredMethods(); | |||||
| if (m.length == 1 | |||||
| && java.lang.Void.TYPE.equals(m[0].getReturnType())) { | |||||
| Class args[] = m[0].getParameterTypes(); | |||||
| if (args.length == 1 | |||||
| && !java.lang.String.class.equals(args[0]) | |||||
| && !args[0].isArray() | |||||
| && !args[0].isPrimitive()) { | |||||
| return m[0]; | |||||
| } | |||||
| else { | |||||
| throw new BuildException("Invalid role interface method in: " | |||||
| + clz.getName()); | |||||
| } | |||||
| } | |||||
| else { | |||||
| throw new BuildException("More than one method on role interface"); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Verify if the adapter is valid with respect to the interface. | |||||
| * @param clz the class adapter to validate | |||||
| * @param mtd the method whose only argument must match | |||||
| * @return the static method to use for validating adaptees | |||||
| */ | |||||
| private Method validAdapter(Class clz, Method mtd) { | |||||
| if (clz == null) return null; | |||||
| checkClass(clz); | |||||
| if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) { | |||||
| String msg = "Adapter " + clz.getName() + | |||||
| " is incompatible with role interface " + | |||||
| mtd.getDeclaringClass().getName(); | |||||
| throw new BuildException(msg); | |||||
| } | |||||
| String msg = "Class " + clz.getName() + " is not an adapter: "; | |||||
| if (!RoleAdapter.class.isAssignableFrom(clz)) { | |||||
| throw new BuildException(msg + "does not implement RoleAdapter"); | |||||
| } | |||||
| try { | |||||
| Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS); | |||||
| if (!Modifier.isStatic(chk.getModifiers())) { | |||||
| throw new BuildException(msg + "checkClass() is not static"); | |||||
| } | |||||
| return chk; | |||||
| } | |||||
| catch(NoSuchMethodException nme){ | |||||
| throw new BuildException(msg + "checkClass() not found", nme); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Get the specified loader for the project. | |||||
| * @param name the name of the loader | |||||
| * @return the corresponding ANT classloader | |||||
| */ | |||||
| private AntClassLoader getLoader(String name) { | |||||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||||
| if (cl == null && parentTable != null) { | |||||
| return parentTable.getLoader(name); | |||||
| } | |||||
| return cl; | |||||
| } | |||||
| /** | |||||
| * Add the specified class-path to a loader. | |||||
| * If the loader is defined in an ancestor project then a new | |||||
| * classloader inheritin from the one already existing | |||||
| * will be created, otherwise the path willbe added to the existing | |||||
| * ClassLoader. | |||||
| * @param name the name of the loader to use. | |||||
| * @param clspath the path to be added to the classloader | |||||
| */ | |||||
| public ClassLoader addToLoader(String name, Path clspath) { | |||||
| // Find if the loader is already defined in the current project | |||||
| AntClassLoader cl = (AntClassLoader) loaders.get(name); | |||||
| if (cl == null) { | |||||
| // Is it inherited from the calling project | |||||
| if (parentTable != null) { | |||||
| cl = parentTable.getLoader(name); | |||||
| } | |||||
| cl = new AntClassLoader(cl, project, clspath, true); | |||||
| loaders.put(name, cl); | |||||
| } | |||||
| else { | |||||
| // Add additional path to the existing definition | |||||
| String[] pathElements = clspath.list(); | |||||
| for (int i = 0; i < pathElements.length; ++i) { | |||||
| try { | |||||
| cl.addPathElement(pathElements[i]); | |||||
| } | |||||
| catch (BuildException e) { | |||||
| // ignore path elements invalid relative to the project | |||||
| } | |||||
| } | |||||
| } | |||||
| return cl; | |||||
| } | |||||
| /** | /** | ||||
| * Add a new type of element to a role. | * Add a new type of element to a role. | ||||
| * @param role the role for this Class. | * @param role the role for this Class. | ||||
| @@ -291,13 +234,13 @@ public class SymbolTable { | |||||
| throw new BuildException("Unknown role: " + role); | throw new BuildException("Unknown role: " + role); | ||||
| } | } | ||||
| // Check if it is already defined | // Check if it is already defined | ||||
| Class old = get(role, name); | |||||
| Factory old = get(role, name); | |||||
| if (old != null) { | if (old != null) { | ||||
| if (old.equals(clz)) { | |||||
| if (old.getOriginalClass().equals(clz)) { | |||||
| project.log("Ignoring override for "+ role + " " + name | project.log("Ignoring override for "+ role + " " + name | ||||
| + ", it is already defined by the same class.", | + ", it is already defined by the same class.", | ||||
| project.MSG_VERBOSE); | project.MSG_VERBOSE); | ||||
| return old; | |||||
| return old.getOriginalClass(); | |||||
| } | } | ||||
| else { | else { | ||||
| project.log("Trying to override old definition of " + | project.log("Trying to override old definition of " + | ||||
| @@ -305,26 +248,33 @@ public class SymbolTable { | |||||
| project.MSG_WARN); | project.MSG_WARN); | ||||
| } | } | ||||
| } | } | ||||
| checkClass(clz); | |||||
| Factory f = checkClass(clz); | |||||
| // Check that the Class is compatible with the role definition | // Check that the Class is compatible with the role definition | ||||
| r.verifyAdaptability(role, clz); | |||||
| f = r.verifyAdaptability(role, f); | |||||
| // Record the new type | // Record the new type | ||||
| Hashtable defTable = (Hashtable)defs.get(role); | Hashtable defTable = (Hashtable)defs.get(role); | ||||
| if (defTable == null) { | if (defTable == null) { | ||||
| defTable = new Hashtable(); | defTable = new Hashtable(); | ||||
| defs.put(role, defTable); | defs.put(role, defTable); | ||||
| } | } | ||||
| defTable.put(name, clz); | |||||
| return old; | |||||
| defTable.put(name, f); | |||||
| String msg = | |||||
| " +User " + role + ": " + name + " " + clz.getName(); | |||||
| project.log(msg, project.MSG_DEBUG); | |||||
| return (old != null ? old.getOriginalClass() : null); | |||||
| } | } | ||||
| /** | /** | ||||
| * Checks a class, whether it is suitable for serving in ANT. | * Checks a class, whether it is suitable for serving in ANT. | ||||
| * @return the factory to use when instantiating the class | |||||
| * @throws BuildException and logs as Project.MSG_ERR for | * @throws BuildException and logs as Project.MSG_ERR for | ||||
| * conditions, that will cause execution to fail. | * conditions, that will cause execution to fail. | ||||
| */ | */ | ||||
| void checkClass(final Class clz) | |||||
| Factory checkClass(final Class clz) // Package on purpose | |||||
| throws BuildException { | throws BuildException { | ||||
| if (clz == null) return null; | |||||
| if(!Modifier.isPublic(clz.getModifiers())) { | if(!Modifier.isPublic(clz.getModifiers())) { | ||||
| final String message = clz + " is not public"; | final String message = clz + " is not public"; | ||||
| project.log(message, Project.MSG_ERR); | project.log(message, Project.MSG_ERR); | ||||
| @@ -342,8 +292,37 @@ public class SymbolTable { | |||||
| // getConstructor finds public constructors only. | // getConstructor finds public constructors only. | ||||
| try { | try { | ||||
| clz.getConstructor(new Class[0]); | clz.getConstructor(new Class[0]); | ||||
| return new Factory(){ | |||||
| public Object create(Project p) { | |||||
| try { | |||||
| return clz.newInstance(); | |||||
| } | |||||
| catch(Exception e) { | |||||
| throw new BuildException(e); | |||||
| } | |||||
| } | |||||
| public Class getOriginalClass() { | |||||
| return clz; | |||||
| } | |||||
| }; | |||||
| } catch (NoSuchMethodException nse) { | } catch (NoSuchMethodException nse) { | ||||
| clz.getConstructor(new Class[] {Project.class}); | |||||
| final Constructor c = | |||||
| clz.getConstructor(new Class[] {Project.class}); | |||||
| return new Factory(){ | |||||
| public Object create(Project p) { | |||||
| try { | |||||
| return c.newInstance(new Object[]{p}); | |||||
| } | |||||
| catch(Exception e) { | |||||
| throw new BuildException(e); | |||||
| } | |||||
| } | |||||
| public Class getOriginalClass() { | |||||
| return clz; | |||||
| } | |||||
| }; | |||||
| } | } | ||||
| } catch(NoSuchMethodException e) { | } catch(NoSuchMethodException e) { | ||||
| final String message = | final String message = | ||||
| @@ -359,11 +338,11 @@ public class SymbolTable { | |||||
| * @param name the name of the element to sea | * @param name the name of the element to sea | ||||
| * @return the Class implementation | * @return the Class implementation | ||||
| */ | */ | ||||
| public Class get(String role, String name) { | |||||
| public Factory get(String role, String name) { | |||||
| Hashtable defTable = (Hashtable)defs.get(role); | Hashtable defTable = (Hashtable)defs.get(role); | ||||
| if (defTable != null) { | if (defTable != null) { | ||||
| Class clz = (Class)defTable.get(name); | |||||
| if (clz != null) return clz; | |||||
| Factory f = (Factory)defTable.get(name); | |||||
| if (f != null) return f; | |||||
| } | } | ||||
| if (parentTable != null) { | if (parentTable != null) { | ||||
| return parentTable.get(role, name); | return parentTable.get(role, name); | ||||
| @@ -372,19 +351,12 @@ public class SymbolTable { | |||||
| } | } | ||||
| /** | /** | ||||
| * Get a Hashtable that is usable for manipulating Tasks, | |||||
| * Get a Hashtable that is usable for manipulating elements on Role. | |||||
| * @param role the role of the elements in the table | |||||
| * @return a Hashtable that delegates to the Symbol table. | * @return a Hashtable that delegates to the Symbol table. | ||||
| */ | */ | ||||
| public Hashtable getTaskDefinitions() { | |||||
| return new SymbolHashtable("task"); | |||||
| } | |||||
| /** | |||||
| * Get a Hashtable that is usable for manipulating Datatypes, | |||||
| * @return a Hashtable that delegates to the Symbol table. | |||||
| */ | |||||
| public Hashtable getDataTypeDefinitions() { | |||||
| return new SymbolHashtable("datatype"); | |||||
| Hashtable getDefinitions(String role) { // package scope on purpose | |||||
| return new SymbolHashtable(role); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -402,16 +374,43 @@ public class SymbolTable { | |||||
| } | } | ||||
| public synchronized Object get(Object key) { | public synchronized Object get(Object key) { | ||||
| return SymbolTable.this.get(role, (String)key); | |||||
| Factory f = SymbolTable.this.get(role, (String)key); | |||||
| return (f == null? null : f.getOriginalClass()); | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Factory for creating ANT objects. | |||||
| * Class objects are not instanciated directly but through a Factory | |||||
| * which is able to resolve issues such as proxys and such. | |||||
| */ | |||||
| public static interface Factory { | |||||
| /** | |||||
| * Creates an object for the Role | |||||
| * @param the project in which it is created | |||||
| * @return the instantiated object with a proxy if necessary | |||||
| */ | |||||
| public Object create(Project p); | |||||
| /** | |||||
| * Creates an object for the Role, adapted if necessary | |||||
| * for a particular interface. | |||||
| */ | |||||
| // public Object adaptFor(Class clz, Project p, Object o); | |||||
| /** | |||||
| * The original class of the object without proxy. | |||||
| */ | |||||
| public Class getOriginalClass(); | |||||
| } | |||||
| /** | /** | ||||
| * The definition of a role | * The definition of a role | ||||
| */ | */ | ||||
| public class Role { | public class Role { | ||||
| private Method interfaceMethod; | private Method interfaceMethod; | ||||
| private Method adapterVerifier; | private Method adapterVerifier; | ||||
| private Factory adapterFactory; | |||||
| /** | /** | ||||
| * Creates a new Role object | * Creates a new Role object | ||||
| @@ -420,6 +419,7 @@ public class SymbolTable { | |||||
| */ | */ | ||||
| Role(Class roleClz, Class adapterClz) { | Role(Class roleClz, Class adapterClz) { | ||||
| interfaceMethod = validInterface(roleClz); | interfaceMethod = validInterface(roleClz); | ||||
| adapterFactory = checkClass(adapterClz); | |||||
| adapterVerifier = validAdapter(adapterClz, interfaceMethod); | adapterVerifier = validAdapter(adapterClz, interfaceMethod); | ||||
| } | } | ||||
| @@ -433,12 +433,11 @@ public class SymbolTable { | |||||
| /** | /** | ||||
| * Instantiate a new adapter for this role. | * Instantiate a new adapter for this role. | ||||
| */ | */ | ||||
| public RoleAdapter createAdapter() { | |||||
| if (adapterVerifier == null) return null; | |||||
| public RoleAdapter createAdapter(Project p) { | |||||
| if (adapterFactory == null) return null; | |||||
| try { | try { | ||||
| return (RoleAdapter) | |||||
| adapterVerifier.getDeclaringClass().newInstance(); | |||||
| return (RoleAdapter) adapterFactory.create(p); | |||||
| } | } | ||||
| catch(BuildException be) { | catch(BuildException be) { | ||||
| throw be; | throw be; | ||||
| @@ -451,11 +450,12 @@ public class SymbolTable { | |||||
| /** | /** | ||||
| * Verify if the class can be adapted to use by the role | * Verify if the class can be adapted to use by the role | ||||
| * @param role the name of the role to verify | * @param role the name of the role to verify | ||||
| * @param clz the class to verify | |||||
| * @param f the factory for the class to verify | |||||
| */ | */ | ||||
| public void verifyAdaptability(String role, Class clz) { | |||||
| public Factory verifyAdaptability(String role, final Factory f) { | |||||
| final Class clz = f.getOriginalClass(); | |||||
| if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) { | if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) { | ||||
| return; | |||||
| return f; | |||||
| } | } | ||||
| if (adapterVerifier == null) { | if (adapterVerifier == null) { | ||||
| String msg = "Class " + clz.getName() + | String msg = "Class " + clz.getName() + | ||||
| @@ -464,8 +464,18 @@ public class SymbolTable { | |||||
| } | } | ||||
| try { | try { | ||||
| try { | try { | ||||
| adapterVerifier.invoke(null, | |||||
| new Object[]{clz, project}); | |||||
| adapterVerifier.invoke(null, new Object[]{clz, project}); | |||||
| return new Factory(){ | |||||
| public Object create(Project p) { | |||||
| RoleAdapter ra = createAdapter(p); | |||||
| ra.setProxy(f.create(p)); | |||||
| return ra; | |||||
| } | |||||
| public Class getOriginalClass() { | |||||
| return clz; | |||||
| } | |||||
| }; | |||||
| } | } | ||||
| catch (InvocationTargetException ite) { | catch (InvocationTargetException ite) { | ||||
| throw ite.getTargetException(); | throw ite.getTargetException(); | ||||
| @@ -487,5 +497,63 @@ public class SymbolTable { | |||||
| public boolean isImplementedBy(Class clz) { | public boolean isImplementedBy(Class clz) { | ||||
| return interfaceMethod.getDeclaringClass().isAssignableFrom(clz); | return interfaceMethod.getDeclaringClass().isAssignableFrom(clz); | ||||
| } | } | ||||
| /** | |||||
| * Verify if the interface is valid. | |||||
| * @param clz the interface to validate | |||||
| * @return the method defined by the interface | |||||
| */ | |||||
| private Method validInterface(Class clz) { | |||||
| Method m[] = clz.getDeclaredMethods(); | |||||
| if (m.length == 1 | |||||
| && java.lang.Void.TYPE.equals(m[0].getReturnType())) { | |||||
| Class args[] = m[0].getParameterTypes(); | |||||
| if (args.length == 1 | |||||
| && !java.lang.String.class.equals(args[0]) | |||||
| && !args[0].isArray() | |||||
| && !args[0].isPrimitive()) { | |||||
| return m[0]; | |||||
| } | |||||
| else { | |||||
| throw new BuildException("Invalid role interface method in: " | |||||
| + clz.getName()); | |||||
| } | |||||
| } | |||||
| else { | |||||
| throw new BuildException("More than one method on role interface"); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Verify if the adapter is valid with respect to the interface. | |||||
| * @param clz the class adapter to validate | |||||
| * @param mtd the method whose only argument must match | |||||
| * @return the static method to use for validating adaptees | |||||
| */ | |||||
| private Method validAdapter(Class clz, Method mtd) { | |||||
| if (clz == null) return null; | |||||
| if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) { | |||||
| String msg = "Adapter " + clz.getName() + | |||||
| " is incompatible with role interface " + | |||||
| mtd.getDeclaringClass().getName(); | |||||
| throw new BuildException(msg); | |||||
| } | |||||
| String msg = "Class " + clz.getName() + " is not an adapter: "; | |||||
| if (!RoleAdapter.class.isAssignableFrom(clz)) { | |||||
| throw new BuildException(msg + "does not implement RoleAdapter"); | |||||
| } | |||||
| try { | |||||
| Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS); | |||||
| if (!Modifier.isStatic(chk.getModifiers())) { | |||||
| throw new BuildException(msg + "checkClass() is not static"); | |||||
| } | |||||
| return chk; | |||||
| } | |||||
| catch(NoSuchMethodException nme){ | |||||
| throw new BuildException(msg + "checkClass() not found", nme); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -165,4 +165,5 @@ public class TaskAdapter extends Task implements RoleAdapter { | |||||
| return this.proxy ; | return this.proxy ; | ||||
| } | } | ||||
| public void setId(String id) {} | |||||
| } | } | ||||
| @@ -138,10 +138,8 @@ public class Ant extends Task { | |||||
| } | } | ||||
| public void init() { | public void init() { | ||||
| newProject = new Project(project); | |||||
| newProject = project.createSubProject(); | |||||
| newProject.setJavaVersionProperty(); | newProject.setJavaVersionProperty(); | ||||
| // newProject.addTaskDefinition("property", | |||||
| // (Class)project.getTaskDefinitions().get("property")); | |||||
| } | } | ||||
| private void reinit() { | private void reinit() { | ||||
| @@ -185,26 +183,6 @@ public class Ant extends Task { | |||||
| } | } | ||||
| } | } | ||||
| // Hashtable taskdefs = project.getTaskDefinitions(); | |||||
| // Enumeration et = taskdefs.keys(); | |||||
| // while (et.hasMoreElements()) { | |||||
| // String taskName = (String) et.nextElement(); | |||||
| // if (taskName.equals("property")) { | |||||
| // // we have already added this taskdef in #init | |||||
| // continue; | |||||
| // } | |||||
| // Class taskClass = (Class) taskdefs.get(taskName); | |||||
| // newProject.addTaskDefinition(taskName, taskClass); | |||||
| // } | |||||
| // Hashtable typedefs = project.getDataTypeDefinitions(); | |||||
| // Enumeration e = typedefs.keys(); | |||||
| // while (e.hasMoreElements()) { | |||||
| // String typeName = (String) e.nextElement(); | |||||
| // Class typeClass = (Class) typedefs.get(typeName); | |||||
| // newProject.addDataTypeDefinition(typeName, typeClass); | |||||
| // } | |||||
| // set user-defined or all properties from calling project | // set user-defined or all properties from calling project | ||||
| Hashtable prop1; | Hashtable prop1; | ||||
| if (inheritAll) { | if (inheritAll) { | ||||
| @@ -418,7 +418,7 @@ public class Antlib extends Task { | |||||
| if (classpath != null) { | if (classpath != null) { | ||||
| clspath.append(classpath); | clspath.append(classpath); | ||||
| } | } | ||||
| return project.getSymbols().addToLoader(loaderId, clspath); | |||||
| return project.addToLoader(loaderId, clspath); | |||||
| } | } | ||||
| @@ -505,8 +505,6 @@ public class Antlib extends Task { | |||||
| private int level = 0; | private int level = 0; | ||||
| private SymbolTable symbols = null; | |||||
| private String name = null; | private String name = null; | ||||
| private String className = null; | private String className = null; | ||||
| private String adapter = null; | private String adapter = null; | ||||
| @@ -520,7 +518,6 @@ public class Antlib extends Task { | |||||
| AntLibraryHandler(ClassLoader classloader, Properties als) { | AntLibraryHandler(ClassLoader classloader, Properties als) { | ||||
| this.classloader = classloader; | this.classloader = classloader; | ||||
| this.aliasMap = als; | this.aliasMap = als; | ||||
| this.symbols = project.getSymbols(); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -591,15 +588,15 @@ public class Antlib extends Task { | |||||
| try { | try { | ||||
| if ("role".equals(tag)) { | if ("role".equals(tag)) { | ||||
| if (isRoleInUse(name)) { | |||||
| if (project.isRoleDefined(name)) { | |||||
| String msg = "Cannot override role: " + name; | String msg = "Cannot override role: " + name; | ||||
| log(msg, Project.MSG_WARN); | log(msg, Project.MSG_WARN); | ||||
| return; | return; | ||||
| } | } | ||||
| // Defining a new role | // Defining a new role | ||||
| symbols.addRole(name, loadClass(className), | |||||
| (adapter == null? | |||||
| null : loadClass(adapter))); | |||||
| project.addRoleDefinition(name, loadClass(className), | |||||
| (adapter == null? | |||||
| null : loadClass(adapter))); | |||||
| return; | return; | ||||
| } | } | ||||
| @@ -610,12 +607,12 @@ public class Antlib extends Task { | |||||
| name = alias; | name = alias; | ||||
| } | } | ||||
| //catch an attempted override of an existing name | //catch an attempted override of an existing name | ||||
| if (!override && isInUse(tag, name)) { | |||||
| if (!override && project.isDefinedOnRole(tag, name)) { | |||||
| String msg = "Cannot override " + tag + ": " + name; | String msg = "Cannot override " + tag + ": " + name; | ||||
| log(msg, Project.MSG_WARN); | log(msg, Project.MSG_WARN); | ||||
| return; | return; | ||||
| } | } | ||||
| symbols.add(tag, name, loadClass(className)); | |||||
| project.addDefinitionOnRole(tag, name, loadClass(className)); | |||||
| } | } | ||||
| catch(BuildException be) { | catch(BuildException be) { | ||||
| throw new SAXParseException(be.getMessage(), locator, be); | throw new SAXParseException(be.getMessage(), locator, be); | ||||
| @@ -651,26 +648,6 @@ public class Antlib extends Task { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * test for a name being in use already on this role | |||||
| * | |||||
| * @param name the name to test | |||||
| * @return true if it is a task or a datatype | |||||
| */ | |||||
| private boolean isInUse(String role, String name) { | |||||
| return (symbols.get(role, name) != null); | |||||
| } | |||||
| /** | |||||
| * test for a role name being in use already | |||||
| * | |||||
| * @param name the name to test | |||||
| * @return true if it is a task or a datatype | |||||
| */ | |||||
| private boolean isRoleInUse(String name) { | |||||
| return (symbols.getRole(name) != null); | |||||
| } | |||||
| //end inner class AntLibraryHandler | //end inner class AntLibraryHandler | ||||
| } | } | ||||
| @@ -66,6 +66,7 @@ import org.apache.tools.ant.*; | |||||
| public class DataTypeAdapterTask extends Task implements RoleAdapter { | public class DataTypeAdapterTask extends Task implements RoleAdapter { | ||||
| Object proxy; | Object proxy; | ||||
| String id = null; | |||||
| /** | /** | ||||
| * Checks a class, whether it is suitable to be adapted. | * Checks a class, whether it is suitable to be adapted. | ||||
| @@ -83,14 +84,27 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter { | |||||
| * Do the execution. | * Do the execution. | ||||
| */ | */ | ||||
| public void execute() throws BuildException { | public void execute() throws BuildException { | ||||
| if (id != null) { | |||||
| // Need to re-register this reference | |||||
| // The container has register the Adapter instead | |||||
| project.addReference(id, proxy); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Propagate configuration of Project | |||||
| */ | |||||
| public void setProject(Project p) { | |||||
| super.setProject(p); | |||||
| // Check to see if the DataType has a setProject method to set | // Check to see if the DataType has a setProject method to set | ||||
| if (proxy instanceof ProjectComponent) { | if (proxy instanceof ProjectComponent) { | ||||
| ((ProjectComponent)proxy).setProject(project); | |||||
| ((ProjectComponent)proxy).setProject(p); | |||||
| return; | return; | ||||
| } | } | ||||
| // This may not be needed | // This may not be needed | ||||
| // We are trying to set project even it is was not declared | |||||
| // We are trying to set project even if is was not declared | |||||
| // just like TaskAdapter does for beans, this is not done | // just like TaskAdapter does for beans, this is not done | ||||
| // by the original code | // by the original code | ||||
| Method setProjectM = null; | Method setProjectM = null; | ||||
| @@ -99,7 +113,7 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter { | |||||
| setProjectM = | setProjectM = | ||||
| c.getMethod( "setProject", new Class[] {Project.class}); | c.getMethod( "setProject", new Class[] {Project.class}); | ||||
| if(setProjectM != null) { | if(setProjectM != null) { | ||||
| setProjectM.invoke(proxy, new Object[] {project}); | |||||
| setProjectM.invoke(proxy, new Object[] {p}); | |||||
| } | } | ||||
| } catch (NoSuchMethodException e) { | } catch (NoSuchMethodException e) { | ||||
| // ignore this if the class being used as a task does not have | // ignore this if the class being used as a task does not have | ||||
| @@ -122,4 +136,8 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter { | |||||
| return this.proxy ; | return this.proxy ; | ||||
| } | } | ||||
| public void setId(String id) { | |||||
| log("Setting adapter id to: " + id, Project.MSG_DEBUG); | |||||
| this.id = id; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project name="local" default="libs" > | |||||
| <taskdef name="antjar" classname="org.apache.tools.ant.taskdefs.Antjar" /> | |||||
| <property name="src" location="." /> | |||||
| <property name="classes" location="../../build/testcases" /> | |||||
| <property name="contrib" location="../../build/case_contrib.jar" /> | |||||
| <target name="libs" depends="compile" > | |||||
| <antjar destfile="${contrib}" antxml="${src}/case-antlib.xml" > | |||||
| <fileset dir="${classes}" > | |||||
| <include name="org/**" /> | |||||
| </fileset> | |||||
| </antjar> | |||||
| </target> | |||||
| <target name="compile" > | |||||
| <mkdir dir="${classes}" /> | |||||
| <javac srcdir="${src}" destdir="${classes}" > | |||||
| <include name="org/**/*.java" /> | |||||
| </javac> | |||||
| </target> | |||||
| <target name="clean" > | |||||
| <delete dir="${classes}" /> | |||||
| <delete file='${contrib}'/> | |||||
| </target> | |||||
| </project> | |||||
| @@ -0,0 +1,5 @@ | |||||
| <?xml version="1.0" ?> | |||||
| <antlib version="1.0" > | |||||
| <task name="case" class="org.apache.ant.contrib.Case" /> | |||||
| </antlib> | |||||
| @@ -0,0 +1,39 @@ | |||||
| <?xml version="1.0"?> | |||||
| <project name="case-test" default="test" basedir="."> | |||||
| <property name="value" value="task.xml" /> | |||||
| <target name="init"> | |||||
| <taskdef name="antlib" classname="org.apache.tools.ant.taskdefs.Antlib" /> | |||||
| <antlib file="../../build/case_contrib.jar" /> | |||||
| </target> | |||||
| <target name="test" depends="init,case,test1,test2,test3"> | |||||
| <echo message="Value=${value}" /> | |||||
| </target> | |||||
| <target name="case" > | |||||
| <case property="value" > | |||||
| <when value="task.xml" property="value.xml" /> | |||||
| </case> | |||||
| <case property="location" > | |||||
| <when value="loc" property="location.fail" /> | |||||
| <when value="" property="location.fail" /> | |||||
| <else property="location.unset" /> | |||||
| </case> | |||||
| </target> | |||||
| <target name="test1" if="value.xml"> | |||||
| <echo message="Value equals to itself" /> | |||||
| </target> | |||||
| <target name="test2" if="location.fail"> | |||||
| <fail message="Location passed" /> | |||||
| </target> | |||||
| <target name="test3" if="location.unset"> | |||||
| <echo message="Location does not exists" /> | |||||
| </target> | |||||
| </project> | |||||
| @@ -0,0 +1,169 @@ | |||||
| /* | |||||
| * The Apache Software License, Version 1.1 | |||||
| * | |||||
| * Copyright (c) 1999 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.ant.contrib; | |||||
| import java.io.*; | |||||
| import java.util.*; | |||||
| import org.apache.tools.ant.*; | |||||
| import org.apache.tools.ant.types.*; | |||||
| /** | |||||
| * Will set one of the given properties depending on the result of testing | |||||
| * the value of another property. | |||||
| * | |||||
| * <!ELEMENT case (when*, else) > | |||||
| * <!ATTLIST case property CDATA #REQUIRED > The name of the property to test | |||||
| * <!ELEMENT when EMPTY > | |||||
| * <!ATTLIST when value CDATA #REQUIRED > The value to compare and set prop. | |||||
| * <!ATTLIST when property CDATA #REQUIRED > The name of the property to set | |||||
| * <!ELEMENT else EMPTY > | |||||
| * <!ATTLIST else property CDATA #REQUIRED > The name of the property to set otherwise | |||||
| * <!ATTLIST else value CDATA #IMPLIED > The value to set; default "true". | |||||
| * | |||||
| * @author Jose Alberto Fernandez <a href="mailto:jfernandez@viquity.com">jfernandez@viquity.com</a> | |||||
| */ | |||||
| public class Case extends Task { | |||||
| public class When { | |||||
| private String property; | |||||
| private String value; | |||||
| public void setProperty(String name) { | |||||
| property = name; | |||||
| } | |||||
| public String getProperty() { | |||||
| return property; | |||||
| } | |||||
| public void setValue(String val) { | |||||
| value = val; | |||||
| } | |||||
| public String getValue() { | |||||
| return value; | |||||
| } | |||||
| public boolean tryCase(String caseValue) throws BuildException { | |||||
| if (property == null) | |||||
| throw new BuildException("Property attribute is mandatory"); | |||||
| if (value == null) | |||||
| throw new BuildException("Value attribute is mandatory"); | |||||
| if (!value.equals(caseValue)) return false; | |||||
| if (getProject().getProperty(property) == null) { | |||||
| getProject().setProperty(property, value); | |||||
| } else { | |||||
| log("Override ignored for " + property, Project.MSG_VERBOSE); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| public void doElse() throws BuildException { | |||||
| if (property == null) | |||||
| throw new BuildException("Property attribute is mandatory"); | |||||
| String elseValue = (value == null) ? "true" : value; | |||||
| if (getProject().getProperty(property) == null) { | |||||
| getProject().setProperty(property, elseValue); | |||||
| } else { | |||||
| log("Override ignored for " + property, Project.MSG_VERBOSE); | |||||
| } | |||||
| } | |||||
| } | |||||
| private String caseProperty; | |||||
| private Vector whenList = new Vector(); | |||||
| private When elseCase = null; | |||||
| public When createWhen() throws BuildException { | |||||
| When w = new When(); | |||||
| whenList.addElement(w); | |||||
| return w; | |||||
| } | |||||
| public When createElse() throws BuildException { | |||||
| if (elseCase != null) | |||||
| throw new BuildException("Only one else element allowed per case"); | |||||
| return (elseCase = new When()); | |||||
| } | |||||
| public void setProperty(String property) { | |||||
| this.caseProperty = property; | |||||
| } | |||||
| public void execute() throws BuildException { | |||||
| if (caseProperty == null) { | |||||
| throw new BuildException("property attribute is required", | |||||
| location); | |||||
| } | |||||
| String caseValue = getProject().getProperty(caseProperty); | |||||
| for (Enumeration e = whenList.elements(); e.hasMoreElements(); ) { | |||||
| When w = (When)e.nextElement(); | |||||
| if (w.tryCase(caseValue)) return; | |||||
| } | |||||
| if (elseCase != null) | |||||
| elseCase.doElse(); | |||||
| } | |||||
| } | |||||