You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2011/04/07 23:16:54 UTC

svn commit: r1090018 - in /tapestry/tapestry5/trunk/plastic/src: main/java/org/apache/tapestry5/internal/plastic/ main/java/org/apache/tapestry5/plastic/ test/groovy/org/apache/tapestry5/plastic/ test/java/testinterfaces/ test/java/testsubjects/

Author: hlship
Date: Thu Apr  7 21:16:50 2011
New Revision: 1090018

URL: http://svn.apache.org/viewvc?rev=1090018&view=rev
Log:
TAP5-853: Add support for copying annotations from a source class to a PlasticClass, used in certain Tapestry IoC proxy creation scenarios

Added:
    tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/
    tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/AnnotationTransfer.java
    tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/AnnotationTransferImpl.java
Modified:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ProxyCreation.groovy

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java?rev=1090018&r1=1090017&r2=1090018&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java Thu Apr  7 21:16:50 2011
@@ -639,9 +639,9 @@ public class PlasticClassImpl extends Lo
 
             // Easiest to load this, for the putField(), early, in case the field is
             // wide (long or double primitive)
-            
+
             constructorBuilder.loadThis();
-            
+
             // Add the InstanceContext to the stack
 
             constructorBuilder.loadArgument(1);
@@ -2213,4 +2213,94 @@ public class PlasticClassImpl extends Lo
         return methodBundle.isImplemented(description.methodName, nameCache.toDesc(description));
     }
 
+    public PlasticClass copyAnnotations(String sourceClassName)
+    {
+        assert PlasticInternalUtils.isNonBlank(sourceClassName);
+
+        ClassNode sourceClass = pool.constructClassNode(sourceClassName);
+
+        classNode.visibleAnnotations = sourceClass.visibleAnnotations;
+
+        Map<String, MethodNode> sourceMethods = buildMethodNodeMap(sourceClass, true);
+
+        if (sourceMethods.isEmpty())
+            return this;
+
+        Map<String, MethodNode> targetMethods = buildMethodNodeMap(classNode, false);
+
+        for (Map.Entry<String, MethodNode> entry : sourceMethods.entrySet())
+        {
+            MethodNode target = targetMethods.get(entry.getKey());
+
+            // Not all source methods (especially private ones) will be in the target class,
+            // which is typically a proxy, implementing just public methods defined in an interface.
+
+            if (target == null)
+                continue;
+
+            MethodNode source = entry.getValue();
+
+            target.visibleAnnotations = source.visibleAnnotations;
+            target.visibleParameterAnnotations = source.visibleParameterAnnotations;
+        }
+
+        return this;
+    }
+
+    private static Map<String, MethodNode> buildMethodNodeMap(ClassNode source, boolean withAnnotationsOnly)
+    {
+        boolean all = !withAnnotationsOnly;
+
+        Map<String, MethodNode> result = new HashMap<String, MethodNode>();
+
+        for (Object m : source.methods)
+        {
+            MethodNode mn = (MethodNode) m;
+
+            if (mn.name.equals(CONSTRUCTOR_NAME))
+                continue;
+
+            if (all || hasAnnotations(mn))
+                result.put(mn.name + ":" + mn.desc, mn);
+        }
+
+        return result;
+    }
+
+    /**
+     * True if the node has any visible annotations, or it has visible annotations on any
+     * parameter.
+     * 
+     * @param mn
+     * @return true if any annotations present
+     */
+    private static boolean hasAnnotations(MethodNode mn)
+    {
+        if (nonEmpty(mn.visibleAnnotations))
+            return true;
+
+        if (mn.visibleParameterAnnotations != null)
+        {
+            for (List pa : mn.visibleParameterAnnotations)
+            {
+                if (nonEmpty(pa))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean nonEmpty(List l)
+    {
+        return l != null && !l.isEmpty();
+    }
+
+    public PlasticClass copyAnnotations(Class sourceClass)
+    {
+        assert sourceClass != null;
+
+        return copyAnnotations(sourceClass.getName());
+    }
+
 }

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java?rev=1090018&r1=1090017&r2=1090018&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java Thu Apr  7 21:16:50 2011
@@ -60,7 +60,7 @@ public class PlasticClassPool implements
 
         protected TypeCategory convert(String typeName)
         {
-            ClassNode cn = createClassNode(typeName);
+            ClassNode cn = constructClassNode(typeName);
 
             return Modifier.isInterface(cn.access) ? TypeCategory.INTERFACE : TypeCategory.CLASS;
         }
@@ -267,7 +267,7 @@ public class PlasticClassPool implements
     {
         assert PlasticInternalUtils.isNonBlank(className);
 
-        ClassNode classNode = createClassNode(className);
+        ClassNode classNode = constructClassNode(className);
 
         String baseClassName = PlasticInternalUtils.toClassName(classNode.superName);
 
@@ -291,7 +291,15 @@ public class PlasticClassPool implements
         return new PlasticClassImpl(classNode, this, emptyMethodBundle, emptyStaticContext);
     }
 
-    private ClassNode createClassNode(String className)
+    /**
+     * Constructs a class node by reading the raw bytecode for a class and instantiating a ClassNode
+     * (via {@link ClassReader#accept(org.apache.tapestry5.internal.plastic.asm.ClassVisitor, int)}).
+     * 
+     * @param className
+     *            fully qualified class name
+     * @return corresponding ClassNode
+     */
+    public ClassNode constructClassNode(String className)
     {
         byte[] bytecode = readBytecode(className);
 

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java?rev=1090018&r1=1090017&r2=1090018&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java Thu Apr  7 21:16:50 2011
@@ -149,4 +149,28 @@ public interface PlasticClass extends An
      * @return this plastic class, for further configuration
      */
     PlasticClass addToString(String toStringValue);
+
+    /**
+     * Copies annotations from the indicated source class name; this copies all class annotations,
+     * and for each method that exists in both classes, copies over method and parameter annotations. This addresses
+     * a specific use case for Tapestry IoC, where a proxy class is expected to expose the visible annotations of the
+     * implementation class. As currently implemented, copied annotations <em>overwrite</em> annotations in this
+     * PlasticClass (because it is expected that there are no annotations in a proxy).
+     * <p>
+     * This method should be invoked late in the PlasticClass transformation; copied annotations may not be visible as
+     * annotations on the PlasticClass or {@link PlasticMethod}s of the PlasticClass.
+     * 
+     * @param sourceClassName
+     *            source class from which to extract annotations
+     * @return this plastic class, for further configuration
+     */
+    PlasticClass copyAnnotations(String sourceClassName);
+
+    /**
+     * An convenience for {@link #copyAnnotations(String)}.
+     * 
+     * @param sourceClass
+     * @return this plastic class, for further configuration
+     */
+    PlasticClass copyAnnotations(Class sourceClass);
 }

Modified: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ProxyCreation.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ProxyCreation.groovy?rev=1090018&r1=1090017&r2=1090018&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ProxyCreation.groovy (original)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ProxyCreation.groovy Thu Apr  7 21:16:50 2011
@@ -1,6 +1,10 @@
 package org.apache.tapestry5.plastic
 
-import spock.lang.Specification;
+import spock.lang.Specification
+import testannotations.Maybe
+import testannotations.Truth
+import testinterfaces.AnnotationTransfer
+import testsubjects.AnnotationTransferImpl
 
 class ProxyCreation extends Specification {
     def "create a proxy"() {
@@ -46,4 +50,25 @@ class ProxyCreation extends Specificatio
         def e = thrown(IllegalArgumentException)
         e.message == "Class java.lang.String is not an interface; proxies may only be created for interfaces."
     }
+
+    def "copying of annotations"() {
+        setup:
+        def mgr = new PlasticManager()
+
+        def o = mgr.createProxy (AnnotationTransfer.class, { PlasticClass pc ->
+
+            pc.copyAnnotations (AnnotationTransferImpl.class)
+        } as PlasticClassTransformer).newInstance()
+
+        def oc = o.getClass()
+        def method1 = oc.getMethod("method1", int.class)
+
+        expect:
+
+        oc.getAnnotation(Maybe.class).value() == Truth.YES
+
+        method1.getAnnotation(Maybe.class).value() == Truth.YES
+        method1.getParameterAnnotations()[0][0].annotationType() == Maybe.class
+        method1.getParameterAnnotations()[0][0].value() == Truth.YES
+    }
 }

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/AnnotationTransfer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/AnnotationTransfer.java?rev=1090018&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/AnnotationTransfer.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/AnnotationTransfer.java Thu Apr  7 21:16:50 2011
@@ -0,0 +1,14 @@
+package testinterfaces;
+
+import testannotations.Maybe;
+import testannotations.Truth;
+
+@Maybe(Truth.NO)
+public interface AnnotationTransfer
+{
+    @Maybe(Truth.NO)
+    void method1(@Maybe(Truth.NO)
+    int foo);
+    
+    void method2(int bar);
+}

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/AnnotationTransferImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/AnnotationTransferImpl.java?rev=1090018&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/AnnotationTransferImpl.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/AnnotationTransferImpl.java Thu Apr  7 21:16:50 2011
@@ -0,0 +1,33 @@
+package testsubjects;
+
+import testannotations.Maybe;
+import testannotations.Truth;
+import testinterfaces.AnnotationTransfer;
+
+@Maybe(Truth.YES)
+public class AnnotationTransferImpl implements AnnotationTransfer
+{
+
+    @Maybe(Truth.YES)
+    public void method1(@Maybe(Truth.YES)
+    int foo)
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void method2(@Maybe(Truth.YES)
+    int bar)
+    {
+    }
+
+    @Maybe(Truth.YES)
+    public void notInInterfaceMethod()
+    {
+    }
+
+    protected void hasNoAnnotationsMethod()
+    {
+    }
+
+}