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