Author: hibou
Date: Sun Feb 21 22:12:56 2010
New Revision: 912417
URL: http://svn.apache.org/viewvc?rev=912417&view=rev
Log:
- use delegates more than metaclass
- make condition evaluation work
Removed:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontMetaClass.java
Modified:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontBuilder.java
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontProjectHelper.java
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontScriptMetaClass.java
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/SimpleNamespaceBuilder.java
ant/sandbox/groovyfront/src/test/antunit/conditionTest.groovy
Modified:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontBuilder.java
URL:
http://svn.apache.org/viewvc/ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontBuilder.java?rev=912417&r1=912416&r2=912417&view=diff
==============================================================================
---
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontBuilder.java
(original)
+++
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontBuilder.java
Sun Feb 21 22:12:56 2010
@@ -18,29 +18,75 @@
package org.apache.ant.groovyfront;
import groovy.lang.Closure;
+import groovy.lang.MissingMethodException;
import groovy.util.AntBuilder;
import groovy.xml.QName;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.RuntimeConfigurable;
-import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskAdapter;
import org.apache.tools.ant.UnknownElement;
import org.apache.tools.ant.taskdefs.ConditionTask;
import org.apache.tools.ant.taskdefs.condition.AccessorHack;
import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.codehaus.groovy.runtime.InvokerHelper;
public class GroovyFrontBuilder extends AntBuilder {
+ // TODO: find a way to handle that list dynamically
+ private static final Set CONDITION_TASK_NAMES = new
HashSet(Arrays.asList(new Object[] { "available", "uptodate",
+ "antversion" }));
+
+ private IntrospectionHelper conditionHelper;
+
public GroovyFrontBuilder(GroovyFrontProject project) {
super(project);
+ conditionHelper = IntrospectionHelper.getHelper(project,
ConditionTask.class);
+ }
+
+ protected Object doInvokeMethod(String methodName, Object name, Object
args) {
+ Project p = getProject();
+ logDebug(p, "trying", methodName);
+ try {
+ Object o;
+ try {
+ o = super.doInvokeMethod(methodName, name, args);
+ } catch (BuildException be) {
+ // try to find out why we failed
+ Object[] argsArray = InvokerHelper.asList(args).toArray();
+ if (isTaskDefined(methodName) && isNotCondition(methodName,
argsArray)) {
+ // this should have been an ant task but there is an Ant
configuration error
+ throw be;
+ }
+ // just continue to lookup
+ throw new MissingMethodException(methodName, getClass(),
argsArray, false);
+ // TODO check that failing to resolve a nested type is
properly caught up by some parent builder
+ }
+ logDebug(p, "caught", methodName);
+ return o;
+ } catch (MissingMethodException mme) {
+ logDebug(p, "missed", methodName);
+ throw mme;
+ }
+ }
+
+ private void logDebug(Project p, String status, String methodName) {
+ p.log(status + " in GroovyFrontBuilder: " + methodName,
Project.MSG_DEBUG);
}
protected void setClosureDelegate(Closure closure, Object node) {
super.setClosureDelegate(closure, node);
+ // ensure that we first hit the delegate: the builder
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
}
@@ -73,8 +119,37 @@
return true;
}
+ /**
+ * Check that the method invoked is not a 'pure' condition. This function
is taking into account the difference
+ * between the conditions like 'available' which as task must has
'property' set but as 'pure' condition doesn't.
+ *
+ * @param methodName
+ * @param arguments
+ * @return
+ */
+ public boolean isNotCondition(String methodName, Object[] arguments) {
+ if (CONDITION_TASK_NAMES.contains(methodName)) {
+ return arguments.length > 0 && arguments[0] instanceof Map &&
((Map) arguments[0]).containsKey("property");
+ }
+ Enumeration elements = conditionHelper.getNestedElements();
+ while (elements.hasMoreElements()) {
+ if (elements.nextElement().equals(methodName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Create a 'pure' condition
+ *
+ * @param methodName
+ * @param arguments
+ * @return
+ */
public Condition createCondition(String methodName, Object[] arguments) {
- Object conditionNode = createNode("condition",
Collections.singletonMap("property", "__groovyfront_condition__"));
+ Object conditionNode = createNode("condition", Collections
+ .singletonMap("property", "__groovyfront_condition__"));
Object current = getCurrent();
setCurrent(conditionNode);
invokeMethod(methodName, arguments);
Modified:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontProjectHelper.java
URL:
http://svn.apache.org/viewvc/ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontProjectHelper.java?rev=912417&r1=912416&r2=912417&view=diff
==============================================================================
---
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontProjectHelper.java
(original)
+++
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontProjectHelper.java
Sun Feb 21 22:12:56 2010
@@ -25,6 +25,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -52,7 +53,7 @@
}
public void parse(Project project, Object source) throws BuildException {
- Vector/*<Object>*/ stack = getImportStack();
+ Vector/* <Object> */stack = getImportStack();
stack.addElement(source);
GroovyFrontParsingContext context = null;
context = (GroovyFrontParsingContext)
project.getReference(REFID_CONTEXT);
@@ -62,12 +63,12 @@
}
if (getImportStack().size() > 1) {
- Map/*<String, Target>*/ currentTargets =
context.getCurrentTargets();
+ Map/* <String, Target> */currentTargets =
context.getCurrentTargets();
String currentProjectName = context.getCurrentProjectName();
boolean imported = context.isImported();
try {
context.setImported(true);
- context.setCurrentTargets(new HashMap/*<String, Target>*/());
+ context.setCurrentTargets(new HashMap/* <String, Target> */());
parse(project, source, context);
} finally {
context.setCurrentTargets(currentTargets);
@@ -76,7 +77,7 @@
}
} else {
// top level file
- context.setCurrentTargets(new HashMap/*<String, Target>*/());
+ context.setCurrentTargets(new HashMap/* <String, Target> */());
parse(project, source, context);
}
}
@@ -128,12 +129,13 @@
GroovyShell groovyShell = new GroovyShell(getClass().getClassLoader(),
binding);
final Script script;
try {
- script = groovyShell.parse(in, buildFileName);
+ script = groovyShell.parse(new InputStreamReader(in),
buildFileName);
} catch (CompilationFailedException e) {
throw new BuildException("Error reading groovy file " +
buildFileName + ": " + e.getMessage(), e);
}
script.setBinding(binding);
- script.setMetaClass(new
GroovyFrontScriptMetaClass(script.getMetaClass(), groovyFrontProject,
antBuilder, context));
+ script.setMetaClass(new
GroovyFrontScriptMetaClass(script.getMetaClass(), groovyFrontProject,
antBuilder,
+ context));
new GroovyRunner() {
protected void doRun() {
script.run();
Modified:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontScriptMetaClass.java
URL:
http://svn.apache.org/viewvc/ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontScriptMetaClass.java?rev=912417&r1=912416&r2=912417&view=diff
==============================================================================
---
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontScriptMetaClass.java
(original)
+++
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/GroovyFrontScriptMetaClass.java
Sun Feb 21 22:12:56 2010
@@ -18,7 +18,10 @@
package org.apache.ant.groovyfront;
import groovy.lang.Closure;
+import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;
+import groovy.lang.MissingMethodException;
+import groovy.lang.Tuple;
import java.util.Hashtable;
import java.util.Map;
@@ -29,27 +32,58 @@
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.taskdefs.AntlibDefinition;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.codehaus.groovy.runtime.MetaClassHelper;
-public class GroovyFrontScriptMetaClass extends GroovyFrontMetaClass {
+public class GroovyFrontScriptMetaClass extends DelegatingMetaClass {
private final GroovyFrontProject project;
private final GroovyFrontParsingContext context;
+ private final GroovyFrontBuilder groovyFrontBuilder;
- public GroovyFrontScriptMetaClass(MetaClass metaClass, GroovyFrontProject
project, GroovyFrontBuilder groovyFrontBuilder,
- GroovyFrontParsingContext context) {
- super(metaClass, groovyFrontBuilder);
+ public GroovyFrontScriptMetaClass(MetaClass metaClass, GroovyFrontProject
project,
+ GroovyFrontBuilder groovyFrontBuilder, GroovyFrontParsingContext
context) {
+ super(metaClass);
this.project = project;
+ this.groovyFrontBuilder = groovyFrontBuilder;
this.context = context;
}
+ public Object invokeMethod(final Object object, final String methodName,
final Object arguments) {
+ if (arguments == null) {
+ return invokeMethod(object, methodName,
MetaClassHelper.EMPTY_ARRAY);
+ } else if (arguments instanceof Tuple) {
+ return invokeMethod(object, methodName, ((Tuple)
arguments).toArray());
+ } else if (arguments instanceof Object[]) {
+ return invokeMethod(object, methodName, (Object[]) arguments);
+ } else {
+ return invokeMethod(object, methodName, new Object[] { arguments
});
+ }
+ }
+
+ public Object invokeMethod(final String name, final Object args) {
+ return invokeMethod(this, name, args);
+ }
+
+ public Object invokeMethod(final Class sender, final Object receiver,
final String methodName,
+ final Object[] arguments, final boolean isCallToSuper, final
boolean fromInsideClass) {
+ return invokeMethod(receiver, methodName, arguments);
+ }
+
public Object invokeMethod(Object object, String methodName, Object[]
arguments) {
+ logDebug("trying", methodName);
+
if ("target".equals(methodName)) {
+ logDebug("caught", methodName);
defineTarget(arguments);
return null;
}
+
if ("include".equals(methodName)) {
+ logDebug("caught", methodName);
return importScript(arguments);
}
+
if ("groovyns".equals(methodName)) {
if (arguments.length != 1 && !(arguments[0] instanceof Map)) {
throw new BuildException("Invalid method signature for
'groovyns'");
@@ -59,23 +93,53 @@
throw new BuildException("Missing 'uri' argument on
'groovyns'");
}
String prefix = (String) ((Map) arguments[0]).get("prefix");
+ logDebug("caught", methodName);
return new SimpleNamespaceBuilder(groovyFrontBuilder, prefix, uri);
}
- Object returnObject = super.invokeMethod(object, methodName,
arguments);
+
+ if (!groovyFrontBuilder.isNotCondition(methodName, arguments)) {
+ Condition condition =
groovyFrontBuilder.createCondition(methodName, arguments);
+ logDebug("caught", methodName);
+ return Boolean.valueOf(condition.eval());
+ }
+
+ Object returnObject;
+ try {
+ // try the script functions
+ returnObject = super.invokeMethod(object, methodName, arguments);
+ } catch (MissingMethodException mme) {
+ // this may be a call to an ant task
+ if (groovyFrontBuilder.isTaskDefined(methodName)
+ && groovyFrontBuilder.isNotCondition(methodName,
arguments)) {
+ return groovyFrontBuilder.invokeMethod(methodName, arguments);
+ }
+ logDebug("missed", methodName);
+ throw mme;
+ }
+ logDebug("caught", methodName);
+
if (returnObject instanceof AntlibDefinition) {
+ // wrap the antlib into a builder
AntlibDefinition antlibDefinition = (AntlibDefinition)
returnObject;
- returnObject = new SimpleNamespaceBuilder(groovyFrontBuilder,
antlibDefinition.getURI(), antlibDefinition.getURI());
+ returnObject = new SimpleNamespaceBuilder(groovyFrontBuilder,
antlibDefinition.getURI(), antlibDefinition
+ .getURI());
}
+
return returnObject;
}
+ private void logDebug(String status, String methodName) {
+ project.log(status + " in GroovyFrontScriptMetaClass: " + methodName,
Project.MSG_DEBUG);
+ }
+
private void defineTarget(Object[] args) {
if (args.length != 2 || !(args[0] instanceof Map) || !(args[1]
instanceof Closure)) {
throw new BuildException("A target is ill formed. Expecting map,
closure but was: " + Arrays.toString(args));
}
- Map/*<String, String>*/ map = (Map/*<String, String>*/) args[0];
+ Map/* <String, String> */map = (Map/* <String, String> */) args[0];
Closure closure = (Closure) args[1];
- closure.setMetaClass(new GroovyFrontMetaClass(closure.getMetaClass(),
groovyFrontBuilder));
+ closure.setDelegate(groovyFrontBuilder);
+ closure.setResolveStrategy(Closure.DELEGATE_FIRST);
String name = (String) map.get("name");
String description = (String) map.get("description");
String depends = (String) map.get("depends");
@@ -103,7 +167,7 @@
target.setDescription(description);
}
- Hashtable/*<?, ?>*/ projectTargets = project.getTargets();
+ Hashtable/* <?, ?> */projectTargets = project.getTargets();
// If the name has not already been defined, log an override
// NB: unlike ant xml project helper, the imported file are executed
before the target definition of the main
// file
@@ -123,7 +187,6 @@
context.getCurrentTargets().put(newName, newTarget);
project.addOrReplaceTarget(newName, newTarget);
}
-
}
private Object importScript(Object[] arguments) {
Modified:
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/SimpleNamespaceBuilder.java
URL:
http://svn.apache.org/viewvc/ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/SimpleNamespaceBuilder.java?rev=912417&r1=912416&r2=912417&view=diff
==============================================================================
---
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/SimpleNamespaceBuilder.java
(original)
+++
ant/sandbox/groovyfront/src/main/java/org/apache/ant/groovyfront/SimpleNamespaceBuilder.java
Sun Feb 21 22:12:56 2010
@@ -17,8 +17,8 @@
*/
package org.apache.ant.groovyfront;
+import groovy.lang.Closure;
import groovy.lang.GroovyObjectSupport;
-import groovy.xml.NamespaceBuilder;
import groovy.xml.NamespaceBuilderSupport;
import java.util.Collections;
@@ -31,7 +31,13 @@
public SimpleNamespaceBuilder(GroovyFrontBuilder groovyFrontBuilder,
String prefix, String uri) {
this.prefix = prefix == null ? uri.replaceAll(":", ".") : prefix;
- nsBuilder =
NamespaceBuilder.newInstance(Collections.singletonMap(this.prefix, uri),
groovyFrontBuilder);
+ nsBuilder = new NamespaceBuilderSupport(groovyFrontBuilder,
Collections.singletonMap(this.prefix, uri)) {
+ protected void setClosureDelegate(Closure closure, Object node) {
+ closure.setDelegate(this);
+ // ensure that we first hit the delegate: the builder
+ closure.setResolveStrategy(Closure.DELEGATE_FIRST);
+ }
+ };
}
public Object invokeMethod(String name, Object args) {
Modified: ant/sandbox/groovyfront/src/test/antunit/conditionTest.groovy
URL:
http://svn.apache.org/viewvc/ant/sandbox/groovyfront/src/test/antunit/conditionTest.groovy?rev=912417&r1=912416&r2=912417&view=diff
==============================================================================
--- ant/sandbox/groovyfront/src/test/antunit/conditionTest.groovy (original)
+++ ant/sandbox/groovyfront/src/test/antunit/conditionTest.groovy Sun Feb 21
22:12:56 2010
@@ -34,3 +34,11 @@
def a = available(classname: 'org.apache.tools.ant.Project')
au.assertEquals(actual: true, expected: a)
}
+
+target(name: 'testAnd') {
+ def a = and {
+ available(classname: 'org.apache.tools.ant.Project')
+ istrue(value: 'true')
+ }
+ au.assertEquals(actual: true, expected: a)
+}