Author: hlship
Date: Fri Nov 30 12:35:56 2007
New Revision: 599949
URL: http://svn.apache.org/viewvc?rev=599949&view=rev
Log:
TAPESTRY-1843: Fields not rewritten when modifying existing methods
Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/PlusOne.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/MethodPrefixTarget.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/services/LocaleAppModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/pagelevel/PrefixMethodTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.tml
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
Fri Nov 30 12:35:56 2007
@@ -28,12 +28,6 @@
public interface InternalClassTransformation extends ClassTransformation
{
/**
- * Returns the name of the protected field that is injected with the
- * [EMAIL PROTECTED]
org.apache.tapestry.internal.InternalComponentResources}.
- */
- String getResourcesFieldName();
-
- /**
* Invoked after all [EMAIL PROTECTED] ComponentClassTransformWorker}s
have had their chance to work over
* the class. This performs any final operations for the class
transformation, which includes
* coming up with the final constructor method for the class.
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
Fri Nov 30 12:35:56 2007
@@ -113,8 +113,7 @@
private ClassLoader _loader;
/**
- * This is a constructor for the root class, the class that directly
contains the ComponentClass
- * annotation.
+ * This is a constructor for a base class.
*/
public InternalClassTransformationImpl(CtClass ctClass, ClassLoader
loader, Logger logger,
ComponentModel componentModel)
@@ -143,8 +142,13 @@
"getComponentResources", null, null);
addMethod(sig, "return " + _resourcesFieldName + ";");
+
+ // The "}" will be added later, inside finish().
}
+ /**
+ * Constructor for a component sub-class.
+ */
public InternalClassTransformationImpl(CtClass ctClass,
InternalClassTransformation parentTransformation,
ClassLoader loader, Logger logger,
ComponentModel componentModel)
{
@@ -184,7 +188,7 @@
_constructor.append(");\n");
- // The "}" will be added later, inside
+ // The "}" will be added later, inside finish().
}
private void freeze()
@@ -622,11 +626,31 @@
_addedMethods.add(method);
}
+ public void extendExistingMethod(TransformMethodSignature methodSignature,
String methodBody)
+ {
+ failIfFrozen();
+
+ CtMethod method = findMethod(methodSignature);
+
+ try
+ {
+ method.insertAfter(methodBody);
+ }
+ catch (CannotCompileException ex)
+ {
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
methodBody, ex),
+ methodBody, ex);
+ }
+
+ addMethodToDescription("extend existing", methodSignature, methodBody);
+ }
+
public void prefixMethod(TransformMethodSignature methodSignature, String
methodBody)
{
failIfFrozen();
CtMethod method = findMethod(methodSignature);
+
try
{
method.insertBefore(methodBody);
@@ -638,7 +662,6 @@
}
addMethodToDescription("prefix", methodSignature, methodBody);
- _addedMethods.add(method);
}
private void addMethodToDescription(String operation,
TransformMethodSignature methodSignature, String methodBody)
@@ -1383,15 +1406,32 @@
@Override
public void edit(FieldAccess access) throws CannotCompileException
{
+ boolean isRead = access.isReader();
+ String fieldName = access.getFieldName();
+ CtBehavior where = access.where();
+
+ _formatter.format("Checking field %s %s in %s: ", isRead ?
"read" : "write", fieldName,
+ where.getLongName());
+
// Ignore any methods to were added as part of the
transformation.
// If we reference the field there, we really mean the field.
- if (_addedMethods.contains(access.where())) return;
+ if (_addedMethods.contains(where))
+ {
+ _formatter.format("added method\n");
+ return;
+ }
- Map<String, String> transformMap = access.isReader() ?
_fieldReadTransforms : _fieldWriteTransforms;
+ Map<String, String> transformMap = isRead ?
_fieldReadTransforms : _fieldWriteTransforms;
- String body = transformMap.get(access.getFieldName());
- if (body == null) return;
+ String body = transformMap.get(fieldName);
+ if (body == null)
+ {
+ _formatter.format("field not transformed\n");
+ return;
+ }
+
+ _formatter.format("replacing with %s\n", body);
access.replace(body);
}
@@ -1405,6 +1445,8 @@
{
throw new RuntimeException(ex);
}
+
+ _formatter.format("\n");
}
public Class toClass(String type)
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
Fri Nov 30 12:35:56 2007
@@ -227,24 +227,47 @@
* inherited methods, a method is added that first invokes the super
implementation. Use
* [EMAIL PROTECTED] #addMethod(TransformMethodSignature, String)} when it
is necessary to control when the
* super-class method is invoked.
+ * <p/>
+ * The extended method is considered <em>new</em>. New methods <em>do
not</em> are not
+ * scanned for [EMAIL PROTECTED] #removeField(String)} removed}, [EMAIL
PROTECTED] #replaceReadAccess(String, String)} read replaced},
+ * or [EMAIL PROTECTED] #replaceWriteAccess(String, String) write
replaced} fields. Generally that's what you want!
*
* @param methodSignature the signature of the method to extend
* @param methodBody the body of code
- * @throws IllegalArgumentException if the provided Javassist method body
can not be compiled
+ * @throws org.apache.tapestry.internal.services.MethodCompileException
+ * if the provided Javassist method body can not be compiled
+ * @see #extendExistingMethod(TransformMethodSignature, String)
*/
void extendMethod(TransformMethodSignature methodSignature, String
methodBody);
/**
+ * Like [EMAIL PROTECTED] #extendMethod(TransformMethodSignature,
String)}, but the extension does not mark
+ * the method as new, and field changes <em>will</em> be processed.
+ *
+ * @param methodSignature signature of the method to extend
+ * @param methodBody the body of code
+ * @throws org.apache.tapestry.internal.services.MethodCompileException
+ * if the provided method body can not be compiled
+ * @see #prefixMethod(TransformMethodSignature, String)
+ */
+ void extendExistingMethod(TransformMethodSignature methodSignature, String
methodBody);
+
+ /**
* Inserts code at the beginning of a method body (i.e. [EMAIL PROTECTED]
CtBehavior#insertBefore(String)}.
* <p/>
* The method may be declared in the class, or may be inherited from a
super-class. For
* inherited methods, a method is added that first invokes the super
implementation. Use
* [EMAIL PROTECTED] #addMethod(TransformMethodSignature, String)} when it
is necessary to control when the
* super-class method is invoked.
+ * <p/>
+ * <p/>
+ * Like [EMAIL PROTECTED] #extendExistingMethod(TransformMethodSignature,
String)}, this method is generally used to "wrap" an existing method adding
additional functionality
+ * such as caching or transaction support.
*
* @param methodSignature
* @param methodBody
- * @throws IllegalArgumentException If the provided Javassist method body
could not be compiled
+ * @throws org.apache.tapestry.internal.services.MethodCompileException
+ * if the provided method body can not be compiled
*/
void prefixMethod(TransformMethodSignature methodSignature, String
methodBody);
Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/PlusOne.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/PlusOne.java?rev=599949&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/PlusOne.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/PlusOne.java
Fri Nov 30 12:35:56 2007
@@ -0,0 +1,32 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app2;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * Adds 1 to a method result. Testing only.
+ */
[EMAIL PROTECTED](ElementType.METHOD)
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]
+public @interface PlusOne
+{
+
+}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.java
Fri Nov 30 12:35:56 2007
@@ -14,7 +14,12 @@
package org.apache.tapestry.integration.app2.pages;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.annotations.InjectPage;
import org.apache.tapestry.integration.app2.FortyTwo;
+import org.apache.tapestry.integration.app2.PlusOne;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.services.BeanModelSource;
public class TestPrefixMethod
{
@@ -24,4 +29,28 @@
return 0;
}
+ @Inject
+ private ComponentResources _resources;
+
+ @Inject
+ private BeanModelSource _modelSource;
+
+ @InjectPage
+ private TestPrefixMethod2 _otherPage;
+
+ private int foo;
+
+ @FortyTwo
+ public int getValue2()
+ {
+ foo = _modelSource.hashCode();
+ return foo;
+ }
+
+ @PlusOne
+ public int getValue3()
+ {
+ int value = _otherPage.hashCode();
+ return value * 0;
+ }
}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/services/LocaleAppModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/services/LocaleAppModule.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/services/LocaleAppModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app2/services/LocaleAppModule.java
Fri Nov 30 12:35:56 2007
@@ -15,6 +15,7 @@
package org.apache.tapestry.integration.app2.services;
import org.apache.tapestry.integration.app2.FortyTwo;
+import org.apache.tapestry.integration.app2.PlusOne;
import org.apache.tapestry.ioc.MappedConfiguration;
import org.apache.tapestry.ioc.OrderedConfiguration;
import org.apache.tapestry.model.MutableComponentModel;
@@ -24,8 +25,7 @@
public class LocaleAppModule
{
- public static void contributeApplicationDefaults(
- MappedConfiguration<String, String> configuration)
+ public static void
contributeApplicationDefaults(MappedConfiguration<String, String> configuration)
{
configuration.add("tapestry.supported-locales", "en,fr,de");
}
@@ -34,6 +34,7 @@
OrderedConfiguration<ComponentClassTransformWorker> configuration)
{
configuration.add("FortyTwo", new FortyTwoWorker());
+ configuration.add("PlusOne", new PlusOneWorker());
}
private static final class FortyTwoWorker implements
ComponentClassTransformWorker
@@ -47,5 +48,16 @@
}
}
+ }
+
+ private static final class PlusOneWorker implements
ComponentClassTransformWorker
+ {
+ public void transform(ClassTransformation transformation,
MutableComponentModel model)
+ {
+ for (TransformMethodSignature method :
transformation.findMethodsWithAnnotation(PlusOne.class))
+ {
+ transformation.extendExistingMethod(method, "return $_ + 1;");
+ }
+ }
}
}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/pagelevel/PrefixMethodTest.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/pagelevel/PrefixMethodTest.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/pagelevel/PrefixMethodTest.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/pagelevel/PrefixMethodTest.java
Fri Nov 30 12:35:56 2007
@@ -33,7 +33,9 @@
_tester = new PageTester(appPackage, appName,
PageTester.DEFAULT_CONTEXT_PATH, LocaleAppModule.class);
Document doc = _tester.renderPage("TestPrefixMethod");
- assertEquals(doc.getElementById("value").getChildMarkup(), "42");
+ // make sure you can use on methods that have injected fields
+ assertEquals(doc.getElementById("value2").getChildMarkup(), "42");
+ assertEquals(doc.getElementById("value3").getChildMarkup(), "1");
// should override the method in the superclass
doc = _tester.renderPage("TestPrefixMethod2");
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
Fri Nov 30 12:35:56 2007
@@ -23,8 +23,8 @@
import org.apache.tapestry.internal.test.InternalBaseTestCase;
import org.apache.tapestry.internal.transform.InheritedAnnotation;
import org.apache.tapestry.internal.transform.pages.*;
-import org.apache.tapestry.ioc.internal.services.PropertyAccessImpl;
import org.apache.tapestry.ioc.services.PropertyAccess;
+import org.apache.tapestry.ioc.util.BodyBuilder;
import org.apache.tapestry.runtime.Component;
import org.apache.tapestry.runtime.ComponentResourcesAware;
import org.apache.tapestry.services.ClassTransformation;
@@ -176,9 +176,8 @@
}
catch (RuntimeException ex)
{
- assertEquals(
- ex.getMessage(),
- "Class
org.apache.tapestry.internal.transform.pages.ParentClass does not contain a
field named 'unknownField'.");
+ assertEquals(ex.getMessage(),
+ "Class
org.apache.tapestry.internal.transform.pages.ParentClass does not contain a
field named 'unknownField'.");
}
verify();
@@ -323,9 +322,8 @@
}
catch (RuntimeException ex)
{
- assertEquals(
- ex.getMessage(),
- "Field _field4 of class
org.apache.tapestry.internal.transform.pages.ClaimedFields is already claimed
by Fred and can not be claimed by Barney.");
+ assertEquals(ex.getMessage(),
+ "Field _field4 of class
org.apache.tapestry.internal.transform.pages.ClaimedFields is already claimed
by Fred and can not be claimed by Barney.");
}
verify();
@@ -387,9 +385,7 @@
replay();
- ClassTransformation ct = createClassTransformation(
- ChildClassInheritsAnnotation.class,
- logger);
+ ClassTransformation ct =
createClassTransformation(ChildClassInheritsAnnotation.class, logger);
InheritedAnnotation ia = ct.getAnnotation(InheritedAnnotation.class);
@@ -428,8 +424,7 @@
}
@Test
- public void
ensure_javassist_does_not_show_interface_methods_on_abstract_class()
- throws Exception
+ public void
ensure_javassist_does_not_show_interface_methods_on_abstract_class() throws
Exception
{
CtClass ctClass = findCtClass(AbstractFoo.class);
@@ -449,8 +444,7 @@
}
@Test
- public void
ensure_javassist_does_not_show_extended_interface_methods_on_interface()
- throws Exception
+ public void
ensure_javassist_does_not_show_extended_interface_methods_on_interface() throws
Exception
{
CtClass ctClass = findCtClass(FooBarInterface.class);
@@ -473,8 +467,8 @@
replay();
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
// Default behavior is to add an injected field for the
InternalComponentResources object,
// so we'll just check that.
@@ -505,8 +499,8 @@
replay();
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_loader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _loader, logger,
+
null);
String parentFieldName = ct.addInjectedField(String.class, "_value",
value);
@@ -534,8 +528,8 @@
// This proves the the field is protected and can be used in
subclasses.
- ct.addMethod(new TransformMethodSignature(Modifier.PUBLIC,
"java.lang.String", "getValue",
- null, null), "return " +
subclassFieldName + ";");
+ ct.addMethod(new TransformMethodSignature(Modifier.PUBLIC,
"java.lang.String", "getValue", null, null),
+ "return " + subclassFieldName + ";");
ct.finish();
@@ -561,8 +555,8 @@
replay();
- InternalClassTransformation ct = new
InternalClassTransformationImpl(ctClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(ctClass, _contextClassLoader, logger,
+
null);
_classPool.toClass(ctClass, _loader);
@@ -573,9 +567,8 @@
}
catch (IllegalArgumentException ex)
{
- assertEquals(ex.getMessage(),
ServicesMessages.incorrectClassForInstantiator(
- BasicComponent.class.getName(),
- Boolean.class));
+ assertEquals(ex.getMessage(),
+
ServicesMessages.incorrectClassForInstantiator(BasicComponent.class.getName(),
Boolean.class));
}
verify();
@@ -592,8 +585,8 @@
replay();
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.addImplementedInterface(FooInterface.class);
ct.addImplementedInterface(GetterMethodsInterface.class);
@@ -604,8 +597,7 @@
Class[] interfaces = transformed.getInterfaces();
- assertEquals(interfaces, new Class[]
- {Component.class, FooInterface.class,
GetterMethodsInterface.class});
+ assertEquals(interfaces, new Class[]{Component.class,
FooInterface.class, GetterMethodsInterface.class});
Object target =
ct.createInstantiator(transformed).newInstance(resources);
@@ -640,31 +632,26 @@
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.makeReadOnly("_value");
ct.finish();
- Class transformed = _classPool.toClass(targetObjectCtClass, _loader);
-
- Object target =
ct.createInstantiator(transformed).newInstance(resources);
-
- PropertyAccess access = new PropertyAccessImpl();
+ Object target = instantiate(ReadOnlyBean.class, ct, resources);
try
{
- access.set(target, "value", "anything");
+ _access.set(target, "value", "anything");
unreachable();
}
catch (RuntimeException ex)
{
// The PropertyAccess layer adds a wrapper exception around the
real one.
- assertEquals(
- ex.getCause().getMessage(),
- "Field
org.apache.tapestry.internal.services.ReadOnlyBean._value is read-only.");
+ assertEquals(ex.getCause().getMessage(),
+ "Field
org.apache.tapestry.internal.services.ReadOnlyBean._value is read-only.");
}
verify();
@@ -679,8 +666,8 @@
CtClass targetObjectCtClass = findCtClass(RemoveFieldBean.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.removeField("_barney");
@@ -700,20 +687,16 @@
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.extendConstructor("_value = \"from constructor\";");
ct.finish();
- Class transformed = _classPool.toClass(targetObjectCtClass, _loader);
-
- Object target =
ct.createInstantiator(transformed).newInstance(resources);
-
- PropertyAccess access = new PropertyAccessImpl();
+ Object target = instantiate(ReadOnlyBean.class, ct, resources);
- assertEquals(access.get(target, "value"), "from constructor");
+ assertEquals(_access.get(target, "value"), "from constructor");
verify();
}
@@ -729,33 +712,28 @@
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.injectField("_value", "Tapestry");
ct.finish();
- Class transformed = _classPool.toClass(targetObjectCtClass, _loader);
-
- Object target =
ct.createInstantiator(transformed).newInstance(resources);
-
- PropertyAccess access = new PropertyAccessImpl();
+ Object target = instantiate(ReadOnlyBean.class, ct, resources);
- assertEquals(access.get(target, "value"), "Tapestry");
+ assertEquals(_access.get(target, "value"), "Tapestry");
try
{
- access.set(target, "value", "anything");
+ _access.set(target, "value", "anything");
unreachable();
}
catch (RuntimeException ex)
{
// The PropertyAccess layer adds a wrapper exception around the
real one.
- assertEquals(
- ex.getCause().getMessage(),
- "Field
org.apache.tapestry.internal.services.ReadOnlyBean._value is read-only.");
+ assertEquals(ex.getCause().getMessage(),
+ "Field
org.apache.tapestry.internal.services.ReadOnlyBean._value is read-only.");
}
verify();
@@ -776,8 +754,8 @@
CtClass targetObjectCtClass = findCtClass(FieldAccessBean.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
replaceAccessToField(ct, "foo");
replaceAccessToField(ct, "bar");
@@ -786,27 +764,23 @@
ct.finish();
- Class transformed = _classPool.toClass(targetObjectCtClass, _loader);
-
- Object target =
ct.createInstantiator(transformed).newInstance(resources);
+ Object target = instantiate(FieldAccessBean.class, ct, resources);
// target is no longer assignable to FieldAccessBean; its a new class
from a new class
// loader. So we use reflective access, which doesn't care about such
things.
- PropertyAccess access = new PropertyAccessImpl();
-
- checkReplacedFieldAccess(access, target, "foo");
- checkReplacedFieldAccess(access, target, "bar");
+ checkReplacedFieldAccess(target, "foo");
+ checkReplacedFieldAccess(target, "bar");
verify();
}
- private void checkReplacedFieldAccess(PropertyAccess access, Object
target, String propertyName)
+ private void checkReplacedFieldAccess(Object target, String propertyName)
{
try
{
- access.get(target, propertyName);
+ _access.get(target, propertyName);
unreachable();
}
catch (RuntimeException ex)
@@ -817,7 +791,7 @@
try
{
- access.set(target, propertyName, "new value");
+ _access.set(target, propertyName, "new value");
unreachable();
}
catch (RuntimeException ex)
@@ -832,23 +806,20 @@
String fieldName = "_" + baseName;
String readMethodName = "_read_" + baseName;
- TransformMethodSignature readMethodSignature = new
TransformMethodSignature(
- Modifier.PRIVATE, STRING_CLASS_NAME, readMethodName, null,
null);
+ TransformMethodSignature readMethodSignature = new
TransformMethodSignature(Modifier.PRIVATE, STRING_CLASS_NAME,
+
readMethodName, null, null);
- ct.addMethod(readMethodSignature, String.format(
- "throw new RuntimeException(\"read %s\");",
- baseName));
+ ct.addMethod(readMethodSignature, String.format("throw new
RuntimeException(\"read %s\");", baseName));
ct.replaceReadAccess(fieldName, readMethodName);
String writeMethodName = "_write_" + baseName;
- TransformMethodSignature writeMethodSignature = new
TransformMethodSignature(
- Modifier.PRIVATE, "void", writeMethodName, new String[]
- {STRING_CLASS_NAME}, null);
- ct.addMethod(writeMethodSignature, String.format(
- "throw new RuntimeException(\"write %s\");",
- baseName));
+ TransformMethodSignature writeMethodSignature = new
TransformMethodSignature(Modifier.PRIVATE, "void",
+
writeMethodName,
+
new String[]{STRING_CLASS_NAME},
+
null);
+ ct.addMethod(writeMethodSignature, String.format("throw new
RuntimeException(\"write %s\");", baseName));
ct.replaceWriteAccess(fieldName, writeMethodName);
}
@@ -988,9 +959,7 @@
ClassTransformation ct =
createClassTransformation(EventHandlerTarget.class, logger);
- OnEvent annotation = ct.getMethodAnnotation(
- new TransformMethodSignature("handler"),
- OnEvent.class);
+ OnEvent annotation = ct.getMethodAnnotation(new
TransformMethodSignature("handler"), OnEvent.class);
// Check that the attributes of the annotation match the expectation.
@@ -1016,9 +985,8 @@
}
catch (IllegalArgumentException ex)
{
- assertEquals(
- ex.getMessage(),
- "Class
org.apache.tapestry.internal.transform.pages.ParentClass does not declare
method 'public void foo()'.");
+ assertEquals(ex.getMessage(),
+ "Class
org.apache.tapestry.internal.transform.pages.ParentClass does not declare
method 'public void foo()'.");
}
verify();
@@ -1056,6 +1024,125 @@
}
@Test
+ public void fields_in_prefixed_methods_are_transformed() throws Exception
+ {
+ Logger logger = mockLogger();
+ TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PUBLIC, "int", "getTargetValue", null,
+ null);
+ Runnable runnable = mockRunnable();
+
+ runnable.run();
+
+ replay();
+
+ InternalClassTransformation ct =
createClassTransformation(MethodPrefixTarget.class, logger);
+
+ String name = ct.addInjectedField(Runnable.class, "runnable",
runnable);
+
+ // Transform the field.
+
+ TransformMethodSignature reader = new
TransformMethodSignature(Modifier.PRIVATE, "int", "read_target_value",
+ null,
null);
+
+ ct.addMethod(reader, "return 66;");
+
+ ct.replaceReadAccess("_targetField", "read_target_value");
+
+ ct.prefixMethod(sig, name + ".run();");
+
+ ct.finish();
+
+ Object target = instantiate(MethodPrefixTarget.class, ct, null);
+
+ // 66 reflects the change to the field.
+
+ assertEquals(_access.get(target, "targetValue"), 66);
+
+ verify();
+ }
+
+ private Component instantiate(Class<?> expectedClass,
InternalClassTransformation ct,
+ InternalComponentResources resources) throws
Exception
+ {
+ CtClass targetObjectCtClass = findCtClass(expectedClass);
+
+ Class transformedClass = _classPool.toClass(targetObjectCtClass,
_loader);
+
+ Instantiator ins = ct.createInstantiator(transformedClass);
+
+ return ins.newInstance(resources);
+ }
+
+ @Test
+ public void extend_existing_method_fields_are_transformed() throws
Exception
+ {
+ Logger logger = mockLogger();
+ TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PUBLIC, "int", "getTargetValue", null,
+ null);
+ Runnable runnable = mockRunnable();
+
+ runnable.run();
+
+ replay();
+
+ InternalClassTransformation ct =
createClassTransformation(MethodPrefixTarget.class, logger);
+
+ String name = ct.addInjectedField(Runnable.class, "runnable",
runnable);
+
+ // Transform the field.
+
+ TransformMethodSignature reader = new
TransformMethodSignature(Modifier.PRIVATE, "int", "read_target_value",
+ null,
null);
+
+ ct.addMethod(reader, "return 66;");
+
+ ct.replaceReadAccess("_targetField", "read_target_value");
+
+ BodyBuilder builder = new BodyBuilder();
+ builder.begin();
+ builder.addln("%s.run();", name);
+ builder.addln("return $_ + 1;");
+ builder.end();
+
+ ct.extendExistingMethod(sig, builder.toString());
+
+ ct.finish();
+
+ Object target = instantiate(MethodPrefixTarget.class, ct, null);
+
+ // 66 reflects the change to the field, +1 reflects the extension of
the method.
+
+ assertEquals(_access.get(target, "targetValue"), 67);
+
+ verify();
+ }
+
+ @Test
+ public void invalid_code() throws Exception
+ {
+ Logger logger = mockLogger();
+ TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PUBLIC, "int", "getParentField", null,
+ null);
+
+ replay();
+
+ InternalClassTransformation ct =
createClassTransformation(ParentClass.class, logger);
+
+ try
+ {
+ ct.prefixMethod(sig, "return supercalafragalistic;");
+ unreachable();
+ }
+ catch (MethodCompileException ex)
+ {
+
+ }
+
+ verify();
+ }
+
+
+ @Test
public void remove_field() throws Exception
{
Logger logger = mockLogger();
@@ -1064,8 +1151,8 @@
CtClass targetObjectCtClass = findCtClass(FieldRemoval.class);
- InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass,
-
_contextClassLoader, logger, null);
+ InternalClassTransformation ct = new
InternalClassTransformationImpl(targetObjectCtClass, _contextClassLoader,
+
logger, null);
ct.removeField("_fieldToRemove");
@@ -1097,10 +1184,11 @@
TransformMethodSignature sig = sigs.get(0);
- assertEquals(
- ct.getMethodIdentifier(sig),
-
"org.apache.tapestry.internal.transform.pages.MethodIdentifier.makeWaves(java.lang.String,
int[]) (at MethodIdentifier.java:24)");
+ assertEquals(ct.getMethodIdentifier(sig),
+
"org.apache.tapestry.internal.transform.pages.MethodIdentifier.makeWaves(java.lang.String,
int[]) (at MethodIdentifier.java:24)");
verify();
}
+
+
}
Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/MethodPrefixTarget.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/MethodPrefixTarget.java?rev=599949&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/MethodPrefixTarget.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/MethodPrefixTarget.java
Fri Nov 30 12:35:56 2007
@@ -0,0 +1,18 @@
+package org.apache.tapestry.internal.services;
+
+public class MethodPrefixTarget
+{
+ // If this is final, then the read is inlined, defeating the test.
+ private int _targetField = 42;
+
+ public int getTargetValue()
+ {
+ return _targetField;
+ }
+
+ // Again, necessary to defeat inlining of the value.
+ public void setTargetField(int value)
+ {
+ _targetField = value;
+ }
+}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.tml
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.tml?rev=599949&r1=599948&r2=599949&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.tml
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app2/pages/TestPrefixMethod.tml
Fri Nov 30 12:35:56 2007
@@ -1,3 +1,5 @@
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
- <span id="value">${value}</span>
+ <span id="value">${value}</span>
+ <span id="value2">${value2}</span>
+ <span id="value3">${value3}</span>
</html>