diff --git a/WHATSNEW b/WHATSNEW index ebb4c7063..f93945a1f 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -30,6 +30,9 @@ Other changes: * Changed default tempdir for from user.dir to java.io.tmpdir. +* A new base class DispatchTask has been added to facilitate elegant + creation of tasks with multiple actions. + Changes from Ant 1.6.1 to current Ant 1.6 CVS version ===================================================== diff --git a/docs/manual/base_task_classes.html b/docs/manual/base_task_classes.html index 42b92da9a..39c058120 100644 --- a/docs/manual/base_task_classes.html +++ b/docs/manual/base_task_classes.html @@ -76,6 +76,15 @@ Abstract Base class for unpack tasks. + + +DispatchTask + + +Abstract Base class for tasks that may have multiple actions. + + + diff --git a/src/etc/testcases/core/dispatch/dispatch.xml b/src/etc/testcases/core/dispatch/dispatch.xml new file mode 100644 index 000000000..a46793de6 --- /dev/null +++ b/src/etc/testcases/core/dispatch/dispatch.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/TaskAdapter.java b/src/main/org/apache/tools/ant/TaskAdapter.java index 23d457972..99b392393 100644 --- a/src/main/org/apache/tools/ant/TaskAdapter.java +++ b/src/main/org/apache/tools/ant/TaskAdapter.java @@ -17,7 +17,9 @@ package org.apache.tools.ant; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.apache.tools.ant.dispatch.Dispatchable; /** * Uses introspection to "adapt" an arbitrary Bean which doesn't @@ -32,6 +34,9 @@ public class TaskAdapter extends Task implements TypeAdapter { /** * Checks whether or not a class is suitable to be adapted by TaskAdapter. + * If the class is of type Dispatchable, the check is not performed because + * the method that will be executed will be determined only at runtime of + * the actual task and not during parse time. * * This only checks conditions which are additionally required for * tasks adapted by TaskAdapter. Thus, this method should be called by @@ -50,28 +55,30 @@ public class TaskAdapter extends Task implements TypeAdapter { */ public static void checkTaskClass(final Class taskClass, final Project project) { - // don't have to check for interface, since then - // taskClass would be abstract too. - try { - final Method executeM = taskClass.getMethod("execute", null); - // don't have to check for public, since - // getMethod finds public method only. - // don't have to check for abstract, since then + if (!Dispatchable.class.isAssignableFrom(taskClass)) { + // don't have to check for interface, since then // taskClass would be abstract too. - if (!Void.TYPE.equals(executeM.getReturnType())) { - final String message = "return type of execute() should be " - + "void but was \"" + executeM.getReturnType() + "\" in " - + taskClass; - project.log(message, Project.MSG_WARN); + try { + final Method executeM = taskClass.getMethod("execute", null); + // don't have to check for public, since + // getMethod finds public method only. + // don't have to check for abstract, since then + // taskClass would be abstract too. + if (!Void.TYPE.equals(executeM.getReturnType())) { + final String message = "return type of execute() should be " + + "void but was \"" + executeM.getReturnType() + "\" in " + + taskClass; + project.log(message, Project.MSG_WARN); + } + } catch (NoSuchMethodException e) { + final String message = "No public execute() in " + taskClass; + project.log(message, Project.MSG_ERR); + throw new BuildException(message); + } catch (LinkageError e) { + String message = "Could not load " + taskClass + ": " + e; + project.log(message, Project.MSG_ERR); + throw new BuildException(message, e); } - } catch (NoSuchMethodException e) { - final String message = "No public execute() in " + taskClass; - project.log(message, Project.MSG_ERR); - throw new BuildException(message); - } catch (LinkageError e) { - String message = "Could not load " + taskClass + ": " + e; - project.log(message, Project.MSG_ERR); - throw new BuildException(message, e); } } @@ -85,6 +92,37 @@ public class TaskAdapter extends Task implements TypeAdapter { checkTaskClass(proxyClass, getProject()); } + /** + * Returns the name of the action method that the task must + * execute. + */ + private final String getExecuteMethodName() throws NoSuchMethodException, + InvocationTargetException, IllegalAccessException { + String methodName = "execute"; + if (proxy instanceof Dispatchable) { + final Dispatchable dispatchable = (Dispatchable) proxy; + final String name = dispatchable.getActionParameterName(); + if (name != null && name.trim().length() > 0) { + String mName = "get" + name.trim().substring(0, 1).toUpperCase(); + if (name.length() > 1) { + mName += name.substring(1); + } + final Class c = proxy.getClass(); + final Method actionM = c.getMethod(mName, new Class[0]); + if (actionM != null) { + final Object o = actionM.invoke(proxy, null); + if (o != null) { + final String s = o.toString(); + if (s != null && s.trim().length() > 0) { + methodName = s.trim(); + } + } + } + } + } + return methodName; + } + /** * Executes the proxied task. * @@ -113,11 +151,12 @@ public class TaskAdapter extends Task implements TypeAdapter { Method executeM = null; try { Class c = proxy.getClass(); - executeM = c.getMethod("execute", new Class[0]); + final String methodName = getExecuteMethodName(); + executeM = c.getMethod(methodName, new Class[0]); if (executeM == null) { - log("No public execute() in " + proxy.getClass(), + log("No public " + methodName + " in " + proxy.getClass(), Project.MSG_ERR); - throw new BuildException("No public execute() in " + throw new BuildException("No public " + methodName + "() in " + proxy.getClass()); } executeM.invoke(proxy, null); diff --git a/src/main/org/apache/tools/ant/dispatch/DispatchTask.java b/src/main/org/apache/tools/ant/dispatch/DispatchTask.java new file mode 100644 index 000000000..e019b5ab4 --- /dev/null +++ b/src/main/org/apache/tools/ant/dispatch/DispatchTask.java @@ -0,0 +1,46 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.dispatch; + +import org.apache.tools.ant.Task; + +/** + * Tasks extending this class may contain multiple actions. + * The method that is invoked for executoin depends upon the + * value of the action attribute of the task. + *
+ * Example:
+ * <mytask action="list"/> will invoke the method + * with the signature public void list() in mytask's class. + * If the action attribute is not defined in the task or is empty, + * the execute() method will be called. + */ +public abstract class DispatchTask implements Dispatchable { + private String action; + + public String getActionParameterName() { + return "action"; + } + + public void setAction(String action) { + this.action = action; + } + + public String getAction() { + return action; + } +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/dispatch/Dispatchable.java b/src/main/org/apache/tools/ant/dispatch/Dispatchable.java new file mode 100644 index 000000000..7a082ce61 --- /dev/null +++ b/src/main/org/apache/tools/ant/dispatch/Dispatchable.java @@ -0,0 +1,26 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.dispatch; + +/** + * Classes implementing this interface specify the + * name of the parameter that contains the name + * of the task's method to execute. + */ +public interface Dispatchable { + public String getActionParameterName(); +} \ No newline at end of file diff --git a/src/testcases/org/apache/tools/ant/DispatchTaskTest.java b/src/testcases/org/apache/tools/ant/DispatchTaskTest.java new file mode 100644 index 000000000..aff79137e --- /dev/null +++ b/src/testcases/org/apache/tools/ant/DispatchTaskTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant; + +import org.apache.tools.ant.BuildFileTest; + +public class DispatchTaskTest extends BuildFileTest { + + public DispatchTaskTest(String name) { + super(name); + } + + public void setUp() { + configureProject("src/etc/testcases/core/dispatch/dispatch.xml"); + } + + public void testDisp() { + expectBuildException("disp", "list"); + } +} diff --git a/src/testcases/org/apache/tools/ant/PickOneTask.java b/src/testcases/org/apache/tools/ant/PickOneTask.java new file mode 100644 index 000000000..76b9f6559 --- /dev/null +++ b/src/testcases/org/apache/tools/ant/PickOneTask.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant; + +import org.apache.tools.ant.dispatch.DispatchTask; + +public class PickOneTask extends DispatchTask { + public void list() { + throw new BuildException("list"); + } + + public void show() { + throw new BuildException("show"); + } +}