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/03/29 19:24:35 UTC
svn commit: r1086644 [4/5] - in /tapestry/tapestry5/trunk: ./ plastic/
plastic/src/ plastic/src/main/ plastic/src/main/java/
plastic/src/main/java/org/ plastic/src/main/java/org/apache/
plastic/src/main/java/org/apache/tapestry5/ plastic/src/main/java/...
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocation.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocation.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocation.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,79 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * A representation of the invocation of a method that allows the behavior of the method to be advised: either by
+ * changing parameter values, or by changing the return value, or by catch or throwing different exeptions.
+ */
+public interface MethodInvocation extends MethodInvocationResult
+{
+ /** The instance on which the method was originally invoked. */
+ Object getInstance();
+
+ InstanceContext getInstanceContext();
+
+ /**
+ * Proceed with the method invocation, either chaining into the next {@link MethodAdvice} added to the method, or
+ * ultimately into the actual method implementation. The method may throw a checked exception, which will be caught
+ * and be reported as {@link #didThrowCheckedException()}.
+ *
+ * @return this method invocation, for a fluent API
+ */
+ MethodInvocation proceed();
+
+ /**
+ * Overrides the return value of the method. The value provided will be cast to the actual return type
+ * (or, if the return type is a primitive value, the value will be cast to the corresponding wrapper type and then
+ * converted to a primitive).
+ *
+ * @param returnValue
+ * @return this method invocation, for a fluent API
+ * @throws NullPointerException
+ * if the method's return type is a primitive and null is provided
+ */
+ MethodInvocation setReturnValue(Object returnValue);
+
+ /**
+ * Returns the parameter at the given index. Primitive types will be wrapped as they are returned.
+ *
+ * @param index
+ * of parameter to access
+ * @return parameter value
+ */
+ Object getParameter(int index);
+
+ /**
+ * Changes a parameter value. The value will be cast to the parameter's type. For primitive types, the
+ * value will be cast to the corresponding wrapper type.
+ *
+ * @param index
+ * index of parameter to modify
+ * @param newValue
+ * new value for parameter
+ * @return this method invocation, for a fluent API
+ */
+ MethodInvocation setParameter(int index, Object newValue);
+
+ /**
+ * Sets the checked exception; this can be used to indicate failure for the method, or
+ * to cancel the thrown exception (by setting the exception to null).
+ *
+ * @param exception
+ * new checked exception, or null
+ * @return this method invocation, for a fluent API
+ */
+ MethodInvocation setCheckedException(Exception exception);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocationResult.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocationResult.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocationResult.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodInvocationResult.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,44 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * The result of a {@linkplain MethodHandle#invoke(Object, Object...) method invocation}, which
+ * encapsulates the actual return value (if any), as well as any checked exception.
+ */
+public interface MethodInvocationResult
+{
+ /**
+ * Returns the actual value returned from the method invocation, if any. This will be null
+ * if the invocation threw a checked exception, or the method itself is void.
+ */
+ Object getReturnValue();
+
+ /**
+ * If the invocation threw a checked exception, then this method throws that exception wrapped
+ * as the cause of a new RuntimeException. Otherwise, this method does nothing.
+ */
+ void rethrow();
+
+ /** Returns true if the method invocation threw a checked exception. */
+ boolean didThrowCheckedException();
+
+ /**
+ * Retrieves the checked exception assignable to the indicated type, or null if
+ * the invocation did not throw a checked exception or the thrown exception is not
+ * assignable.
+ */
+ <T extends Throwable> T getCheckedException(Class<T> exceptionType);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodParameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodParameter.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodParameter.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodParameter.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,31 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Access to the parameters of a method, in particular, any visible annotations
+ * on that method.
+ */
+public interface MethodParameter extends AnnotationAccess
+{
+ /** Returns the Java type name of the parameter. */
+ String getType();
+
+ /**
+ * Returns the index of the parameter (counting from zero). This numbering
+ * is compatible with {@link InstructionBuilder#loadArgument(int)}.
+ */
+ int getIndex();
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Opcodes.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Opcodes.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Opcodes.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Opcodes.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,33 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Used to document the {@link InstructionBuilder} and {@link TryCatchBlock} */
+@Documented
+@Target(
+{ METHOD })
+@Retention(RetentionPolicy.SOURCE)
+public @interface Opcodes
+{
+ /** Describes the Opcodes that may be emitted by this method. */
+ String value();
+}
Added: 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=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClass.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,152 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The representation of a class while it is being instrumented and transformed. PlasticClass allows
+ * for an imperative style of development: the PlastiClass is provided to other objects; they can query it
+ * for relevant fields or methods, and invoke methods that modify the class in various ways. Ultimately, the
+ * end result is a {@link ClassInstantiator} used to create instances of the fully instrumented and transformed class.
+ * <p>
+ * The terminology is that a class that is being transformed is "plastic", but the end result is a normal concrete class
+ * (albeit in a different class loader).
+ * <p>
+ * Implements {@link AnnotationAccess} to provide access to annotations on the type itself.
+ * <p>
+ * This class is expressly <em>not thread safe</em>; only a single thread should be responsible for operating on a
+ * PlasticClass.
+ * <p>
+ * TODO: what about annotation inheritance?
+ */
+@SuppressWarnings("rawtypes")
+public interface PlasticClass extends AnnotationAccess
+{
+ /** Returns the fully qualified class name of the class being transformed. */
+ String getClassName();
+
+ /**
+ * Matches all unclaimed fields that have the given annotation. Returns the fields in sorted order.
+ *
+ * @return Unmodifiable List of fields.
+ */
+ <T extends Annotation> List<PlasticField> getFieldsWithAnnotation(Class<T> annotationType);
+
+ /**
+ * Returns all non-introduced fields, in sorted order by name.
+ *
+ * @return Unmodifiable list of fields.
+ */
+ List<PlasticField> getAllFields();
+
+ /**
+ * Returns all unclaimed fields, in sorted order by name. This does not include introduced fields.
+ *
+ * @return Unmodifiable list of fields.
+ * @see PlasticField#claim(Object)
+ */
+ List<PlasticField> getUnclaimedFields();
+
+ /**
+ * Introduces a new private field into the class.
+ *
+ * @param typeName
+ * the Java class name for the field, or (possibly) a primitive type name or an array
+ * @param suggestedName
+ * the suggested name for the field, which may be modified to ensure that the field name
+ * is unique
+ * @return PlasticField for the introduced field
+ */
+ PlasticField introduceField(String typeName, String suggestedName);
+
+ /** Convenience method that uses a Java class rather than a type name. */
+ PlasticField introduceField(Class fieldType, String suggestedName);
+
+ /**
+ * Matches methods with the given annotation.
+ *
+ * @return Unmodifiable list of methods, in sorted order.
+ */
+ <T extends Annotation> List<PlasticMethod> getMethodsWithAnnotation(Class<T> annotationType);
+
+ /**
+ * Returns all methods of the class, in sorted order. This does not include static methods,
+ * or any {@linkplain #introduceMethod(MethodDescription) introduced methods}.
+ *
+ * @return Unmodifiable list of methods.
+ */
+ List<PlasticMethod> getMethods();
+
+ /**
+ * Returns an existing method declared in this class, or introduces a new method into this class.
+ * The method is created with default behavior. If the method overrides a non-private, non-abstract method
+ * implemented in a <em>transformed</em> super class, the the default behavior is to invoke that method and return
+ * its value. Otherwise, the default behavior is to ignore parameters and return 0, false, or null. Void methods
+ * will invoke the super-class implementation (if it exists) and return no value.
+ *
+ * @param description
+ * describes the method name, visibility, return value, etc.
+ * @return a new (or previously created) PlasticMethod for the method
+ * @throws IllegalArgumentException
+ * if the method is abstract or static
+ */
+ PlasticMethod introduceMethod(MethodDescription description);
+
+ /**
+ * A convenience that creates a {@link MethodDescription} from the Method and introduces that. This is often
+ * invoked when walking the methods of an interface and introducing each of those methods.
+ * <p>
+ * Introduced methods are always concrete, not abstract. The abstract flag on the method modifiers will always be
+ * stripped off, which is handy when {@linkplain #introduceInterface(Class) introducing methods from an interface}.
+ *
+ * @param method
+ * to introduce
+ * @return new (or previously created) PlasticMethod
+ */
+ PlasticMethod introduceMethod(Method method);
+
+ /**
+ * Introduces each method defined by the interface into the class. Determines which new methods must
+ * be introduced in order to ensure that all methods of the interface are implemented. The newly introduced methods,
+ * if any, are returned.
+ */
+ Set<PlasticMethod> introduceInterface(Class interfaceType);
+
+ /**
+ * Introduces the interface, and then invokes {@link PlasticMethod#delegateTo(PlasticField)} on each method
+ * defined by the interface.
+ *
+ * @param interfaceType
+ * defines the interface to proxy
+ * @param field
+ * field containing an object to delegate to
+ * @return this plastic class, for further configuration
+ */
+ PlasticClass proxyInterface(Class interfaceType, PlasticField field);
+
+ /**
+ * Conditionally adds an implementation of <code>toString()</code> to the class, but only if it is not already
+ * present in the class, or in a (transformed) super-class.
+ *
+ * @param toStringValue
+ * the fixed value to be returned from invoking toString()
+ * @return this plastic class, for further configuration
+ */
+ PlasticClass addToString(String toStringValue);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClassTransformer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClassTransformer.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClassTransformer.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticClassTransformer.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,27 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Interface for defining how a {@link PlasticClass} is transformed. Often, a series of transformers are applied, in
+ * a specific order.
+ */
+public interface PlasticClassTransformer
+{
+ /**
+ * Perform whatever transformations are appropriate on this PlasticClass.
+ */
+ void transform(PlasticClass plasticClass);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticField.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,130 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Represents a field of a class being {@linkplain PlasticClass transformed}.
+ * <p>
+ * No methods of this object should be invoked after the class transformation is
+ * {@linkplain PlasticClass#createInstantiator() completed}.
+ */
+public interface PlasticField extends AnnotationAccess
+{
+ /**
+ * Returns a handle that can be used to directly access a private field of a
+ * transformed class instance.
+ */
+ FieldHandle getHandle();
+
+ /**
+ * Returns the name of the field.
+ */
+ String getName();
+
+ /**
+ * Returns the fully qualified class name for the field's type or (for a primitive type)
+ * the primitive type name ("int", "char", etc.). For array types, the returned name includes
+ * a "[]" suffix.
+ */
+ String getTypeName();
+
+ /**
+ * Claims the field, used to indicate that the field is "processed". A field may only
+ * be claimed once. Claiming a field is intended as a mechanism to detect or prevent
+ * conflicts between different isolated transformations of the field. The tag value used does not matter, and is
+ * typically either an annotation (that drove the transformation) or the instance of {@link PlasticClassTransformer}
+ * that performed the transformation. That tag value is only used when generating the error message for the case
+ * where a field is claimed for than once.
+ *
+ * @throws RuntimeException
+ * if the field is claimed a second time
+ * @throws AssertionError
+ * if tag is null
+ * @see PlasticClass#getUnclaimedFields()
+ * @return the field for further manipulation
+ */
+ PlasticField claim(Object tag);
+
+ /**
+ * Returns true if the field has already been {@linkplain #claim(Object) claimed}.
+ *
+ * @see PlasticClass#getUnclaimedFields()
+ */
+ boolean isClaimed();
+
+ /**
+ * Converts the field to be read-only, and provide the indicated value. The field's value will be
+ * set inside the class' constructor.
+ *
+ * @param value
+ * to inject, which must be type compatible with the field (possibly, a wrapper type if the field is
+ * a primitive value). The value may not be null.
+ * @return the field for further manipulation
+ * @throws IllegalStateException
+ * if the field already has an injection, or the field has a conduit
+ */
+ PlasticField inject(Object value);
+
+ /**
+ * Converts the field to be read-only, and provide the value, which is computed
+ * indirectly inside the class' constructor.
+ *
+ * @param computedValue
+ * provides the actual value to be injected, and must return a value type compatible
+ * with the field (possibly a wrapper type if the field is a primitive value). The computedValue may not
+ * be null.
+ * @return the field for further manipulation
+ * @throws IllegalStateException
+ * if the field already has an injection, or the field has a conduit
+ */
+ PlasticField injectComputed(ComputedValue<?> computedValue);
+
+ /**
+ * Intercepts all access to the field, replacing such access with calls on the conduit. Even access via
+ * the FieldHandle will instead delegate to the conduit. Once a conduit is provided, it is not possible
+ * to inject a value into the field.
+ *
+ * @return the field for further manipulation
+ * @throws IllegalStateException
+ * if the field already has an injection or a conduit
+ */
+ PlasticField setConduit(FieldConduit<?> conduit);
+
+ /**
+ * Creates access to the field, using the default property name derived from the name of the field.
+ * The default property name is the same as the name of the field, but with any leading or trailing underscore
+ * characters removed (a common convention among some programmers). Also, strips leading "m_" from the field name
+ * (another
+ * common convention).
+ *
+ * @param accessType
+ * which methods to create
+ * @return the field for further manipulation
+ */
+ PlasticField createAccessors(PropertyAccessType accessType);
+
+ /**
+ * Creates accessors, possibly replacing existing methods (or overriding methods from a super class).
+ * The method names consist of the property name, with its first character converted to upper-case, prefixed
+ * with "get" or "set".
+ *
+ * @param accessType
+ * which methods to create
+ * @param propertyName
+ * the name of the property (from which the names of the methods are generated)
+ * @return the field for further manipulation
+ */
+ PlasticField createAccessors(PropertyAccessType accessType, String propertyName);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManager.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,134 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.tapestry5.internal.plastic.NoopDelegate;
+import org.apache.tapestry5.internal.plastic.PlasticClassPool;
+import org.apache.tapestry5.internal.plastic.PlasticClassTransformation;
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+
+/**
+ * Manages the internal class loaders and other logics necessary to load and transform existing classes,
+ * or to create new classes dynamically at runtime.
+ */
+public class PlasticManager
+{
+ private final PlasticClassPool pool;
+
+ /**
+ * Creates a PlasticManager using the Thread's contextClassLoader as the parent class loader. No classes will
+ * be automatically transformed, but instead {@link #createClass(Class, CreateClassCallback)} can be used to create
+ * entirely new classes.
+ */
+ public PlasticManager()
+ {
+ this(Thread.currentThread().getContextClassLoader(), new NoopDelegate(), Collections.<String> emptySet());
+ }
+
+ /**
+ * The standard constructor for PlasticManager, allowing a parent class loader to be specified (this is most often
+ * the thread's contextClassLoader), as well as the delegate and the names of all controlled packages.
+ *
+ * @param parentClassLoader
+ * main source for (untransformed) classes
+ * @param delegate
+ * performs transformations on top-level classes from controlled packages
+ * @param controlledPackageName
+ * defines the packages that are to be transformed; top-classes in these packages
+ * (or sub-packages) will be passed to the delegate for transformation
+ */
+ public PlasticManager(ClassLoader parentClassLoader, PlasticManagerDelegate delegate,
+ Set<String> controlledPackageName)
+ {
+ assert parentClassLoader != null;
+ assert delegate != null;
+
+ pool = new PlasticClassPool(parentClassLoader, delegate, controlledPackageName);
+ }
+
+ /**
+ * Returns the ClassLoader that is used to instantiate transformed classes. The parent class loader
+ * of the returned class loader is the class loader provided to
+ * {@link #PlasticManager(ClassLoader, PlasticManagerDelegate, Set)}
+ *
+ * @return class loader
+ */
+ public ClassLoader getClassLoader()
+ {
+ return pool.getClassLoader();
+ }
+
+ /**
+ * This method is used only in testing to get the PlasticClass directly, bypassing the normal code paths. This
+ * is only invoked by Groovy tests which fudges the fact that the same class implements both PlasticClass and
+ * PlasticClassTransformation.
+ * TODO: This may make a kind of callback when we get to proxy creation, rather then pure transformation.
+ * TODO: Clean up this mess!
+ *
+ * @throws ClassNotFoundException
+ */
+ PlasticClassTransformation getPlasticClass(String className) throws ClassNotFoundException
+ {
+ assert PlasticInternalUtils.isNonBlank(className);
+
+ return pool.getPlasticClassTransformation(className);
+ }
+
+ /**
+ * Gets the {@link ClassInstantiator} for the indicated class, which must be in a transformed package.
+ *
+ * @param className
+ * fully qualified class name
+ * @return instantiator (configured via the
+ * {@linkplain PlasticManagerDelegate#configureInstantiator(String, ClassInstantiator) delegate} for the
+ * class
+ * @throws IllegalArgumentException
+ * if the class is not a transformed class
+ */
+ public ClassInstantiator getClassInstantiator(String className)
+ {
+ return pool.getClassInstantiator(className);
+ }
+
+ /**
+ * Creates an entirely new class, extending from the provided base class.
+ *
+ * @param baseClass
+ * class to extend from, which must be a class, not an interface
+ * @param callback
+ * used to configure the new class
+ * @return the instantiator, which allows instances of the new class to be created
+ */
+ public ClassInstantiator createClass(Class baseClass, PlasticClassTransformer callback)
+ {
+ assert baseClass != null;
+ assert callback != null;
+
+ if (baseClass.isInterface())
+ throw new IllegalArgumentException(String.format("Class %s defines an interface, not a base class.",
+ baseClass.getName()));
+
+ String name = String.format("$PlasticProxy$%s_%s", baseClass.getSimpleName(), PlasticUtils.nextUID());
+
+ PlasticClassTransformation transformation = pool.createTransformation(baseClass.getName(), name);
+
+ callback.transform(transformation.getPlasticClass());
+
+ return transformation.createInstantiator();
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManagerDelegate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManagerDelegate.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManagerDelegate.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticManagerDelegate.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,33 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Delegate to the {@link PlasticManager} that performs the actual transformations of the class.
+ * Transformations only occur on main classes, not on inner classes.
+ */
+public interface PlasticManagerDelegate extends PlasticClassTransformer
+{
+ /**
+ * Configures the instantiator for a transformed PlasticClass.
+ *
+ * @param className
+ * fully qualified class name that was transformed
+ * @param instantiator
+ * default instantiator, which has an empty {@link InstanceContext}
+ * @return the same instantiator, or a new one configured with additional {@link InstanceContext} values
+ */
+ ClassInstantiator configureInstantiator(String className, ClassInstantiator instantiator);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticMethod.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticMethod.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticMethod.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticMethod.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,100 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+import java.util.List;
+
+/**
+ * A method of a {@linkplain PlasticClass transformed class}.
+ * <p>
+ * No methods of this object should be invoked after the class transformation is
+ * {@linkplain PlasticClass#createInstantiator() completed}.
+ */
+public interface PlasticMethod extends AnnotationAccess
+{
+ /**
+ * Returns a representation of the method's name, return value, argument types, etc.
+ */
+ MethodDescription getDescription();
+
+ /**
+ * Returns a handle that can be used to invoke a method of a transformed class instance.
+ */
+ MethodHandle getHandle();
+
+ /**
+ * Clears the instructions for this method, and creates a new empty InstructionBuilder so that the implementation of
+ * the method can be specified. This may be considered a kind of last resort when no other approach is sufficient.
+ * <p>
+ * If the method is currently abstract, it will have its abstract flag cleared.
+ * <p>
+ * If the method has advice, the advice is <em>not</em> lost but will instead wrap around the new method
+ * implementation.
+ *
+ * @return this method, for further configuration
+ * @param callback
+ * passed the InstructionBuilder so that an implementation of the method can be created
+ */
+ PlasticMethod changeImplementation(InstructionBuilderCallback callback);
+
+ /**
+ * Adds advice to the method. Adding advice implicitly rewrites the implementation of the method (this occurs
+ * inside at the end of the class transformation). When the method is invoked, control will flow
+ * through the MethodAdvice <em>in the order they are added</em>. Each piece of advice will receive the
+ * {@link MethodInvocation} and should invoke {@link MethodInvocation#proceed()} to pass control to the next piece
+ * of advice (and ultimately, to the actual method invocation).
+ * <p>
+ * If a method implementation is changed, using {@link #changeImplementation(InstructionBuilderCallback)}, that
+ * change will be honored, but the logic will only be invoked at the end of the chain of MethodAdvice. Internally, a
+ * new method is created with the same parameters, exceptions, return type and implementation (bytecode) as the
+ * advised method, <em>then</em> the advised method's implementation is changed.
+ * <p>
+ * Note additionally that a recursive method invocation will still invoke the MethodAdvice chain on each recursive
+ * call (this is an intended side-effect of copying the exact bytecode of the method implementation.
+ *
+ * @param advice
+ * advice to add to the method
+ * @return this method, for further configuration
+ */
+ PlasticMethod addAdvice(MethodAdvice advice);
+
+ /**
+ * Changes the implementation of the method to delegate to the provided field. The field must implement the
+ * correct interface (or extend the correct class). The original implementation of the method is lost,
+ * though (as with {@link #changeImplementation(InstructionBuilderCallback)}), method advice is retained.
+ *
+ * @param field
+ * to delegate to
+ * @return this method, for further configuration
+ */
+ PlasticMethod delegateTo(PlasticField field);
+
+ /**
+ * Much like {@link #delegateTo(PlasticField)}, but the object to delegate to
+ * is dynamically computed by another method of the class. The method should take no parameters
+ * and must not return null, or throw any exceptions not compatible with the method being proxied.
+ *
+ * @param method
+ * to provide the dynamic delegate
+ * @return this method, for further configuration
+ */
+ PlasticMethod delegateTo(PlasticMethod method);
+
+ /**
+ * Returns access to the parameters of the method and, in particular,
+ * the visible annotations on those parameters.
+ */
+ List<MethodParameter> getParameters();
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,40 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Utilities for user code making use of Plastic.
+ */
+public class PlasticUtils
+{
+ private static final AtomicLong UID_GENERATOR = new AtomicLong(System.nanoTime());
+
+ /**
+ * Returns a string that can be used as part of a Java identifier and is unique
+ * for this JVM. Currently returns a hexadecimal string and initialized by
+ * System.nanoTime() (but both those details may change in the future).
+ * <p>
+ * Note that the returned value may start with a numeric digit, so it should be used as a <em>suffix</em>, not
+ * <em>prefix</em> of a Java identifier.
+ *
+ * @return unique id that can be used as part of a Java identifier
+ */
+ public static String nextUID()
+ {
+ return Long.toHexString(PlasticUtils.UID_GENERATOR.getAndIncrement());
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyAccessType.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyAccessType.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyAccessType.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyAccessType.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,33 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Used when converting a field into a property (that is, adding accessor methods for the field) to identify
+ * which method(s) to create (a getter to access the value and/or a mutator to modify the value).
+ *
+ * @see PlasticField#createAccessors(PropertyAccessType, String)
+ */
+public enum PropertyAccessType
+{
+ /** Create just a getter, not a mutator. */
+ READ_ONLY,
+
+ /** Create just a mutator, not a getter. */
+ WRITE_ONLY,
+
+ /** Create both a mutator and a getter. */
+ READ_WRITE;
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchBlock.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchBlock.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchBlock.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchBlock.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,47 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Support for building the equivalent of a Java switch statement.
+ */
+public interface SwitchBlock
+{
+ /**
+ * Adds a handler for a particular case value. This method should only be invoked at most once for each case
+ * value.
+ *
+ * @param caseValue
+ * value to match
+ * @param jumpToEnd
+ * true if a jump to the end should be provided, or false
+ * if either the callback generated a return opcode, or
+ * it is desired to "drop down" into the next case handler.
+ * The last case handled drop down out of the SwitchBlock.
+ * @param callback
+ * provides the logic for the specified case
+ */
+ void addCase(int caseValue, boolean jumpToEnd, InstructionBuilderCallback callback);
+
+ /**
+ * Adds the default handler. This is optional, and is only allowed after all cases have been added.
+ * The default handler automatically throws an {@link IllegalArgumentException}.
+ *
+ * @param callback
+ * provides the logic for the default handler case.
+ */
+ void addDefault(InstructionBuilderCallback callback);
+
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchCallback.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchCallback.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchCallback.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/SwitchCallback.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,21 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/** A callback used by a {@link SwitchBlock} to manage the generation of code. */
+public interface SwitchCallback
+{
+ void doSwitch(SwitchBlock block);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchBlock.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchBlock.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchBlock.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchBlock.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,58 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * Allows a portion of a method to be marked so that exception and finally handlers can be provided.
+ *
+ * @see InstructionBuilder#startTryCatch(InstructionBuilderCallback, TryCatchCallback)
+ */
+public interface TryCatchBlock
+{
+ /**
+ * Invoked first, to generate the code in which exceptions may be caught.
+ *
+ * @param callback
+ */
+ void addTry(InstructionBuilderCallback callback);
+
+ /**
+ * Ends the block (if not already ended) and inserts a catch block for the named exception.
+ * The InstructionBuilder is returned so that the code for handling the exception can be added. The exception object
+ * will be on top of the stack. This should be called after {@link #addTry(InstructionBuilderCallback)}.
+ * <p>
+ * Note: no attempt is made currently to sort the handlers; for example adding a catch for java.lang.Exception first
+ * will mean that more specific exception handlers added later will never be invoked.
+ *
+ * @param exceptionClassName
+ * caught exception class
+ * @param callback
+ * that implements the logic of the catch block
+ */
+ @Opcodes("TRYCATCHBLOCK")
+ void addCatch(String exceptionClassName, InstructionBuilderCallback callback);
+
+ /**
+ * As with {@link #addCatch(String, org.jplastic.core.InstructionBuilder.Callback))}, but the exception caught is
+ * null, which acts as a finally block in the Java language. This must be called last (after
+ * {@link #addTry(InstructionBuilderCallback)} and any calls to
+ * {@link #addCatch(String, InstructionBuilderCallback)}.
+ *
+ * @param callback
+ * implements the logic of the finally block
+ */
+ @Opcodes("TRYCATCHBLOCK")
+ void addFinally(InstructionBuilderCallback callback);
+}
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchCallback.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchCallback.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchCallback.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/TryCatchCallback.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,24 @@
+// Copyright 2011 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.tapestry5.plastic;
+
+/**
+ * A callback used with {@link InstructionBuilder#startTryCatch(InstructionBuilderCallback, TryCatchCallback)}.
+ */
+public interface TryCatchCallback
+{
+ /** Invoked by the block to allow exception and finally handlers to be added. */
+ void doBlock(TryCatchBlock block);
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/ClassInstantiatorTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/ClassInstantiatorTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/ClassInstantiatorTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/ClassInstantiatorTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,71 @@
+// Copyright 2011 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.tapestry5.internal.plastic
+
+import java.text.DateFormat
+
+import spock.lang.Specification
+import testsubjects.ContextCatcher
+
+class ClassInstantiatorTests extends Specification
+{
+
+ def ins = new ClassInstantiatorImpl(ContextCatcher.class, ContextCatcher.class.constructors[0], null)
+
+ def "adding a context value returns a new instantiator"() {
+
+ String value = "instance value of type String";
+
+ when:
+ def ins2 = ins.with(String.class, value)
+
+ then:
+ ! ins2.is(ins)
+
+ ins2.get(String.class).is(value)
+ }
+
+ def "may not add a duplicate instance context value"() {
+
+ when:
+ ins.with(String.class, "initial value").with(String.class, "conflicting value")
+
+ then:
+ def e = thrown(IllegalStateException)
+
+ e.message == "An instance context value of type java.lang.String has already been added."
+ }
+
+ def "get a value not stored is a failure"() {
+ when:
+ ins.get(DateFormat.class)
+
+ then:
+ def e = thrown(IllegalArgumentException)
+ e.message == "Instance context for class testsubjects.ContextCatcher does not contain a value for type class java.text.DateFormat."
+ }
+
+ def "instance map wrapped as InstanceContext and passed to constructed object"() {
+ def value = "instance value of type String"
+
+ when:
+
+ def o = ins.with(String.class, value).newInstance()
+
+ then:
+
+ o.instanceContext.get(String.class).is(value)
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/PlasticUtilsTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/PlasticUtilsTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/PlasticUtilsTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/internal/plastic/PlasticUtilsTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,111 @@
+// Copyright 2011 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.tapestry5.internal.plastic
+
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class PlasticUtilsTests extends Specification
+{
+ def "toDescriptor handles primitive, object and array types"() {
+ expect:
+ PlasticInternalUtils.toDescriptor(className) == desc
+
+ where:
+ className | desc
+ "void" | "V"
+ "boolean" | "Z"
+ "boolean[]" | "[Z"
+ "java.lang.Integer" | "Ljava/lang/Integer;"
+ "java.lang.String[]" | "[Ljava/lang/String;"
+ "java.lang.Long[][]" | "[[Ljava/lang/Long;"
+ }
+
+ def "descriptorToClassName for proper description"() {
+ expect:
+ PlasticInternalUtils.objectDescriptorToClassName(descriptor) == className
+ where:
+ descriptor | className
+ "Ljava/lang/String;" | "java.lang.String"
+ 'Lfoo/bar/Baz$Biff;' | 'foo.bar.Baz$Biff'
+ }
+
+ def "not object descriptor is an exception"() {
+ when:
+ PlasticInternalUtils.objectDescriptorToClassName("I")
+
+ then:
+ def e = thrown(IllegalArgumentException)
+ e.message == "Input 'I' is not an object descriptor."
+ }
+
+ @Unroll("#featureName[field '#fieldName' should convert to property '#propertyName']")
+ def "conversion of field name to property name"() {
+ expect:
+
+ PlasticInternalUtils.toPropertyName (fieldName) == propertyName
+
+ where:
+
+ fieldName | propertyName
+
+ "fred" | "fred"
+
+ "m_fred" | "fred"
+
+ "M_fred"| "fred"
+
+ "barney_" | "barney"
+
+ "m_wilma_" | "wilma"
+
+ "foo_bar" | "foo_bar"
+
+ "_______overkill________" | "overkill"
+
+ "m____overkill____" | "overkill"
+
+ "m_" | "m"
+ }
+
+ def "bad input for field name to property name conversion"() {
+ when:
+
+ PlasticInternalUtils.toPropertyName ""
+
+ then:
+
+ thrown(IllegalArgumentException)
+ }
+
+ @Unroll("#featureName['#input' should capitalize to '#output']")
+ def "capitalize strings"() {
+ expect:
+
+ PlasticInternalUtils.capitalize(input) == output
+
+ where:
+
+ input | output
+
+ "Hello" | "Hello"
+
+ "g" | "G"
+
+ "goodbye" | "Goodbye"
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/AbstractPlasticSpecification.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/AbstractPlasticSpecification.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/AbstractPlasticSpecification.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/AbstractPlasticSpecification.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,22 @@
+package org.apache.tapestry5.plastic
+
+import org.apache.tapestry5.internal.plastic.StandardDelegate
+
+import spock.lang.Specification
+
+abstract class AbstractPlasticSpecification extends Specification {
+ PlasticMethod findMethod(pc, name) {
+ pc.methods.find { it.description.methodName == name }
+ }
+
+ MethodHandle findHandle(pc, name) {
+ findMethod(pc, name)?.handle
+ }
+
+ PlasticManager createMgr(PlasticClassTransformer... transformers) {
+ def delegate = new StandardDelegate(transformers)
+ def packages = ["testsubjects"]as Set
+
+ return new PlasticManager(Thread.currentThread().contextClassLoader, delegate, packages)
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ArrayAttributeAnnotations.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ArrayAttributeAnnotations.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ArrayAttributeAnnotations.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ArrayAttributeAnnotations.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,55 @@
+package org.apache.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+import testsubjects.ArrayAnnotation
+import testsubjects.Truth;
+
+class ArrayAttributeAnnotations extends Specification {
+ def mgr = new PlasticManager()
+
+ def "handling of array attribute defaults"() {
+ when:
+ def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+
+ def a = pc.getAnnotation(ArrayAnnotation.class)
+
+ then:
+ a.numbers().length == 0
+ a.strings().length == 0
+ a.types().length == 0
+ a.annotations().length == 0
+ }
+
+ def "explicit values for array attributes"() {
+ when:
+ def pc = mgr.getPlasticClass("testsubjects.ArrayAttributesSubject")
+ def a = pc.getAnnotation(ArrayAnnotation.class)
+
+ then:
+
+ a.numbers() == [5]
+
+ a.strings() == ["frodo", "sam"]
+
+ a.types() == [Runnable.class]
+
+ a.annotations().length == 2
+ a.annotations()[0].value() == Truth.YES
+ a.annotations()[1].value() == Truth.NO
+ }
+
+ def "handling of explicitly empty array attributes"() {
+ when:
+ def pc = mgr.getPlasticClass("testsubjects.ExplicityEmptyArrayAttributesSubject")
+
+ def a = pc.getAnnotation(ArrayAnnotation.class)
+
+ then:
+ a.numbers().length == 0
+ a.strings().length == 0
+ a.types().length == 0
+ a.annotations().length == 0
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ClassAnnotationAccess.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ClassAnnotationAccess.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ClassAnnotationAccess.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/ClassAnnotationAccess.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,33 @@
+package org.apache.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+import testsubjects.SimpleAnnotation
+
+class ClassAnnotationAccess extends Specification {
+ def mgr = new PlasticManager()
+ def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+
+ def "access to non-existent annotation"() {
+
+ expect:
+
+ pc.hasAnnotation(Deprecated.class) == false
+ pc.getAnnotation(Deprecated.class) == null
+ }
+
+ def "check existence of known, simple annotation"() {
+
+ expect:
+ pc.hasAnnotation(SimpleAnnotation.class) == true
+
+ when:
+ def a = pc.getAnnotation(SimpleAnnotation.class)
+
+ then:
+ a instanceof SimpleAnnotation
+
+ a.annotationType() == SimpleAnnotation.class
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,36 @@
+package org.apache.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification
+import testsubjects.Maybe
+
+class FieldAnnotationAccess extends Specification {
+ def mgr = new PlasticManager()
+
+ def "locate field by annotation"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+
+ when:
+ def fields = pc.getFieldsWithAnnotation(Maybe.class)
+
+ then:
+ fields.size() == 1
+ fields[0].name == "hasMaybeAnnotation"
+ }
+
+ def "claimed fields not visible to getFieldsWithAnnotation()"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+
+ when:
+ def fields = pc.getFieldsWithAnnotation(Maybe.class)
+
+ fields[0].claim(this)
+
+ then:
+
+ pc.getFieldsWithAnnotation(Maybe.class).empty
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldClaiming.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldClaiming.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldClaiming.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldClaiming.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,59 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+
+class FieldClaiming extends Specification
+{
+ def mgr = new PlasticManager()
+
+ def "get fields ignores claimed fields"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.SingleField")
+ def f = pc.unclaimedFields.first()
+
+ expect:
+ f.name == "myField"
+ ! f.claimed
+
+ when:
+ def f2 = f.claim("my tag")
+
+ then:
+ f2.is(f)
+
+ f.claimed
+ pc.unclaimedFields == []
+ pc.allFields == [f]
+ }
+
+ def "a field may only be claimed once"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.SingleField")
+ def f = pc.unclaimedFields.first()
+
+ f.claim "[first tag]"
+
+ when:
+ f.claim "[second tag]"
+
+ then:
+ def e = thrown(IllegalStateException)
+ e.message == "Field myField of class testsubjects.SingleField can not be claimed by [second tag] as it is already claimed by [first tag]."
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldConduitTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,76 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.FieldConduit;
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification
+
+class FieldConduitTests extends Specification
+{
+ def mgr = new PlasticManager()
+
+ def "setting a field invokes the conduit"() {
+
+ FieldConduit fc = Mock()
+
+ def pc = mgr.getPlasticClass("testsubjects.IntFieldHolder")
+
+ pc.allFields.first().setConduit(fc)
+
+ def o = pc.createInstantiator().newInstance()
+
+ when:
+
+ o.setValue(123)
+
+ then:
+
+ 1 * fc.set(_, 123)
+
+ when:
+ fc.get(_) >>> 999
+
+ then:
+
+ o.getValue() == 999
+
+ expect:
+
+ // The field doesn't really get used (it may be removed some day, though its useful because of the
+ // annotations that may be on it).
+
+ o.@value == 0
+ }
+
+ def "field initializations are visible to the conduit"() {
+ FieldConduit fc = Mock()
+
+ def pc = mgr.getPlasticClass("testsubjects.LongFieldHolder")
+
+ pc.allFields.first().setConduit(fc)
+
+ when:
+
+ pc.createInstantiator().newInstance()
+
+ then:
+
+ // 100 is the initial value of the field
+
+ 1 * fc.set(_, 100)
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldHandleTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldHandleTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldHandleTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldHandleTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,165 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+
+class FieldHandleTests extends Specification
+{
+ def mgr = new PlasticManager()
+
+ def "getting access to a new primitive field"() {
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ def f = pc.introduceField("int", "count")
+ def h = f.handle
+
+ def ins = pc.createInstantiator()
+
+ when:
+ def empty = ins.newInstance()
+
+ // Use Groovy to access the field directly
+
+ empty.@count = 77
+
+ then:
+ h.get(empty) == 77
+
+ when:
+ h.set(empty, 99)
+
+ then:
+ empty.@count == 99
+ }
+
+ /**
+ * Useful to check this, since long and double take up two slots in the stack and may behave
+ * differently.
+ */
+ def "access to existing long field"() {
+ def pc = mgr.getPlasticClass("testsubjects.LongFieldHolder")
+
+ def f = pc.allFields.first()
+ def h = f.handle
+
+ def ins = pc.createInstantiator()
+
+ when:
+ def holder = ins.newInstance()
+
+ // Use Groovy to access the field directly
+
+ holder.@value = Long.MAX_VALUE
+
+ then:
+ h.get(holder) == Long.MAX_VALUE
+
+ when:
+ h.set(holder, 99l)
+
+ then:
+ holder.@value == 99l
+ }
+
+ /**
+ * Default value for fields are set inside the default
+ * <init> constructor method; since Plastic creates a new
+ * constructor, it must find the existing constructor and
+ * execute it, after it does its own initializations.
+ */
+ def "default value for field is not lost"() {
+
+ def pc = mgr.getPlasticClass("testsubjects.LongFieldHolder")
+
+ def f = pc.allFields.first()
+ def h = f.handle
+
+ def ins = pc.createInstantiator()
+ def holder = ins.newInstance()
+
+ expect:
+ h.get(holder) == 100l
+ }
+
+ def "access to reference field"() {
+ def pc = mgr.getPlasticClass("testsubjects.StringHolder")
+
+ def f = pc.allFields.first()
+ def h = f.handle
+
+ def holder = pc.createInstantiator().newInstance()
+
+ when:
+ String alpha = "alpha"
+ String beta = "beta"
+
+ holder.@value = alpha
+
+ then:
+ holder.@value.is(alpha)
+ h.get(holder).is(alpha)
+
+ when:
+ h.set(holder, beta)
+
+ then:
+ holder.@value.is(beta)
+ h.get(holder).is(beta)
+ }
+
+ def "access to multiple fields in single class"() {
+ def pc = mgr.getPlasticClass("testsubjects.MultipleFields")
+
+ def fred = handleByName(pc, "fred")
+ def barney = handleByName(pc, "barney")
+ def wilma = handleByName(pc, "wilma")
+ def betty = handleByName(pc, "betty")
+
+ def instance = pc.createInstantiator().newInstance()
+
+ when:
+
+ fred.set(instance, 99)
+
+ then:
+ instance.fred == 99
+
+ when:
+
+ barney.set(instance, "rubble")
+
+ then:
+
+ instance.barney == "rubble"
+
+
+ // skip wilma since its a List<Long>
+
+ when:
+
+ betty.set(instance, 101)
+
+ then:
+
+ instance.betty == 101
+ }
+
+ def handleByName(pc, name) {
+ pc.allFields.find({ it.name == name}).handle
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldInjection.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldInjection.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldInjection.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldInjection.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,160 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.ComputedValue;
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+import testsubjects.StringPropertyHolder;
+
+class FieldInjection extends Specification
+{
+ def mgr = new PlasticManager()
+
+ def "injection of a reference value"() {
+ String injected = "Value injected into the Empty class"
+
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ def f = pc.introduceField("java.lang.String", "stringValue");
+
+ f.inject(injected);
+
+ def ins = pc.createInstantiator()
+
+ def o = ins.newInstance()
+
+ expect:
+ o.stringValue.is(injected)
+ }
+
+ def "computed injection"() {
+
+ def instanceType
+
+ String injected = "Computed value injected into the StringPropertyHolder class"
+
+ def pc = mgr.getPlasticClass("testsubjects.StringPropertyHolder")
+
+ pc.allFields.first().injectComputed({
+
+ instanceType = it.instanceType
+
+ return it.get(String.class)
+ } as ComputedValue)
+
+ def o = pc.createInstantiator().with(String.class, injected).newInstance()
+
+ expect:
+
+ o.value.is(injected)
+ instanceType.is(o.getClass())
+
+ ! instanceType.is(StringPropertyHolder.class) // it's a new class with the same name
+
+ when:
+ o.value = "attempt to update"
+
+ then:
+ thrown(IllegalStateException)
+ }
+
+ def "injection of primitive value"() {
+
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ def f = pc.introduceField("double", "pi");
+
+ f.inject(Math.PI);
+
+ def ins = pc.createInstantiator()
+
+ def o = ins.newInstance()
+
+ expect:
+ o.pi == Math.PI
+ }
+
+
+ def "injected field is read-only"() {
+
+ def pc = mgr.getPlasticClass("testsubjects.InjectFieldSubject")
+
+ def pf = pc.allFields.first();
+
+ def handle = pf.handle;
+
+ pf.inject(99)
+
+ def ins = pc.createInstantiator()
+ def o = ins.newInstance()
+
+ expect:
+ o.getValue() == 99
+
+ when:
+ o.setValue(123)
+
+ then:
+ def e = thrown(IllegalStateException)
+ e.message == "Field value of class testsubjects.InjectFieldSubject is read-only."
+
+ when:
+ handle.set(o, 456)
+
+ then:
+
+ thrown(IllegalStateException)
+ }
+
+ def "injected field is read-only, even via handle"() {
+ def pc = mgr.getPlasticClass("testsubjects.InjectFieldSubject")
+ def f = pc.allFields.first();
+ def h = f.handle
+
+ f.inject(66)
+
+ def ins = pc.createInstantiator()
+ def o = ins.newInstance()
+
+ expect:
+ o.getValue() == 66
+
+ when:
+ h.set(o, 123)
+
+ then:
+ def e = thrown(IllegalStateException)
+ e.message == "Field value of class testsubjects.InjectFieldSubject is read-only."
+ }
+
+ def "a field may only be injected once"() {
+ def pc = mgr.getPlasticClass("testsubjects.StringHolder")
+ def f = pc.allFields.first();
+
+ f.inject("[first]")
+
+ when:
+
+ f.inject("[second]")
+
+ then:
+
+ def e = thrown(IllegalStateException)
+
+ e.message == "Unable to inject a value into field value of class testsubjects.StringHolder, as it already has an injection."
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldPropertyMethodCreation.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldPropertyMethodCreation.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldPropertyMethodCreation.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldPropertyMethodCreation.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,65 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+import org.apache.tapestry5.plastic.PropertyAccessType;
+
+import spock.lang.Specification;
+import testsubjects.Property;
+
+class FieldPropertyMethodCreation extends Specification
+{
+ def "create accessors for fields"() {
+ setup:
+
+ def mgr = new PlasticManager()
+ def pc = mgr.getPlasticClass ("testsubjects.CreateAccessorsSubject")
+
+ pc.getFieldsWithAnnotation(Property.class).each { f -> f.createAccessors(PropertyAccessType.READ_WRITE) }
+
+ def o = pc.createInstantiator().newInstance()
+
+ when:
+
+ o.m_title = "via direct field access"
+
+ then:
+
+ assert o.m_title == o.title
+
+
+ when:
+ o.title = "via generated accessor"
+
+ then: "Setting object property reflected in original field"
+
+ assert o.m_title == o.title
+
+
+ when:
+ o.m_count = 1
+
+ then: "Updates to primitive field reflected in generated getter"
+
+ assert o.m_count == o.count
+
+ when:
+ o.count = 2
+
+ then: "Setting primitive property reflected in original field"
+ assert o.m_count == o.count
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/IntroduceFieldTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/IntroduceFieldTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/IntroduceFieldTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/IntroduceFieldTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,90 @@
+// Copyright 2011 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.tapestry5.plastic
+
+import java.lang.reflect.Modifier;
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification
+
+class IntroduceFieldTests extends Specification
+{
+ def mgr = new PlasticManager()
+
+ def "introduced fields are not visible in the allFields list"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ expect:
+ pc.allFields == []
+
+ when:
+ def f = pc.introduceField("java.lang.String", "message")
+
+ then:
+ f.toString() == "PlasticField[private java.lang.String message (in class testsubjects.Empty)]"
+ f.name == "message"
+ pc.allFields == []
+ }
+
+ def "introducing a duplicate field name results in a unique id"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ when:
+ def f1 = pc.introduceField("java.lang.Integer", "count")
+ def f2 = pc.introduceField("java.lang.Integer", "count")
+
+ then:
+ ! f1.is(f2)
+ f1.name == "count"
+
+ f2.name != "count"
+ f2.name.startsWith("count")
+ }
+
+
+ def "instantiate a class with an introduced field"() {
+ setup:
+ def pc = mgr.getPlasticClass("testsubjects.Empty")
+
+ pc.introduceField("java.lang.Integer", "count")
+
+ def ins = pc.createInstantiator()
+
+ when:
+ def empty = ins.newInstance()
+ def f = empty.class.getDeclaredField("count")
+
+ // Use Groovy to access the field directly
+
+ empty.@count = 77
+
+ then:
+ empty.@count == 77
+
+ when:
+ empty.@count = 99
+
+ then:
+ empty.@count == 99
+
+ expect:
+
+ f.modifiers == Modifier.PRIVATE
+ f.type == Integer.class
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAdviceTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAdviceTests.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAdviceTests.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAdviceTests.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,86 @@
+package org.apache.tapestry5.plastic
+
+import java.sql.SQLException
+
+import org.apache.tapestry5.plastic.test.NoopAdvice
+
+class MethodAdviceTests extends AbstractPlasticSpecification {
+ def "advice for a void method"() {
+ setup:
+
+ def didInvoke = false
+
+ def mgr = createMgr( { PlasticClass pc ->
+
+ findMethod(pc, "aSingleMethod").addAdvice ({
+ didInvoke = true
+ it.proceed()
+ } as MethodAdvice)
+ } as PlasticClassTransformer)
+
+ when:
+
+ def o = mgr.getClassInstantiator("testsubjects.SingleMethod").newInstance()
+
+ then:
+
+ didInvoke == false
+
+ when:
+
+ o.aSingleMethod()
+
+ then:
+
+ didInvoke == true
+ }
+
+ def "multiple advice on method with parameters and return values"() {
+
+ setup:
+
+ def mgr = createMgr( { PlasticClass pc ->
+ findMethod(pc, "dupe").addAdvice( {
+
+ it.setParameter(0, it.getParameter(0) + 2)
+ it.proceed()
+ } as MethodAdvice).addAdvice ( {
+
+ it.setParameter(0, it.getParameter(0) * 3)
+ it.proceed()
+
+ it.setReturnValue(it.getReturnValue().toUpperCase())
+ } as MethodAdvice)
+ } as PlasticClassTransformer)
+
+ def o = mgr.getClassInstantiator("testsubjects.MethodAdviceTarget").newInstance()
+
+ expect:
+
+ o.dupe(2, "Fam") == "FAM FAM FAM FAM FAM FAM FAM FAM FAM FAM FAM FAM"
+ }
+
+ def "method that throws exceptions"() {
+
+ setup:
+
+ def mgr = createMgr({ PlasticClass pc ->
+ findMethod(pc, "maybeThrow").addAdvice(new NoopAdvice())
+ } as PlasticClassTransformer)
+
+ def o = mgr.getClassInstantiator("testsubjects.MethodAdviceTarget").newInstance()
+
+ expect:
+
+ o.maybeThrow(7) == 7
+
+ when:
+
+ o.maybeThrow(0)
+
+ then:
+
+ thrown(SQLException)
+
+ }
+}
Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAnnotationAccess.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAnnotationAccess.groovy?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAnnotationAccess.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodAnnotationAccess.groovy Tue Mar 29 17:24:31 2011
@@ -0,0 +1,58 @@
+package org.apache.tapestry5.plastic
+
+import org.apache.tapestry5.plastic.PlasticManager;
+
+import spock.lang.Specification;
+import testsubjects.Maybe
+import testsubjects.Outer
+import testsubjects.PrimitiveValues
+import testsubjects.Truth
+
+class MethodAnnotationAccess extends Specification {
+ def mgr = new PlasticManager()
+ def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+
+ def "access to method annotation with enum attribute"() {
+
+ when:
+
+ def methods = pc.getMethodsWithAnnotation(Maybe.class)
+ def noMethod = methods[0]
+ def yesMethod = methods[1]
+
+ then:
+
+ methods.size == 2
+
+ noMethod.description.methodName == "no"
+ yesMethod.description.methodName == "yes"
+
+ noMethod.getAnnotation(Maybe.class).value() == Truth.NO
+ yesMethod.getAnnotation(Maybe.class).value() == Truth.YES
+ }
+
+ def "method annotation with primitive attributes"() {
+ when:
+ def methods = pc.getMethodsWithAnnotation(PrimitiveValues.class)
+
+ then:
+ methods.size == 1
+
+ when:
+ def pv = methods[0].getAnnotation(PrimitiveValues.class)
+
+ then:
+ pv.count() == 5
+ pv.title() == "runnables" // explicit
+ pv.type() == Runnable.class
+ pv.message() == "created" // default
+ }
+
+ def "nested annotation as attribute of outer annotation"() {
+ when:
+ def ann = pc.getMethodsWithAnnotation(Outer.class)[0].getAnnotation(Outer.class)
+
+ then:
+ ann.maybe().value() == Truth.YES
+ }
+}