diff --git a/docs/manual/CoreTasks/typedef.html b/docs/manual/CoreTasks/typedef.html index 9b439065e..c2238f736 100644 --- a/docs/manual/CoreTasks/typedef.html +++ b/docs/manual/CoreTasks/typedef.html @@ -9,23 +9,43 @@

Typedef

Description

-

Adds a data type definition to the current project, such that this -new type can be used in the current project. Two attributes are -needed, the name that identifies this data type uniquely, and the full -name of the class (including the packages) that implements this -type.

-

You can also define a group of data types at once using the file or -resource attributes. These attributes point to files in the format of -Java property files. Each line defines a single data type in the -format:

-
-typename=fully.qualified.java.classname
-
-

Typedef should be used to add your own tasks and types to the system. Data -types are things like paths or filesets that can be defined at -the project level and referenced via their ID attribute.

-

Custom data types usually need custom tasks to put them to good use.

+

+ Adds a task or a data type definition to the current project + such that this new type or task can be used in the current project. +

+

+ Tasks are any class that extend org.apache.tools.ant.Task or + a class that is adapted to a Task using an adapter class. +

+

+ Data types are things like paths or + filesets that can be defined at + the project level and referenced via their ID attribute. + Custom data types usually need custom tasks to put them to good use. +

+

+ Two attributes are needed to make a definition, + the name that identifies this data type uniquely, and the full + name of the class (including the packages) that implements this + type. +

+

+ You can also define a group of definitions at once using the file or + resource attributes. These attributes point to files in the format of + Java property files or an xml format. +

+

+ For property files each line defines a single data type in the + format:

+
+    typename=fully.qualified.java.classname
+  
+ +

+ The xml format is described below in the Antlib + section. +

+

Parameters

@@ -55,6 +75,19 @@ the project level and referenced via their ID attribute.

+ + + + +
Name of the resouce to load definitions from. No
formatThe format of the file or resource. The values + are "properties" or "xml". If the value is "properties" the file/resource + is a property file contains name to classname pairs. If the value + is "xml", the file/resource is an xml file/resource structured according + to Antlib. + The default is "properties" unless the file/resorce name ends with + ".xml", in which case the format attribute will have the value "xml". + (introduced in ant1.6) + No
classpath the classpath to use when looking up classname. No
-

Parameters specified as nested elements

-

classpath

-

Typedef's classpath attribute is a -PATH like structure and can also be set -via a nested classpath element.

+

Parameters specified as nested elements

+

classpath

+

Typedef's classpath attribute is a + PATH like structure and can also be set + via a nested classpath element.

+

Examples

-
  <typedef name="urlset" classname="com.mydomain.URLSet"/>
-

makes a data type called urlset available to Ant. The -class com.mydomain.URLSet implements this type.

+ The following fragment defines define a type called urlset. +
+    <typedef name="urlset" classname="com.mydomain.URLSet"/> 
+ The data type is now availabe to Ant. The + class com.mydomain.URLSet implements this type.

+

+ Assuming a class org.acme.ant.RunnableAdapter that + extends Task and implements org.apache.tools.ant.TypeAdapter, + and in the execute method invokes run on the proxied object, + one may use a Runnable class as an Ant task. The following fragment + defines a task called runclock. +

+    <typedef name="runclock"
+             classname="com.acme.ant.RunClock"
+             adapter="org.acme.ant.RunnableAdapter"/>
+  
+

Antlib xml format

+ An antlib file is an xml file with a root element of "antlib". + Antlib is actually a Sequential task with + special treatment for tasks that are ant definition tasks - like typedef + and Taskdef. + + A group of tasks and types may be defined together in an antlib + file. For example the file sample.xml contains the following: +
+    <?xml version="1.0"?>
+    <antlib>
+      <typedef name="if" classname="org.acme.ant.If"/>
+      <typedef name="scriptpathmapper"
+               classname="org.acme.ant.ScriptPathMapper"
+               onerror="ignore"/>
+    </antlib>
+  
+ It defines two types or tasks, if and scriptpathmapper. + This antlib file may be used in a build script as follows: +
+      <typedef file="sample.xml"/>
+    

Copyright © 2001-2003 Apache Software diff --git a/src/etc/testcases/taskdefs/antlib.xml b/src/etc/testcases/taskdefs/antlib.xml new file mode 100644 index 000000000..b23023fa4 --- /dev/null +++ b/src/etc/testcases/taskdefs/antlib.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/etc/testcases/taskdefs/test.antlib.xml b/src/etc/testcases/taskdefs/test.antlib.xml new file mode 100644 index 000000000..a2760a076 --- /dev/null +++ b/src/etc/testcases/taskdefs/test.antlib.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java index 3d1d94e5b..8b870809e 100644 --- a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java +++ b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java @@ -57,8 +57,10 @@ package org.apache.tools.ant.helper; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.InputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URL; import java.util.Hashtable; import java.util.Stack; @@ -75,6 +77,7 @@ import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Location; @@ -102,6 +105,31 @@ public class ProjectHelper2 extends ProjectHelper { */ private static FileUtils fu = FileUtils.newFileUtils(); + /** + * Parse an unknown element from a url + * + * @param project the current project + * @param source the url containing the task + * @return a configured task + * @exception BuildException if an error occurs + */ + public UnknownElement parseUnknownElement(Project project, URL source) + throws BuildException { + Target dummyTarget = new Target(); + dummyTarget.setProject(project); + + AntXMLContext context = new AntXMLContext(project); + context.addTarget(dummyTarget); + context.setImplicitTarget(dummyTarget); + + parse(context.getProject(), source, + new RootHandler(context, elementHandler)); + Task[] tasks = dummyTarget.getTasks(); + if (tasks.length != 1) { + throw new BuildException("No tasks defined"); + } + return (UnknownElement) tasks[0]; + } /** * Parse a source xml input. * @@ -127,11 +155,11 @@ public class ProjectHelper2 extends ProjectHelper { // we are in an imported file. context.setIgnoreProjectTag(true); context.getCurrentTarget().startImportedTasks(); - parse(project, source, new RootHandler(context)); + parse(project, source, new RootHandler(context, mainHandler)); context.getCurrentTarget().endImportedTasks(); } else { // top level file - parse(project, source, new RootHandler(context)); + parse(project, source, new RootHandler(context, mainHandler)); // Execute the top-level target context.getImplicitTarget().execute(); } @@ -152,22 +180,33 @@ public class ProjectHelper2 extends ProjectHelper { AntXMLContext context = handler.context; File buildFile = null; + URL url = null; + String buildFileName = null; if (source instanceof File) { buildFile = (File) source; -// } else if (source instanceof InputStream) { -// } else if (source instanceof URL) { -// } else if (source instanceof InputSource) { + buildFile = new File(buildFile.getAbsolutePath()); + context.setBuildFile(buildFile); + buildFileName = buildFile.toString(); +// } else if (source instanceof InputStream ) { + } else if (source instanceof URL) { + if (handler.getCurrentAntHandler() != elementHandler) { + throw new BuildException( + "Source " + source.getClass().getName() + + " not supported by this plugin for " + + " non task xml"); + } + url = (URL) source; + buildFileName = url.toString(); +// } else if (source instanceof InputSource ) { } else { throw new BuildException("Source " + source.getClass().getName() - + " not supported by this plugin"); + + " not supported by this plugin"); } - FileInputStream inputStream = null; + InputStream inputStream = null; InputSource inputSource = null; - buildFile = new File(buildFile.getAbsolutePath()); - context.setBuildFile(buildFile); try { /** @@ -175,13 +214,21 @@ public class ProjectHelper2 extends ProjectHelper { */ XMLReader parser = JAXPUtils.getNamespaceXMLReader(); - String uri = fu.toURI(buildFile.getAbsolutePath()); + String uri = null; + if (buildFile != null) { + uri = fu.toURI(buildFile.getAbsolutePath()); + inputStream = new FileInputStream(buildFile); + } else { + inputStream = url.openStream(); + uri = url.toString(); // ?? OK ?? + } - inputStream = new FileInputStream(buildFile); inputSource = new InputSource(inputStream); - inputSource.setSystemId(uri); - project.log("parsing buildfile " + buildFile - + " with URI = " + uri, Project.MSG_VERBOSE); + if (uri != null) { + inputSource.setSystemId(uri); + } + project.log("parsing buildfile " + buildFileName + + "with URI = " + uri, Project.MSG_VERBOSE); DefaultHandler hb = handler; @@ -357,13 +404,22 @@ public class ProjectHelper2 extends ProjectHelper { * Creates a new RootHandler instance. * * @param context The context for the handler. + * @param rootHandler The handler for the root element. */ - public RootHandler(AntXMLContext context) { - currentHandler = ProjectHelper2.mainHandler; + public RootHandler(AntXMLContext context, AntHandler rootHandler) { + currentHandler = rootHandler; antHandlers.push(currentHandler); this.context = context; } + /** + * Returns the current ant handler object. + * @return the current ant handler. + */ + public AntHandler getCurrentAntHandler() { + return currentHandler; + } + /** * Resolves file: URIs relative to the build file. * diff --git a/src/main/org/apache/tools/ant/taskdefs/Antlib.java b/src/main/org/apache/tools/ant/taskdefs/Antlib.java new file mode 100644 index 000000000..c96b31355 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/Antlib.java @@ -0,0 +1,190 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 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 "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 + * . + */ + +package org.apache.tools.ant.taskdefs; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.tools.ant.TaskContainer; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.helper.ProjectHelper2; +import org.apache.tools.ant.UnknownElement; + + +/** + * Antlib task. + * + * @author Peter Reilly + * + * @since Ant 1.6 + */ +public class Antlib extends Task implements TaskContainer { + // + // Static + // + + /** The name of this task */ + public static final String TAG = "antlib"; + + /** + * Static method to read an ant lib definition from + * a url. + * + * @param project the current project + * @param antlibUrl the url to read the definitions from + * @return the ant lib task + */ + public static Antlib createAntlib(Project project, URL antlibUrl) { + // Check if we can contact the URL + try { + antlibUrl.openConnection().connect(); + } catch (IOException ex) { + throw new BuildException( + "Unable to find " + antlibUrl, ex); + } + // Should be safe to parse + try { + ProjectHelper2 parser = new ProjectHelper2(); + UnknownElement ue = + parser.parseUnknownElement(project, antlibUrl); + // Check name is "antlib" + if (!(ue.getTag().equals(TAG))) { + throw new BuildException( + "Unexpected tag " + ue.getTag() + " expecting " + + TAG, ue.getLocation()); + } + Antlib antlib = new Antlib(); + antlib.setProject(project); + antlib.setLocation(ue.getLocation()); + antlib.init(); + ue.configure(antlib); + return antlib; + } catch (BuildException ex) { + Location location = ex.getLocation(); + if (location == null) { + throw ex; + } + throw new BuildException( + "Error in " + + System.getProperty("line.separator") + + location.toString() + + " " + ex.getMessage()); + } + } + + + // + // Instance + // + private ClassLoader classLoader; + private String prefix; + private List tasks = new ArrayList(); + + /** + * Set the class loader for this antlib. + * This class loader is used for any tasks that + * derive from Definer. + * + * @param classLoader the class loader + */ + protected void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + private ClassLoader getClassLoader() { + if (classLoader == null) { + classLoader = Antlib.class.getClassLoader(); + } + return classLoader; + } + + /** + * add a task to the list of tasks + * + * @param nestedTask Nested task to execute in antlibe + */ + public void addTask(Task nestedTask) { + tasks.add(nestedTask); + } + + /** + * Execute the nested tasks, setting the classloader for + * any tasks that derive from Definer. + */ + public void execute() { + for (Iterator i = tasks.iterator(); i.hasNext();) { + UnknownElement ue = (UnknownElement) i.next(); + ue.maybeConfigure(); + Task t = ue.getTask(); + if (t == null) { + continue; + } + if (t instanceof Definer) { + Definer d = (Definer) t; + d.setInternalClassLoader(getClassLoader()); + } + t.init(); + t.execute(); + } + } + +} diff --git a/src/main/org/apache/tools/ant/taskdefs/Definer.java b/src/main/org/apache/tools/ant/taskdefs/Definer.java index 67f519d9b..c7ba90e3c 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Definer.java +++ b/src/main/org/apache/tools/ant/taskdefs/Definer.java @@ -59,12 +59,14 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; +import java.util.Locale; import java.util.Properties; import org.apache.tools.ant.AntTypeDefinition; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; @@ -88,6 +90,7 @@ public abstract class Definer extends Task { private String resource; private ClasspathUtils.Delegate cpDelegate; + private int format = Format.PROPERTIES; private boolean definerSet = false; private ClassLoader internalClassLoader; private int onError = OnError.FAIL; @@ -129,6 +132,24 @@ public abstract class Definer extends Task { } } + /** + * Enumerated type for format attribute + * + * @see EnumeratedAttribute + */ + public static class Format extends EnumeratedAttribute { + /** Enumerated values */ + public static final int PROPERTIES = 0, XML = 1; + + /** + * get the values + * @return an array of the allowed values for this attribute. + */ + public String[] getValues() { + return new String[] {"properties", "xml"}; + } + } + /** * What to do if there is an error in loading the class. *

@@ -143,6 +164,14 @@ public abstract class Definer extends Task { this.onError = onError.getIndex(); } + /** + * Sets the format of the file or resource + * @param format the enumerated value - xml or properties + */ + public void setFormat(Format format) { + this.format = format.getIndex(); + } + /** * @param reverseLoader if true a delegated loader will take precedence over * the parent @@ -289,7 +318,15 @@ public abstract class Definer extends Task { return; } - loadProperties(al, url); + if (url.toString().toLowerCase(Locale.US).endsWith(".xml")) { + format = Format.XML; + } + + if (format == Format.PROPERTIES) { + loadProperties(al, url); + } else { + loadAntlib(al, url); + } } } @@ -359,6 +396,30 @@ public abstract class Definer extends Task { } } + /** + * Load an antlib from a url. + * + * @param classLoader the classloader to use. + * @param url the url to load the definitions from. + */ + private void loadAntlib(ClassLoader classLoader, URL url) { + try { + Antlib antlib = Antlib.createAntlib(getProject(), url); + antlib.setClassLoader(classLoader); + antlib.perform(); + } catch (BuildException ex) { + Location location = ex.getLocation(); + if (location == null) { + throw ex; + } + throw new BuildException( + "Error in " + + System.getProperty("line.separator") + + getLocation().toString() + + " " + ex.getMessage()); + } + } + /** * create a classloader for this definition * @return the classloader from the cpDelegate @@ -543,7 +604,7 @@ public abstract class Definer extends Task { } catch (NoClassDefFoundError ncdfe) { String msg = getTaskName() + " A class needed by class " + classname + " cannot be found: " + ncdfe.getMessage(); - throw new BuildException(msg, ncdfe, location); + throw new BuildException(msg, ncdfe, getLocation()); } } catch (BuildException ex) { switch (onError) { diff --git a/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java b/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java new file mode 100644 index 000000000..87ac94777 --- /dev/null +++ b/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java @@ -0,0 +1,84 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 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 "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 + * . + */ + +package org.apache.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildFileTest; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * @author Peter Reilly + */ +public class AntlibTest extends BuildFileTest { + public AntlibTest(String name) { + super(name); + } + + public void setUp() { + configureProject("src/etc/testcases/taskdefs/antlib.xml"); + } + + public void testAntlibFile() { + expectLog("antlib.file", "MyTask called"); + } + + public static class MyTask extends Task { + public void execute() { + log("MyTask called"); + } + } + +} +