You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/01/31 20:03:41 UTC

svn commit: r905100 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/services/ test/java/org/apache/tapestry5/internal/services/ test/java/org/apache/tapestry5/internal/transform/pages/

Author: hlship
Date: Sun Jan 31 19:03:41 2010
New Revision: 905100

URL: http://svn.apache.org/viewvc?rev=905100&view=rev
Log:
Add support MethodAccess with non-public component methods

Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/pages/MethodAccessSubject.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java?rev=905100&r1=905099&r2=905100&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java Sun Jan 31 19:03:41 2010
@@ -17,6 +17,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Inherited;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Formatter;
 import java.util.List;
 import java.util.Map;
@@ -138,7 +139,7 @@
             if (isPublic())
                 return createPublicMethodAccess();
 
-            return null;
+            return createNonPublicMethodAccess();
         }
 
         private boolean isPublic()
@@ -148,11 +149,16 @@
 
         private MethodAccess createPublicMethodAccess()
         {
+            // For a public method, given the instance, we can just invoke the method directly
+            // from the MethodAccess object.
 
-            ClassFab cf = classFactory
-                    .newClass(ClassFabUtils.generateClassName(MethodAccess.class),
-                            AbstractMethodAccess.class);
+            String accessTarget = "instance." + sig.getMethodName();
 
+            return createMethodAccessForTarget(accessTarget, false);
+        }
+
+        private MethodAccess createMethodAccessForTarget(String accessTarget, boolean passInstance)
+        {
             boolean isVoid = sig.getReturnType().equals("void");
 
             BodyBuilder builder = new BodyBuilder().begin();
@@ -166,20 +172,23 @@
                 builder.add("return success(($w) ");
             }
 
-            // Call the instance method, even if its void.
+            // Call the target, even if the eventual method is void
 
-            builder.add("instance.%s(", sig.getMethodName());
+            builder.add(accessTarget);
+            builder.add("(");
+
+            if (passInstance)
+                builder.add("instance");
 
             int p = 0;
 
             for (String type : sig.getParameterTypes())
             {
-                if (p != 0)
+                if (passInstance || p != 0)
                     builder.add(", ");
 
                 String ref = String.format("$2[%d]", p++);
                 builder.add(ClassFabUtils.castReference(ref, type));
-                p++;
             }
 
             // Balance the call to success()
@@ -197,7 +206,16 @@
 
             builder.end();
 
-            cf.addMethod(Modifier.PUBLIC, INVOKE_SIGNATURE, builder.toString());
+            return instantiateMethodAccessFromBody(builder.toString());
+        }
+
+        private MethodAccess instantiateMethodAccessFromBody(String body)
+        {
+            ClassFab cf = classFactory
+                    .newClass(ClassFabUtils.generateClassName(MethodAccess.class),
+                            AbstractMethodAccess.class);
+
+            cf.addMethod(Modifier.PUBLIC, INVOKE_SIGNATURE, body);
 
             cf.addToString(String.format("MethodAccess[method %s of class %s]", sig
                     .getMediumDescription(), getClassName()));
@@ -216,6 +234,66 @@
             }
         }
 
+        private MethodAccess createNonPublicMethodAccess()
+        {
+            // As with Java inner classes, we have to create a static bridge method.
+
+            String staticAccessMethodName = createStaticAccessMethodForNonPublicMethod();
+
+            // Have the MethodAccess object call the static method and pass the
+            // instance object as the first parameter. The static method will then
+            // invoke the non-public method on the passed instance.
+
+            return createMethodAccessForTarget(String.format("%s#%s", getClassName(),
+                    staticAccessMethodName), true);
+        }
+
+        /**
+         * The static method takes the same parameters as the main method, but takes
+         * an instance object first. Invoking the static method turns into an invocation
+         * of the proper method of the instance object.
+         * 
+         * @return the name of the created static access method
+         */
+        private String createStaticAccessMethodForNonPublicMethod()
+        {
+            List<String> parameterTypes = CollectionFactory.newList(getClassName());
+            parameterTypes.addAll(Arrays.asList(sig.getParameterTypes()));
+
+            String methodName = newMemberName("access", sig.getMethodName());
+
+            TransformMethodSignature accessMethodSignature = new TransformMethodSignature(
+                    Modifier.PUBLIC + Modifier.STATIC, sig.getReturnType(), methodName,
+                    parameterTypes.toArray(new String[0]), sig.getExceptionTypes());
+
+            boolean isVoid = sig.getReturnType().equals("void");
+
+            BodyBuilder builder = new BodyBuilder();
+
+            builder.begin();
+
+            if (!isVoid)
+                builder.add("return ");
+
+            builder.add("$1.%s(", sig.getMethodName());
+
+            for (int i = 0; i < sig.getParameterTypes().length; i++)
+            {
+                if (i > 0)
+                    builder.add(", ");
+
+                builder.add("$%d", i + 2);
+            }
+
+            builder.addln(");");
+
+            builder.end();
+
+            addMethod(accessMethodSignature, builder.toString());
+
+            return methodName;
+        }
+
         public void extend(String body)
         {
             failIfFrozen();

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java?rev=905100&r1=905099&r2=905100&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java Sun Jan 31 19:03:41 2010
@@ -675,7 +675,57 @@
         assertEquals(pi.operate(99), 100);
 
         assertEquals(access.get(instance, "marker"), "incrementer(99)");
+    }
+
+    public interface ProcessStringAndInteger
+    {
+        String process(String input, int value);
+    }
+
+    @Test
+    public void access_to_private_method() throws Exception
+    {
+        Object instance = transform(MethodAccessSubject.class, new ComponentClassTransformWorker()
+        {
+            public void transform(ClassTransformation transformation, MutableComponentModel model)
+            {
+                transformation.addImplementedInterface(ProcessStringAndInteger.class);
+
+                TransformMethod targetMethod = transformation
+                        .getMethod(new TransformMethodSignature(Modifier.PRIVATE,
+                                "java.lang.String", "privateMethod", new String[]
+                                { "java.lang.String", "int" }, null));
+
+                final MethodAccess targetMethodAccess = targetMethod.getAccess();
+
+                TransformMethodSignature processSig = new TransformMethodSignature(Modifier.PUBLIC,
+                        "java.lang.String", "process", new String[]
+                        { "java.lang.String", "int" }, null);
+
+                TransformMethod process = transformation.getMethod(processSig);
+
+                process.addAdvice(new ComponentMethodAdvice()
+                {
+                    public void advise(ComponentMethodInvocation invocation)
+                    {
+                        // Don't even bother with proceed() this time, which is OK (but
+                        // somewhat rare).
+
+                        MethodInvocationResult result = targetMethodAccess.invoke(invocation
+                                .getInstance(), invocation.getParameter(0), invocation
+                                .getParameter(1));
+
+                        invocation.overrideResult(result.getReturnValue());
+                    }
+                });
+            }
+        });
+
+        ProcessStringAndInteger p = (ProcessStringAndInteger) instance;
+
+        assertEquals(p.process("Tapestry!", 2), "Tapestry!Tapestry!");
 
+        assertEquals(access.get(instance, "marker"), "privateMethod");
     }
 
     @Test

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/pages/MethodAccessSubject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/pages/MethodAccessSubject.java?rev=905100&r1=905099&r2=905100&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/pages/MethodAccessSubject.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/pages/MethodAccessSubject.java Sun Jan 31 19:03:41 2010
@@ -56,4 +56,17 @@
 
         throw new SQLException("From publicVoidThrowsException()");
     }
+
+    @SuppressWarnings("unused")
+    private String privateMethod(String input, int count)
+    {
+        marker = "privateMethod";
+
+        StringBuilder builder = new StringBuilder();
+
+        for (int i = 0; i < count; i++)
+            builder.append(input);
+
+        return builder.toString();
+    }
 }