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)
+}


Reply via email to