Author: jhm
Date: Thu Oct 29 13:18:16 2009
New Revision: 830928
URL: http://svn.apache.org/viewvc?rev=830928&view=rev
Log:
Task <autoconf> for discussion
Added:
ant/sandbox/autoconf/
ant/sandbox/autoconf/build.properties
ant/sandbox/autoconf/build.xml
ant/sandbox/autoconf/docs/
ant/sandbox/autoconf/docs/autoconf.html
ant/sandbox/autoconf/src/
ant/sandbox/autoconf/src/antunit/
ant/sandbox/autoconf/src/antunit/autoconf-test.xml
ant/sandbox/autoconf/src/main/
ant/sandbox/autoconf/src/main/org/
ant/sandbox/autoconf/src/main/org/apache/
ant/sandbox/autoconf/src/main/org/apache/tools/
ant/sandbox/autoconf/src/main/org/apache/tools/ant/
ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/
ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/AutoconfTask.java
ant/sandbox/autoconf/test.xml
Added: ant/sandbox/autoconf/build.properties
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/build.properties?rev=830928&view=auto
==============================================================================
--- ant/sandbox/autoconf/build.properties (added)
+++ ant/sandbox/autoconf/build.properties Thu Oct 29 13:18:16 2009
@@ -0,0 +1,4 @@
+build.dir=build
+src.dir=src/main
+
+classes.dir=${build.dir}/classes
Added: ant/sandbox/autoconf/build.xml
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/build.xml?rev=830928&view=auto
==============================================================================
--- ant/sandbox/autoconf/build.xml (added)
+++ ant/sandbox/autoconf/build.xml Thu Oct 29 13:18:16 2009
@@ -0,0 +1,7 @@
+<project>
+ <property file="build.properties"/>
+ <mkdir dir="${classes.dir}"/>
+ <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
+
+ <ant antfile="test.xml"/>
+</project>
\ No newline at end of file
Added: ant/sandbox/autoconf/docs/autoconf.html
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/docs/autoconf.html?rev=830928&view=auto
==============================================================================
--- ant/sandbox/autoconf/docs/autoconf.html (added)
+++ ant/sandbox/autoconf/docs/autoconf.html Thu Oct 29 13:18:16 2009
@@ -0,0 +1,69 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Language" content="en-us">
+<title>Autoconf Task</title>
+</head>
+
+<body>
+
+<h2><a name="taskname">Autoconf</a></h2>
+<h3>Description</h3>
+
+<p>Starts or stops autoconfiguration for tasks and datatypes.
+Autoconfiguration applies properties to tasks and datatypes if
+their names fit to the tasks/datatype and attribute names and the
+attribute is not set.</p>
+
+<p>The property name must match the following rule:<br>
+<tt>propertyname ::= prefix? taskname "." attributename</tt></p>
+
+<p>Only one Autoconf is active at the same time.</p>
+
+
+<h3>Parameters</h3>
+<table border="1" cellpadding="2" cellspacing="0">
+ <tr>
+ <td valign="top"><b>Attribute</b></td>
+ <td valign="top"><b>Description</b></td>
+ <td align="center" valign="top"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td valign="top">start</td>
+ <td valign="top">'start' or 'stop' autoconfiguration</td>
+ <td align="center" valign="top">no, defaults to 'start'</td>
+ </tr>
+ <tr>
+ <td valign="top">prefix</td>
+ <td valign="top">Prefix to use for property names</td>
+ <td align="center" valign="top">no, empty as default</td>
+ </tr>
+</table>
+
+
+<h3>Examples</h3>
+<pre>
+<property name="echo.message" value="Default
Message"/>
+<autoconf/>
+<echo/>
+</pre>
+Echoes <tt>Default Message</tt>, because the name rule ("" + "echo" + "." +
"message")
+catches one existing property.
+
+<pre>
+<property name="echo.notAnAttribute" value="Default
Message"/>
+<autoconf/>
+<echo/>
+</pre>
+Echoes an empty line as the default behaviour of <echo/>. The message
+is not applied because the property name does not match the rule.
+
+<pre>
+<property name="echo.message" value="Default
Message"/>
+<autoconf/>
+<echo message="Individual Message"/>
+</pre>
+Autoconf does not do anything because the attribute of the echo task is
already set.
+
+</body>
+</html>
Added: ant/sandbox/autoconf/src/antunit/autoconf-test.xml
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/src/antunit/autoconf-test.xml?rev=830928&view=auto
==============================================================================
--- ant/sandbox/autoconf/src/antunit/autoconf-test.xml (added)
+++ ant/sandbox/autoconf/src/antunit/autoconf-test.xml Thu Oct 29 13:18:16 2009
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<project xmlns:au="antlib:org.apache.ant.antunit" default="antunit">
+ <import
file="C:/ant/svn-repository/ant/core/src/tests/antunit/antunit-base.xml"/>
+ <taskdef name="autoconf"
classname="org.apache.tools.ant.taskdefs.AutoconfTask"
classpath="../../build/classes"/>
+
+ <target name="testIgnoreNonAttributeValues">
+ <property name="echo.notAnAttribute" value="Default Message"/>
+ <autoconf/>
+ <echo/>
+ <au:assertLogDoesntContain text="Default Message"/>
+ </target>
+
+ <target name="testNoDefault">
+ <autoconf/>
+ <echo message="Individual Message"/>
+ <au:assertLogContains text="Individual Message"/>
+ </target>
+
+ <target name="testDirectValue">
+ <property name="echo.message" value="Default Message"/>
+ <autoconf/>
+ <echo message="Individual Message"/>
+ <au:assertLogContains text="Individual Message"/>
+ </target>
+
+ <target name="testDefault">
+ <property name="echo.message" value="Default Message"/>
+ <autoconf/>
+ <echo/>
+ <au:assertLogContains text="Default Message"/>
+ </target>
+
+ <target name="testPresetDef">
+ <property name="myecho.message" value="Default Message"/>
+ <presetdef name="myecho"><echo/></presetdef>
+ <autoconf/>
+ <myecho/>
+ <au:assertLogContains text="Default Message"/>
+ </target>
+
+ <target name="testPresetDefIndividualValue">
+ <property name="myecho.message" value="Default Message"/>
+ <presetdef name="myecho"><echo/></presetdef>
+ <autoconf/>
+ <myecho message="Individual Message"/>
+ <au:assertLogContains text="Individual Message"/>
+ </target>
+
+ <target name="testPresetDefButTasknameProperty">
+ <property name="myecho.message" value="Default Preset Message"/>
+ <property name="echo.message" value="Default Task Message"/>
+ <presetdef name="myecho"><echo/></presetdef>
+ <autoconf/>
+ <myecho/>
+ <au:assertLogContains text="Default Preset Message"/>
+ </target>
+
+ <target name="testMacroDef">
+ <property name="myecho.message" value="Default Message"/>
+ <macrodef name="myecho">
+ <attribute name="message"/>
+ <sequential>
+ <echo message="@{message}"/>
+ </sequential>
+ </macrodef>
+ <autoconf/>
+ <myecho/>
+ <au:assertLogContains text="Default Message"/>
+ </target>
+
+ <target name="testMacroDefIndividualValue">
+ <property name="myecho.message" value="Default Message"/>
+ <macrodef name="myecho">
+ <attribute name="message"/>
+ <sequential>
+ <echo message="@{message}"/>
+ </sequential>
+ </macrodef>
+ <autoconf/>
+ <myecho message="Individual Message"/>
+ <au:assertLogContains text="Individual Message"/>
+ </target>
+
+ <target name="testStartStop">
+ <property name="myecho.message" value="Autoconf Message"/>
+ <presetdef name="myecho"><echo message="Default Message"/></presetdef>
+ <myecho/>
+ <au:assertLogContains text="Default Message"/>
+ <autoconf/>
+ <myecho/>
+ <au:assertLogContains text="Autoconf Message"/>
+ <autoconf action="stop"/>
+ <myecho/>
+ <au:assertLogContains text="Default Message"/>
+ </target>
+
+ <target name="testPrefix">
+ <property name="echo.message" value="wrong message"/>
+ <property name="prefix.echo.message" value="correct message"/>
+ <autoconf prefix="prefix."/>
+ <echo/>
+ <au:assertLogContains text="correct message"/>
+ </target>
+</project>
\ No newline at end of file
Added:
ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/AutoconfTask.java
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/AutoconfTask.java?rev=830928&view=auto
==============================================================================
---
ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/AutoconfTask.java
(added)
+++
ant/sandbox/autoconf/src/main/org/apache/tools/ant/taskdefs/AutoconfTask.java
Thu Oct 29 13:18:16 2009
@@ -0,0 +1,302 @@
+package org.apache.tools.ant.taskdefs;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.taskdefs.PreSetDef.PreSetDefinition;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+
+/**
+ * <p>Starts or stops autoconfiguration for tasks and datatypes.
+ * Autoconfiguration applies properties to tasks and datatypes if
+ * their names fit to the tasks/datatype and attribute names and the
+ * attribute is not set.</p>
+ *
+ * <p>The property name must match the following rule:<br>
+ * <tt>propertyname ::= prefix? taskname "." attributename</tt></p>
+ *
+ * <p><b>Example:</b><br>
+ * You have defined properties like <tt>javac.debug=on, javac.target=1.5,
+ * javac.source=1.5</tt>. Instead of applying these values manually you
+ * could write <tt><autoconf/> <javac></tt></p>
+ *
+ * @since Ant 1.8.0
+ */
+public class AutoconfTask extends Task {
+
+ /** Start or stop the AutoconfListener? */
+ boolean start = true;
+
+ /** Optional name prefix (before the task name). */
+ String prefix = null;
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public void execute() {
+ if (start) {
+ addAutoconfListener();
+ } else {
+ removeAutoconfListener();
+ }
+ }
+
+ /**
+ * Searches all registered AutoconfListener and removes them.
+ */
+ private void removeAutoconfListener() {
+ Iterator it = getProject().getBuildListeners().iterator();
+ while(it.hasNext()) {
+ Object next = it.next();
+ if (next instanceof AutoconfListener) {
+
getProject().removeBuildListener(((AutoconfListener) next));
+ }
+ }
+ }
+
+ /**
+ * Adds a new AutoconfListener.
+ */
+ private void addAutoconfListener() {
+ getProject().addBuildListener(new AutoconfListener(prefix));
+ }
+
+ /**
+ * Sets the action for the autoconf task.
+ *
+ * @param action The action for the entry to take: start or stop.
+ */
+ public void setAction(ActionChoices action) {
+ start = action.getValue().equalsIgnoreCase("start");
+ }
+
+
+ //-------------------------------------------------------------------------
+
+
+ /**
+ * A list of possible values for the <code>setAction()</code> method.
+ * Possible values include: start and stop.
+ */
+ public static class ActionChoices extends EnumeratedAttribute {
+ private static final String[] VALUES = {"start", "stop"};
+
+ /**
+ * @see EnumeratedAttribute#getValues()
+ */
+ /** {...@inheritdoc}. */
+ public String[] getValues() {
+ return VALUES;
+ }
+ }
+
+
+ //-------------------------------------------------------------------------
+
+
+ /**
+ * Implements the <i>AOP</i>-part of the autoconf task (the real autoconf
logic).
+ * When starting a task, it searches for the properties and applies them.
+ */
+ public class AutoconfListener implements BuildListener {
+
+ /** Prefix for the property name. */
+ private String prefix;
+
+ public AutoconfListener(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public void buildStarted(BuildEvent event) {
+ // no-op
+ }
+
+ public void buildFinished(BuildEvent event) {
+ // no-op
+ }
+
+ public void targetStarted(BuildEvent event) {
+ // no-op
+ }
+
+ public void targetFinished(BuildEvent event) {
+ // no-op
+ }
+
+ public void taskFinished(BuildEvent event) {
+ // no-op
+ }
+
+ public void messageLogged(BuildEvent event) {
+ // no-op
+ }
+
+ public void taskStarted(BuildEvent event) {
+ UnknownElement ue = (UnknownElement) event.getTask();
+
+ // Access to the properties which are available at 'call time'.
+ Hashtable properties = ue.getProject().getProperties();
+
+ // Name of the task, like 'echo', 'javac'
+ String name = ue.getTag();
+
+ // Access to the task object before configuration
+ RuntimeConfigurable rc = ue.getWrapper();
+
+ // Attributes set by parsing the build file: must not overwrite
these!
+ Set alreadySetAttributes = rc.getAttributeMap().keySet();
+
+ // All supported attributes by the task
+ Set allAttributes = getTaskAttributes(name, ue.getProject());
+
+ // Try to apply all properties with prefix 'prefix'+'taskname' to
the task object
+ String propPrefix = prefix == null ? name + "." : prefix + name +
".";
+ for (Iterator keys = properties.keySet().iterator();
keys.hasNext(); ) {
+ String propName = (String) keys.next();
+ if (!propName.startsWith(propPrefix)) {
+ // skip properties which don't apply to the task
+ continue;
+ }
+ String attributeName =
propName.substring(propPrefix.length());
+ if (!alreadySetAttributes.contains(attributeName) &&
allAttributes.contains(attributeName)) {
+ // Register the key-value pair for later
setting via IntrospectionHelper
+ // while doing UnknownElement.configure() -->
RuntimeConfigurable.maybeConfigure()
+ // --> IntrospectionHelper.setAttribute()
+ rc.setAttribute(attributeName,
String.valueOf(properties.get(propName)));
+ } else {
+ // do not overwrite already set attributes
+ // do not try to set attributes which do not
exist
+ }
+ }
+ }
+
+ /**
+ * Tries to analyse a task and retrieve the attributes.
+ * The algorithm creates a dummy instance as template and analyses
+ * that.
+ * @param name name of the task/datatype
+ * @param project Ant Project instance
+ * @return (notnull) list of attributes
+ */
+ private Set getTaskAttributes(String name, Project project) {
+ Object templateObject = createTemplateObject(name, project);
+
+ if (templateObject == null) {
+ // can not create a template object, so give up
+ return new HashSet();
+ }
+
+ if (templateObject instanceof MacroInstance) {
+ // we can ask <macrodef> directly for its attributes
+ return getMacrodefAttributes((MacroInstance)
templateObject);
+ }
+
+ // analyse class via reflection
+ Class templateClass = createTemplateClass(project,
templateObject);
+ return getSetter(templateClass);
+ }
+
+ /**
+ * Returns a list of attributes with public setter for a given
class.
+ * @param templateClass class to analyze
+ * @return (notnull) list of attribute names
+ */
+ private Set getSetter(Class templateClass) {
+ Set rv = new HashSet();
+ Method[] methods = templateClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ if (isSetter(m)) {
+ String attributeName = m.getName().substring(3);
+ attributeName =
attributeName.substring(0,1).toLowerCase() + attributeName.substring(1);
+ rv.add( attributeName );
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * Checks if a given method is a public setter,
+ * means is public, starts with <tt>set</tt> and requires one
+ * argument.
+ * This method allows return values.
+ * @param method method to check
+ * @return <tt>true</tt> if the method is a setter
+ */
+ private boolean isSetter(Method method) {
+ return method.getName().startsWith("set")
+ && method.getParameterTypes().length == 1
+ && method.getModifiers()==Modifier.PUBLIC;
+ }
+
+ /**
+ * Creates a template class to a given object.
+ * Depending on the object type, it is asked differently.
+ * @param project Ant project instance
+ * @param templateObject template object
+ * @return template class for later analyze
+ */
+ private Class createTemplateClass(Project project, Object
templateObject) {
+ Class templateClass = null;
+ if (templateObject instanceof PreSetDefinition) {
+ PreSetDefinition preset = (PreSetDefinition)
templateObject;
+ templateClass = preset.getTypeClass(project);
+ } else {
+ templateClass = templateObject.getClass();
+ }
+ return templateClass;
+ }
+
+ /**
+ * Returns the attributes of a <macrodef>.
+ * @param macro the macro instance
+ * @return attribute list
+ */
+ private Set getMacrodefAttributes(MacroInstance macro) {
+ Set rv = new HashSet();
+ MacroDef macroDef = macro.getMacroDef();
+ List attributes = macroDef.getAttributes();
+ for (Iterator it = attributes.iterator();
it.hasNext();) {
+ MacroDef.Attribute object =
(MacroDef.Attribute) it.next();
+ rv.add(object.getName());
+ }
+ return rv;
+ }
+
+ /**
+ * Creates a template object for the task/datatype of the given
name.
+ * @param name name of the task/datatype
+ * @param project Ant project instance for resolving the name
+ * @return template object
+ */
+ private Object createTemplateObject(String name, Project
project) {
+ Object templateObject = null;
+ try {
+ templateObject = project.createTask(name);
+ } catch (Exception e) {
+ // the task name is recognised but task creation fails.
+ try {
+ // retry as data type, e.g. <presetdef>
+ templateObject = project.createDataType(name);
+ } catch(Exception e2) {
+ // give it up
+ }
+ }
+ return templateObject;
+ }
+
+ }
+}
\ No newline at end of file
Added: ant/sandbox/autoconf/test.xml
URL:
http://svn.apache.org/viewvc/ant/sandbox/autoconf/test.xml?rev=830928&view=auto
==============================================================================
--- ant/sandbox/autoconf/test.xml (added)
+++ ant/sandbox/autoconf/test.xml Thu Oct 29 13:18:16 2009
@@ -0,0 +1,7 @@
+<project>
+ <taskdef name="autoconf"
classname="org.apache.tools.ant.taskdefs.AutoconfTask"
classpath="build/classes"/>
+ <property name="echo.message" value="Default Message"/>
+ <autoconf/>
+ <echo/>
+ <echo message="Individual Message"/>
+</project>
\ No newline at end of file