You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 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
+    }
+}