tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject svn commit: r1086644 [3/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/...
Date Tue, 29 Mar 2011 17:24:35 GMT
Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,428 @@
+// 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.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.plastic.AnnotationAccess;
+import org.apache.tapestry5.plastic.ClassInstantiator;
+import org.apache.tapestry5.plastic.PlasticManagerDelegate;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.ClassNode;
+
+/**
+ * Responsible for managing a class loader that allows ASM {@link ClassNode}s
+ * to be instantiated as runtime classes.
+ */
+@SuppressWarnings("rawtypes")
+public class PlasticClassPool implements ClassLoaderDelegate, Opcodes
+{
+    final PlasticClassLoader loader;
+
+    private final PlasticManagerDelegate delegate;
+
+    private final Set<String> controlledPackages;
+
+    private final Map<String, ClassInstantiator> instantiators = new HashMap<String, ClassInstantiator>();
+
+    private final MethodBundle emptyMethodBundle = new MethodBundle();
+
+    private final StaticContext emptyStaticContext = new StaticContext();
+
+    private final Cache<String, TypeCategory> typeName2Category = new Cache<String, TypeCategory>()
+    {
+
+        protected TypeCategory convert(String typeName)
+        {
+            ClassNode cn = createClassNode(typeName);
+
+            return Modifier.isInterface(cn.access) ? TypeCategory.INTERFACE : TypeCategory.CLASS;
+        }
+    };
+
+    class BaseClassDef
+    {
+        final MethodBundle methodBundle;
+
+        final StaticContext staticContext;
+
+        public BaseClassDef(MethodBundle methodBundle, StaticContext staticContext)
+        {
+            this.methodBundle = methodBundle;
+            this.staticContext = staticContext;
+        }
+    }
+
+    /** Map from FQCN to BaseClassDef. */
+    private final Map<String, BaseClassDef> baseClassDefs = new HashMap<String, PlasticClassPool.BaseClassDef>();
+
+    /**
+     * Creates the pool with a set of controlled packages; all classes in the controlled packages are loaded by the
+     * pool's class loader, and all top-level classes in the controlled packages are transformed via the delegate.
+     * 
+     * @param parentLoader
+     *            typically, the Thread's context class loader
+     * @param delegate
+     *            responsible for end stages of transforming top-level classes
+     * @param controlledPackages
+     *            set of package names (note: retained, not copied)
+     */
+    public PlasticClassPool(ClassLoader parentLoader, PlasticManagerDelegate delegate, Set<String> controlledPackages)
+    {
+        loader = new PlasticClassLoader(parentLoader, this);
+        this.delegate = delegate;
+        this.controlledPackages = controlledPackages;
+    }
+
+    public ClassLoader getClassLoader()
+    {
+        return loader;
+    }
+
+    public synchronized Class realizeTransformedClass(ClassNode classNode, MethodBundle methodBundle,
+            StaticContext staticContext)
+    {
+        Class result = realize(classNode);
+
+        baseClassDefs.put(result.getName(), new BaseClassDef(methodBundle, staticContext));
+
+        return result;
+    }
+
+    public Class realize(ClassNode classNode)
+    {
+        PlasticInternalUtils.debugClass(classNode);
+
+        byte[] bytecode = toBytecode(classNode);
+
+        return loader.defineClassWithBytecode(PlasticInternalUtils.toClassName(classNode.name), bytecode);
+    }
+
+    private byte[] toBytecode(ClassNode classNode)
+    {
+        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+
+        classNode.accept(writer);
+
+        return writer.toByteArray();
+    }
+
+    public AnnotationAccess createAnnotationAccess(List<AnnotationNode> annotationNodes)
+    {
+        if (annotationNodes == null)
+            return EmptyAnnotationAccess.SINGLETON;
+
+        final Map<String, Object> cache = PlasticInternalUtils.newMap();
+        final Map<String, AnnotationNode> nameToNode = PlasticInternalUtils.newMap();
+
+        for (AnnotationNode node : annotationNodes)
+        {
+            nameToNode.put(PlasticInternalUtils.objectDescriptorToClassName(node.desc), node);
+        }
+
+        return new AnnotationAccess()
+        {
+
+            public <T extends Annotation> boolean hasAnnotation(Class<T> annotationType)
+            {
+                return nameToNode.containsKey(annotationType.getName());
+            }
+
+            public <T extends Annotation> T getAnnotation(Class<T> annotationType)
+            {
+                String className = annotationType.getName();
+
+                Object result = cache.get(className);
+
+                if (result == null)
+                {
+                    result = buildAnnotation(className);
+
+                    if (result != null)
+                        cache.put(className, result);
+                }
+
+                return annotationType.cast(result);
+            }
+
+            private Object buildAnnotation(String className)
+            {
+                AnnotationNode node = nameToNode.get(className);
+
+                if (node == null)
+                    return null;
+
+                return createAnnotation(className, node);
+            }
+        };
+    }
+
+    Class loadClass(String className)
+    {
+        try
+        {
+            return loader.loadClass(className);
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(String.format("Unable to load class %s: %s", className,
+                    PlasticInternalUtils.toMessage(ex)), ex);
+        }
+    }
+
+    protected Object createAnnotation(String className, AnnotationNode node)
+    {
+        AnnotationBuilder builder = new AnnotationBuilder(loadClass(className), this);
+
+        node.accept(builder);
+
+        return builder.createAnnotation();
+    }
+
+    public boolean shouldInterceptClassLoading(String className)
+    {
+        int searchFromIndex = className.length() - 1;
+
+        while (true)
+        {
+            int dotx = className.lastIndexOf('.', searchFromIndex);
+
+            if (dotx < 0)
+                break;
+
+            String packageName = className.substring(0, dotx);
+
+            if (controlledPackages.contains(packageName))
+                return true;
+
+            searchFromIndex = dotx - 1;
+        }
+
+        return false;
+    }
+
+    public Class<?> loadAndTransformClass(String className) throws ClassNotFoundException
+    {
+        // Inner classes are not transformed, but they are loaded by the same class loader.
+
+        if (className.contains("$"))
+            return loadInnerClass(className);
+
+        // TODO: What about interfaces, enums, annotations, etc. ... they shouldn't be in the package, but
+        // we should generate a reasonable error message.
+
+        PlasticClassTransformation transformation = getPlasticClassTransformation(className);
+
+        delegate.transform(transformation.getPlasticClass());
+
+        ClassInstantiator createInstantiator = transformation.createInstantiator();
+        ClassInstantiator configuredInstantiator = delegate.configureInstantiator(className, createInstantiator);
+
+        instantiators.put(className, configuredInstantiator);
+
+        return transformation.getTransformedClass();
+    }
+
+    private Class loadInnerClass(String className)
+    {
+        byte[] bytecode = readBytecode(className);
+
+        return loader.defineClassWithBytecode(className, bytecode);
+    }
+
+    /**
+     * For a fully-qualified class name of an <em>existing</em> class, loads the bytes for the class
+     * and returns a PlasticClass instance.
+     * 
+     * @throws ClassNotFoundException
+     */
+    public PlasticClassTransformation getPlasticClassTransformation(String className) throws ClassNotFoundException
+    {
+        assert PlasticInternalUtils.isNonBlank(className);
+
+        ClassNode classNode = createClassNode(className);
+
+        String baseClassName = PlasticInternalUtils.toClassName(classNode.superName);
+
+        return createTransformation(baseClassName, classNode);
+    }
+
+    private PlasticClassTransformation createTransformation(String baseClassName, ClassNode classNode)
+            throws ClassNotFoundException
+    {
+        if (shouldInterceptClassLoading(baseClassName))
+        {
+            loader.loadClass(baseClassName);
+
+            BaseClassDef def = baseClassDefs.get(baseClassName);
+
+            assert def != null;
+
+            return new PlasticClassImpl(classNode, this, def.methodBundle, def.staticContext);
+        }
+
+        return new PlasticClassImpl(classNode, this, emptyMethodBundle, emptyStaticContext);
+    }
+
+    private ClassNode createClassNode(String className)
+    {
+        byte[] bytecode = readBytecode(className);
+
+        return convertBytecodeToClassNode(bytecode);
+    }
+
+    private ClassNode convertBytecodeToClassNode(byte[] bytecode)
+    {
+        ClassReader cr = new ClassReader(bytecode);
+
+        ClassNode result = new ClassNode();
+
+        cr.accept(result, 0);
+
+        return result;
+    }
+
+    private byte[] readBytecode(String className)
+    {
+        ClassLoader parentClassLoader = loader.getParent();
+
+        String path = PlasticInternalUtils.toClassPath(className);
+
+        InputStream stream = parentClassLoader.getResourceAsStream(path);
+
+        if (stream == null)
+            throw new RuntimeException(String.format("Unable to locate class file for '%s' in class loader %s.",
+                    className, parentClassLoader));
+
+        try
+        {
+            return readBytestream(stream);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(String.format("Failure reading bytecode for class %s: %s", className,
+                    PlasticInternalUtils.toMessage(ex)), ex);
+        }
+        finally
+        {
+            PlasticInternalUtils.close(stream);
+        }
+    }
+
+    private byte[] readBytestream(InputStream stream) throws IOException
+    {
+        byte[] buffer = new byte[5000];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        while (true)
+        {
+            int length = stream.read(buffer);
+
+            if (length < 0)
+                break;
+
+            bos.write(buffer, 0, length);
+        }
+
+        bos.close();
+
+        return bos.toByteArray();
+    }
+
+    public PlasticClassTransformation createTransformation(String baseClassName, String newClassName)
+    {
+        try
+        {
+            ClassNode newClassNode = new ClassNode();
+
+            newClassNode.visit(V1_5, ACC_PUBLIC, PlasticInternalUtils.toInternalName(newClassName), null,
+                    PlasticInternalUtils.toInternalName(baseClassName), null);
+
+            return createTransformation(baseClassName, newClassNode);
+        }
+        catch (ClassNotFoundException ex)
+        {
+            throw new RuntimeException(String.format("Unable to create class %s as sub-class of %s: %s", newClassName,
+                    baseClassName, PlasticInternalUtils.toMessage(ex)), ex);
+        }
+    }
+
+    public synchronized ClassInstantiator getClassInstantiator(String className)
+    {
+        if (!instantiators.containsKey(className))
+        {
+            try
+            {
+                loader.loadClass(className);
+            }
+            catch (ClassNotFoundException ex)
+            {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        ClassInstantiator result = instantiators.get(className);
+
+        if (result == null)
+        {
+            // TODO: Verify that the problem is incorrect package, and not any other failure.
+
+            StringBuilder b = new StringBuilder();
+            b.append("Class '")
+                    .append(className)
+                    .append("' is not a transformed class. Transformed classes should be in one of the following packages: ");
+
+            String sep = "";
+
+            List<String> names = new ArrayList<String>(controlledPackages);
+            Collections.sort(names);
+
+            for (String name : names)
+            {
+                b.append(sep);
+                b.append(name);
+
+                sep = ", ";
+            }
+
+            String message = b.append(".").toString();
+
+            throw new IllegalArgumentException(message);
+        }
+
+        return result;
+    }
+
+    TypeCategory getTypeCategory(String typeName)
+    {
+        // TODO: Is this the right place to cache this data?
+
+        return typeName2Category.get(typeName);
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassTransformation.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassTransformation.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassTransformation.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,45 @@
+// 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.plastic.ClassInstantiator;
+import org.apache.tapestry5.plastic.InstanceContext;
+import org.apache.tapestry5.plastic.PlasticClass;
+
+public interface PlasticClassTransformation
+{
+    /**
+     * Returns the PlasticClass being transformed.
+     * 
+     * @return PlasticClass instance
+     */
+    PlasticClass getPlasticClass();
+
+    /**
+     * Terminates the class transformation process, finishes any final bookkeeping, and
+     * returns an object used to instantiate the transformed class. Once this method is invoked,
+     * no other methods of the {@link PlasticClass} (or related objects) can be invoked.
+     * <p>
+     * The returned ClassInstantiator has an empty {@link InstanceContext} map. Use
+     * {@link ClassInstantiator#with(Class, Object)} to create a new ClassInstantiator with new InstanceContext entries.
+     */
+    ClassInstantiator createInstantiator();
+
+    /**
+     * Only invokable after {@link #createInstantiator()}, returns the transformed Class instance, as loaded
+     * by a {@link PlasticClassLoader}.
+     */
+    Class<?> getTransformedClass();
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,296 @@
+// 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.io.Closeable;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.tapestry5.plastic.MethodDescription;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+@SuppressWarnings("rawtypes")
+public class PlasticInternalUtils
+{
+
+    private static final String SEP = "================================================";
+
+    public static final boolean DEBUG_ENABLED = Boolean.getBoolean("plastic-classnode-debug");
+
+    public static final String[] EMPTY = new String[0];
+
+    public static boolean isEmpty(Object[] input)
+    {
+        return input == null || input.length == 0;
+    }
+
+    public static String[] orEmpty(String[] input)
+    {
+        return input == null ? EMPTY : input;
+    }
+
+    public static boolean isBlank(String input)
+    {
+        return input == null || input.length() == 0 || input.trim().length() == 0;
+    }
+
+    public static boolean isNonBlank(String input)
+    {
+        return !isBlank(input);
+    }
+
+    public static String toInternalName(String className)
+    {
+        assert isNonBlank(className);
+
+        return className.replace('.', '/');
+    }
+
+    public static String toClassPath(String className)
+    {
+        return toInternalName(className) + ".class";
+    }
+
+    public static String toMessage(Throwable t)
+    {
+        String message = t.getMessage();
+
+        return isBlank(message) ? t.getClass().getName() : message;
+    }
+
+    public static void close(Closeable closeable)
+    {
+        try
+        {
+            if (closeable != null)
+                closeable.close();
+        }
+        catch (IOException ex)
+        {
+            // Ignore it.
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public static MethodDescription toMethodDescription(MethodNode node)
+    {
+        String returnType = Type.getReturnType(node.desc).getClassName();
+
+        String[] arguments = toClassNames(Type.getArgumentTypes(node.desc));
+
+        List<String> exceptions = node.exceptions;
+
+        String[] exceptionClassNames = new String[exceptions.size()];
+
+        for (int i = 0; i < exceptionClassNames.length; i++)
+        {
+            exceptionClassNames[i] = exceptions.get(i).replace('/', '.');
+        }
+
+        return new MethodDescription(node.access, returnType, node.name, arguments, exceptionClassNames);
+    }
+
+    private static String[] toClassNames(Type[] types)
+    {
+        if (isEmpty(types))
+            return EMPTY;
+
+        String[] result = new String[types.length];
+
+        for (int i = 0; i < result.length; i++)
+        {
+            result[i] = types[i].getClassName();
+        }
+
+        return result;
+    }
+
+    /**
+     * Converts a class's internal name (i.e., using slashes)
+     * to Java source code format (i.e., using periods).
+     */
+    public static String toClassName(String internalName)
+    {
+        assert isNonBlank(internalName);
+
+        return internalName.replace('/', '.');
+    }
+
+    /**
+     * Converts a primitive type or fully qualified class name (or array form) to
+     * a descriptor.
+     * <ul>
+     * <li>boolean --&gt; Z
+     * <li>
+     * <li>java.lang.Integer --&gt; Ljava/lang/Integer;</li>
+     * <li>char[] -->&gt; [C</li>
+     * <li>java.lang.String[][] --&gt; [[java/lang/String;
+     * </ul>
+     */
+    public static String toDescriptor(String className)
+    {
+        String buffer = className;
+        int arrayDepth = 0;
+
+        while (buffer.endsWith("[]"))
+        {
+            arrayDepth++;
+            buffer = buffer.substring(0, buffer.length() - 2);
+        }
+
+        // Get the description of the base element type, then figure out if and
+        // how to identify it as an array type.
+
+        PrimitiveType type = PrimitiveType.getByName(buffer);
+
+        String baseDesc = type == null ? "L" + buffer.replace('.', '/') + ";" : type.descriptor;
+
+        if (arrayDepth == 0)
+            return baseDesc;
+
+        StringBuilder b = new StringBuilder();
+
+        for (int i = 0; i < arrayDepth; i++)
+        {
+            b.append('[');
+        }
+
+        b.append(baseDesc);
+
+        return b.toString();
+    }
+
+    private static final Pattern DESC = Pattern.compile("^L(.*);$");
+
+    /**
+     * Converts an object type descriptor (i.e. "Ljava/lang/Object;") to a class name
+     * ("java.lang.Object").
+     */
+    public static String objectDescriptorToClassName(String descriptor)
+    {
+        assert descriptor != null;
+
+        Matcher matcher = DESC.matcher(descriptor);
+
+        if (!matcher.matches())
+            throw new IllegalArgumentException(String.format("Input '%s' is not an object descriptor.", descriptor));
+
+        return toClassName(matcher.group(1));
+    }
+
+    public static <K, V> Map<K, V> newMap()
+    {
+        return new HashMap<K, V>();
+    }
+
+    public static <T> Set<T> newSet()
+    {
+        return new HashSet<T>();
+    }
+
+    public static <T> List<T> newList()
+    {
+        return new ArrayList<T>();
+    }
+
+    /**
+     * Converts a type (including array and primitive types) to their type name (the way they are represented in Java
+     * source files).
+     */
+    public static String toTypeName(Class type)
+    {
+        if (type.isArray())
+            return toTypeName(type.getComponentType()) + "[]";
+
+        return type.getName();
+    }
+
+    /** Converts a number of types (usually, arguments to a method or constructor) into their type names. */
+    public static String[] toTypeNames(Class[] types)
+    {
+        String[] result = new String[types.length];
+
+        for (int i = 0; i < result.length; i++)
+            result[i] = toTypeName(types[i]);
+
+        return result;
+    }
+
+    public static void debugClass(ClassNode classNode)
+    {
+        if (!DEBUG_ENABLED)
+            return;
+
+        PrintWriter pw = new PrintWriter(System.out);
+
+        TraceClassVisitor visitor = new TraceClassVisitor(pw);
+
+        System.out.println(SEP);
+
+        classNode.accept(visitor);
+
+        pw.flush();
+
+        System.out.println(SEP);
+    }
+
+    private static final Pattern PROPERTY_PATTERN = Pattern.compile("^(m?_+)?(.+?)_*$", Pattern.CASE_INSENSITIVE);
+
+    /**
+     * Strips out leading and trailing underscores, leaving the real property name.
+     * In addition, "m_foo" is converted to "foo".
+     * 
+     * @param fieldName
+     * @return
+     */
+    public static String toPropertyName(String fieldName)
+    {
+        Matcher matcher = PROPERTY_PATTERN.matcher(fieldName);
+
+        if (!matcher.matches())
+            throw new IllegalArgumentException(String.format(
+                    "Field name '%s' can not be converted to a property name.", fieldName));
+
+        return matcher.group(2);
+    }
+
+    /**
+     * Capitalizes the input string, converting the first character to upper case.
+     * 
+     * @param input
+     *            a non-empty string
+     * @return the same string if already capitalized, or a capitalized version
+     */
+    public static String capitalize(String input)
+    {
+        char first = input.charAt(0);
+
+        if (Character.isUpperCase(first))
+            return input;
+
+        return String.valueOf(Character.toUpperCase(first)) + input.substring(1);
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PrimitiveType.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PrimitiveType.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PrimitiveType.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PrimitiveType.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,118 @@
+// 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.util.HashMap;
+import java.util.Map;
+
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Collects together information needed to write code that involves primitive types, including
+ * moving between wrapper types and primitive values, or extracting a primitive value from
+ * the {@link StaticContext}.
+ */
+@SuppressWarnings("rawtypes")
+public enum PrimitiveType implements Opcodes
+{
+    VOID("void", "V", null, null, null, ILOAD, ISTORE, RETURN),
+
+    BOOLEAN("boolean", "Z", Boolean.class, "booleanValue", "getBoolean", ILOAD, ISTORE, IRETURN),
+
+    CHAR("char", "C", Character.class, "charValue", "getChar", ILOAD, ISTORE, IRETURN),
+
+    BYTE("byte", "B", Byte.class, "byteValue", "getByte", ILOAD, ISTORE, IRETURN),
+
+    SHORT("short", "S", Short.class, "shortValue", "getShort", ILOAD, ISTORE, IRETURN),
+
+    INT("int", "I", Integer.class, "intValue", "getInt", ILOAD, ISTORE, IRETURN),
+
+    FLOAT("float", "F", Float.class, "floatValue", "getFloat", FLOAD, FSTORE, FRETURN),
+
+    LONG("long", "J", Long.class, "longValue", "getLong", LLOAD, LSTORE, LRETURN),
+
+    DOUBLE("double", "D", Double.class, "doubleValue", "getDouble", DLOAD, DSTORE, DRETURN);
+
+    /**
+     * @param name
+     *            the Java source name for the type
+     * @param descriptor
+     *            Java descriptor for the type ('Z', 'I', etc.)
+     * @param wrapperType
+     *            wrapper type, e.g., java.lang.Integer
+     * @param toValueMethodName
+     *            name of method of wrapper class to extract primitive value
+     * @param getFromStaticContextMethodName
+     *            name of method of {@link StaticContext} used to extract primitive context value
+     * @param loadOpcode
+     *            Correct opcode for loading an argument or local variable onto the stack (ILOAD, LLOAD, FLOAD or
+     *            DLOAD)
+     * @param storeOpcode
+     *            matching opcode for storing a value to a local variable (ISTORE, LSTORE, FSTORE or DSTORE)
+     * @param returnOpcode
+     *            Correct opcode for returning the top value on the stack (IRETURN, LRETURN, FRETURN
+     *            or DRETURN)
+     */
+    private PrimitiveType(String name, String descriptor, Class wrapperType, String toValueMethodName,
+            String getFromStaticContextMethodName, int loadOpcode, int storeOpcode, int returnOpcode)
+    {
+        this.name = name;
+        this.descriptor = descriptor;
+        this.wrapperType = wrapperType;
+        this.wrapperInternalName = wrapperType == null ? null : PlasticInternalUtils.toInternalName(wrapperType
+                .getName());
+        this.toValueMethodName = toValueMethodName;
+        this.getFromStaticContextMethodName = getFromStaticContextMethodName;
+        this.loadOpcode = loadOpcode;
+        this.storeOpcode = storeOpcode;
+        this.returnOpcode = returnOpcode;
+
+        this.valueOfMethodDescriptor = String.format("(%s)L%s;", descriptor, wrapperInternalName);
+        this.toValueMethodDescriptor = "()" + descriptor;
+        this.getFromStaticContextMethodDescriptor = "(I)" + descriptor;
+    }
+
+    public final String name, descriptor, wrapperInternalName, valueOfMethodDescriptor, toValueMethodName,
+            getFromStaticContextMethodName, toValueMethodDescriptor, getFromStaticContextMethodDescriptor;
+
+    public final Class wrapperType;
+
+    public final int loadOpcode, storeOpcode, returnOpcode;
+
+    private static final Map<String, PrimitiveType> BY_NAME = new HashMap<String, PrimitiveType>();
+    private static final Map<String, PrimitiveType> BY_DESC = new HashMap<String, PrimitiveType>();
+
+    static
+    {
+        for (PrimitiveType type : values())
+        {
+            BY_NAME.put(type.name, type);
+            BY_DESC.put(type.descriptor, type);
+        }
+    }
+
+    /**
+     * Returns the primitive type matching the given type name or null for a non-primitive type (an array type,
+     * or an class name).
+     * 
+     * @param name
+     *            possible primitive name
+     * @return the type or null
+     */
+    public static PrimitiveType getByName(String name)
+    {
+        return BY_NAME.get(name);
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StandardDelegate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StandardDelegate.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StandardDelegate.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StandardDelegate.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,36 @@
+// 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.plastic.PlasticClass;
+import org.apache.tapestry5.plastic.PlasticClassTransformer;
+
+public class StandardDelegate extends NoopDelegate
+{
+    private final PlasticClassTransformer[] transformers;
+
+    public StandardDelegate(PlasticClassTransformer... transformers)
+    {
+        this.transformers = transformers;
+    }
+
+    @Override
+    public void transform(PlasticClass plasticClass)
+    {
+        for (PlasticClassTransformer t : transformers)
+            t.transform(plasticClass);
+    }
+
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StaticContext.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StaticContext.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StaticContext.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/StaticContext.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,62 @@
+// 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.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stores static context information needed by a transformed PlasticClass; this includes data such as
+ * injections.
+ */
+public class StaticContext
+{
+    private final List<Object> values;
+
+    public StaticContext()
+    {
+        this(new ArrayList<Object>());
+    }
+
+    private StaticContext(List<Object> values)
+    {
+        this.values = values;
+    }
+
+    /**
+     * Duplicates this StaticContext, which is used when creating a subclass
+     * of a transformed class.
+     * 
+     * @return new StaticContext with the same values
+     */
+    public StaticContext dupe()
+    {
+        return new StaticContext(new ArrayList<Object>(values));
+    }
+
+    /** Store a context value and return its index. */
+    public int store(Object value)
+    {
+        values.add(value);
+
+        return values.size() - 1;
+    }
+
+    public Object get(int index)
+    {
+        return values.get(index);
+    }
+
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SuccessMethodInvocationResult.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SuccessMethodInvocationResult.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SuccessMethodInvocationResult.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SuccessMethodInvocationResult.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,49 @@
+// 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.plastic.MethodInvocationResult;
+
+/**
+ * A successful method invocation; one that did not throw a checked exception.
+ */
+public class SuccessMethodInvocationResult implements MethodInvocationResult
+{
+    private final Object returnValue;
+
+    public SuccessMethodInvocationResult(Object returnValue)
+    {
+        this.returnValue = returnValue;
+    }
+
+    public Object getReturnValue()
+    {
+        return returnValue;
+    }
+
+    public void rethrow()
+    {
+    }
+
+    public boolean didThrowCheckedException()
+    {
+        return false;
+    }
+
+    public <T extends Throwable> T getCheckedException(Class<T> exceptionType)
+    {
+        return null;
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SwitchBlockImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SwitchBlockImpl.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SwitchBlockImpl.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/SwitchBlockImpl.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,109 @@
+// 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.plastic.InstructionBuilder;
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.SwitchBlock;
+import org.apache.tapestry5.plastic.SwitchCallback;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+
+public class SwitchBlockImpl extends Lockable implements SwitchBlock, Opcodes
+{
+    private final InstructionBuilderState state;
+
+    private final int min, max;
+
+    private final Label defaultLabel, endSwitchLabel;
+
+    private final Label[] caseLabels;
+
+    private boolean defaultAdded = false;
+
+    SwitchBlockImpl(InstructionBuilderState state, int min, int max)
+    {
+        assert min <= max;
+
+        this.state = state;
+        this.min = min;
+        this.max = max;
+
+        defaultLabel = new Label();
+        endSwitchLabel = new Label();
+
+        caseLabels = new Label[max - min + 1];
+
+        for (int i = min; i <= max; i++)
+        {
+            caseLabels[i - min] = new Label();
+        }
+
+        state.visitor.visitTableSwitchInsn(min, max, defaultLabel, caseLabels);
+    }
+
+    void doCallback(SwitchCallback callback)
+    {
+        check();
+
+        callback.doSwitch(this);
+
+        if (!defaultAdded)
+        {
+            addDefault(new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    builder.throwException(IllegalArgumentException.class,
+                            "Switch value not matched in case statement.");
+                }
+            });
+        }
+
+        state.visitor.visitLabel(endSwitchLabel);
+
+        lock();
+    }
+
+    public void addCase(int caseValue, boolean jumpToEnd, InstructionBuilderCallback callback)
+    {
+        assert caseValue >= min;
+        assert caseValue <= max;
+
+        if (defaultAdded)
+            throw new IllegalStateException("The default block must come last.");
+
+        // TODO: Check that no case value is repeated
+
+        state.visitor.visitLabel(caseLabels[caseValue - min]);
+
+        new InstructionBuilderImpl(state).doCallback(callback);
+
+        if (jumpToEnd)
+            state.visitor.visitJumpInsn(GOTO, endSwitchLabel);
+    }
+
+    public void addDefault(InstructionBuilderCallback callback)
+    {
+        if (defaultAdded)
+            throw new IllegalStateException("A SwitchBlock may only have one default block.");
+
+        state.visitor.visitLabel(defaultLabel);
+
+        new InstructionBuilderImpl(state).doCallback(callback);
+
+        defaultAdded = true;
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TryCatchBlockImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TryCatchBlockImpl.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TryCatchBlockImpl.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TryCatchBlockImpl.java 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.internal.plastic;
+
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.TryCatchBlock;
+import org.apache.tapestry5.plastic.TryCatchCallback;
+import org.objectweb.asm.Label;
+
+public class TryCatchBlockImpl extends Lockable implements TryCatchBlock
+{
+    private final InstructionBuilderState state;
+
+    private final Label startLabel, endLabel;
+
+    TryCatchBlockImpl(InstructionBuilderState state)
+    {
+        this.state = state;
+        this.startLabel = new Label();
+        this.endLabel = new Label();
+    }
+
+    public void addTry(InstructionBuilderCallback callback)
+    {
+        state.visitor.visitLabel(startLabel);
+
+        new InstructionBuilderImpl(state).doCallback(callback);
+
+        state.visitor.visitLabel(endLabel);
+    }
+
+    public void addCatch(String exceptionClassName, InstructionBuilderCallback callback)
+    {
+        assert exceptionClassName != null;
+
+        doCatch(state.nameCache.toInternalName(exceptionClassName), callback);
+    }
+
+    private void doCatch(String exceptionInternalName, InstructionBuilderCallback callback)
+    {
+        check();
+
+        Label handler = state.newLabel();
+
+        new InstructionBuilderImpl(state).doCallback(callback);
+
+        state.visitor.visitTryCatchBlock(startLabel, endLabel, handler, exceptionInternalName);
+    }
+
+    public void addFinally(InstructionBuilderCallback callback)
+    {
+        doCatch(null, callback);
+    }
+
+    void doCallback(TryCatchCallback callback)
+    {
+        assert callback != null;
+
+        callback.doBlock(this);
+
+        lock();
+    }
+
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TypeCategory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TypeCategory.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TypeCategory.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/TypeCategory.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,28 @@
+// 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.plastic.InstructionBuilder;
+
+/**
+ * Defines the two basic kinds of non-primitive, non-array Java types: interfaces and classes.
+ * 
+ * @see InstructionBuilder#invokeVirtual(String, String, String, String...)
+ * @see InstructionBuilder#invokeInterface(String, String, String, String...)
+ */
+public enum TypeCategory
+{
+    INTERFACE, CLASS;
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/AnnotationAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/AnnotationAccess.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/AnnotationAccess.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/AnnotationAccess.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;
+
+import java.lang.annotation.Annotation;
+
+/** Interface implemented by members that may hold annotations. */
+public interface AnnotationAccess
+{
+    /** Checks to see if the target has an annotation of the given type. */
+    <T extends Annotation> boolean hasAnnotation(Class<T> annotationType);
+
+    /** Returns an instantiated annotation, or null if the target does not have the indicated annotation. */
+    <T extends Annotation> T getAnnotation(Class<T> annotationType);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassInstantiator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassInstantiator.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassInstantiator.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ClassInstantiator.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,42 @@
+// 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 end result of a class transformation is a ClassInstantiator that can be used to
+ * instantiate an instance of the transformed class.
+ */
+public interface ClassInstantiator
+{
+    /**
+     * Creates and returns a new instance of the transformed class.
+     */
+    Object newInstance();
+
+    /**
+     * Returns a <em>new</em> instantiator that adds the indicated value to the
+     * instance's {@link InstanceContext}.
+     * 
+     * @param valueType
+     *            defines the type of value, and acts as a key to retrieve the value
+     * @param instanceContextValue
+     *            the non-null value stored
+     * @throws AssertionError
+     *             if instanceContextValue is null
+     * @throws IllegalStateException
+     *             if a value of the given value type has already been stored
+     */
+    <T> ClassInstantiator with(Class<T> valueType, T instanceContextValue);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ComputedValue.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ComputedValue.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ComputedValue.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/ComputedValue.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;
+
+/**
+ * Provides an indirect, or computed, value. This is used for certain
+ * kinds of injection, where the exact value to be injected must be computed
+ * for each <em>instance</em> being instantiated, rather than for the
+ * class as a whole.
+ * 
+ * @see PlasticField#injectComputed(ComputedValue)
+ */
+public interface ComputedValue<T>
+{
+    /**
+     * Computes or otherwise provides the value, given the instance's context.
+     */
+    T get(InstanceContext context);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldConduit.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldConduit.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldConduit.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,46 @@
+// 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 {@link FieldConduit} is an object that effectively <em>replaces</em> the field in the instantiated object.
+ * All reads and writes of the field are replaced with invocations on the conduit. Once a field's access is replaced
+ * with a conduit, the field itself is no longer used. The conduit will even see initializations of the field.
+ * <p>
+ * In Aspect Oriented Programming terms, a FieldConduit allows you to advise read and write access to the field.
+ * <p>
+ * If a field has both a FieldConduit and a {@link FieldHandle}, then the methods of the FieldHandle will be connected
+ * to the methods of the FieldConduit.
+ */
+public interface FieldConduit<T>
+{
+    /**
+     * Invoked when the field is read.
+     * 
+     * @param context
+     *            (see {@link ClassInstantiator#with(Class, Object)})
+     */
+    T get(InstanceContext context);
+
+    /**
+     * Invoked when the field's value is updated.
+     * 
+     * @param context
+     *            (see {@link ClassInstantiator#with(Class, Object)})
+     * @param newValue
+     *            value assigned to the field
+     */
+    void set(InstanceContext context, T newValue);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldHandle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldHandle.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldHandle.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/FieldHandle.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,49 @@
+// 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 read/write access directly to a field (bypassing accessors). Does not use reflection, even
+ * if the field is private (the normal case for Plastic classes).
+ */
+public interface FieldHandle
+{
+    /**
+     * Gets the current value of the field. If the field is a primitive value, then the primitive
+     * will be wrapped.
+     * 
+     * @throws NullPointerException
+     *             if the instance is null
+     * @throws ClassCastException
+     *             if the instance is not the type that contains the field
+     */
+    Object get(Object instance);
+
+    /**
+     * Updates the current value of the field. If the field is a primitive value, then the newValue
+     * will be unwrapped automatically.
+     * 
+     * @throws NullPointerException
+     *             if the instance is null
+     * @throws NullPointerException
+     *             if the newValue is null and the field is a primitive type
+     * @throws ClassCastException
+     *             if the instance is not the type that contains the field
+     * @throws ClassCastException
+     *             if the newValue is not assignable to the field type (or not the matching wrapper type
+     *             for a primitive field)
+     */
+    void set(Object instance, Object newValue);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstanceContext.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstanceContext.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstanceContext.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstanceContext.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,35 @@
+// 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;
+
+/**
+ * Defines per-instance context values for a transformed PlasticClass.
+ */
+public interface InstanceContext
+{
+    /**
+     * Returns the type of the instance created with this context. This is most often of interest
+     * to implementations of {@link ComputedValue}.
+     */
+    Class<?> getInstanceType();
+
+    /**
+     * Gets an instance context value which is identified by type.
+     * 
+     * @throws IllegalArgumentException
+     *             if no such value is available in the context.
+     */
+    <T> T get(Class<T> valueType);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,377 @@
+// 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;
+
+/**
+ * Simplifies the generation of method instructions for a particular method (or constructor), allowing bytecode to be
+ * created with a friendlier API that focuses on Java type names (names as they would appear in Java source) rather than
+ * JVM descriptors or internal names. In some limited cases, types may be specified as Java Class instances as well.
+ * In addition, there is good support for primitive type boxing and unboxing.
+ * <p>
+ * Most methods return the same instance of InstructionBuilder, allowing for a "fluid" API.
+ * <p>
+ * More complex functionality, such as {@linkplain #startTryCatch(InstructionBuilderCallback, TryCatchCallback)
+ * try/catch blocks}, is more DSL (domain specific language) like, and is based on callbacks. This looks better in
+ * Groovy and will be more reasonable once JDK 1.8 closures are available; in the meantime, it means some deeply nested
+ * inner classes, but helps ensure that correct bytecode is generated.
+ */
+@SuppressWarnings("rawtypes")
+public interface InstructionBuilder
+{
+    /** Returns the default value for the method, which may be null, or a specific primitive value. */
+    @Opcodes("ACONST_NULL, LCONST_0, FCONST_0, DCONST_0, ICONST_0, RETURN, ARETURN, IRETURN, FRETURN, LRETURN, DRETURN")
+    InstructionBuilder returnDefaultValue();
+
+    /**
+     * Loads this onto the stack.
+     */
+    @Opcodes("ALOAD")
+    InstructionBuilder loadThis();
+
+    /**
+     * Loads the null constant onto the stack.
+     */
+    @Opcodes("ACONST_NULL")
+    InstructionBuilder loadNull();
+
+    /**
+     * Loads an argument onto the stack, using the opcode appropriate to the argument's type.
+     * 
+     * @param index
+     *            to argument (0 is the first argument, not this)
+     */
+    @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD")
+    InstructionBuilder loadArgument(int index);
+
+    /**
+     * Loads all arguments for the current method onto the stack; this is used when invoking a method
+     * that takes the exact same parameters (often, a super-class implementation). A call to {@link #loadThis()} (or
+     * some other way of identifying the target method) should precede this call.
+     */
+    @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD")
+    InstructionBuilder loadArguments();
+
+    /**
+     * Invokes an instance method of a base class, or a private method of a class, using the target object
+     * and parameters already on the stack. Leaves the result on the stack (unless its a void method).
+     * 
+     * @param containingClassName
+     *            class name containing the method
+     * @param description
+     *            describes the method name, parameters and return type
+     */
+    @Opcodes("INVOKESPECIAL")
+    InstructionBuilder invokeSpecial(String containingClassName, MethodDescription description);
+
+    /**
+     * Invokes a standard virtual method.
+     */
+    @Opcodes("INVOKEVIRTUAL")
+    InstructionBuilder invokeVirtual(String className, String returnType, String methodName, String... argumentTypes);
+
+    /**
+     * Invokes a standard virtual method.
+     */
+    @Opcodes("INVOKEINTERFACE")
+    InstructionBuilder invokeInterface(String interfaceName, String returnType, String methodName,
+            String... argumentTypes);
+
+    /**
+     * Automatically invokes an interface or virtual method. Remember to use {@link #invokeConstructor(Class, Class...)}
+     * for constructors and {@link #invokeSpecial(String, MethodDescription)} for private methods.
+     */
+    @Opcodes("INVOKEVIRTUAL, INVOKEINTERFACE")
+    InstructionBuilder invoke(Class clazz, Class returnType, String methodName, Class... argumentTypes);
+
+    /**
+     * Returns the top value on the stack. For void methods, no value should
+     * be on the stack and the method will simply return.
+     */
+    @Opcodes("ARETURN, IRETURN, LRETURN, FRETURN, DRETURN")
+    InstructionBuilder returnResult();
+
+    /**
+     * If the type name is a primitive type, adds code to box the type into the equivalent wrapper type, using static
+     * methods on the wrapper type. Does nothing if the type is not primitive, or type void.
+     */
+    @Opcodes("INVOKESTATIC")
+    InstructionBuilder boxPrimitive(String typeName);
+
+    /**
+     * Unboxes a wrapper type to a primitive type if typeName is a primitive type name (the value on the stack
+     * should be the corresponding wrapper type instance). Does nothing for non-primitive types.
+     * 
+     * @param typeName
+     *            possibly primitive type name
+     */
+    @Opcodes("INVOKEVIRTUAL")
+    InstructionBuilder unboxPrimitive(String typeName);
+
+    /**
+     * Loads an instance field onto the stack. The object containing the field should already be loaded onto the stack.
+     * 
+     * @param className
+     *            name of class containing the field
+     * @param fieldName
+     *            name of the field
+     * @param typeName
+     *            type of field
+     */
+    @Opcodes("GETFIELD")
+    InstructionBuilder getField(String className, String fieldName, String typeName);
+
+    /**
+     * Loads a field onto the stack. This version is used when the
+     * field type is known at build time, rather than discovered at runtime.
+     * 
+     * @param className
+     *            name of class containing the field
+     * @param fieldName
+     *            name of the field
+     * @param fieldType
+     *            type of field
+     */
+    @Opcodes("GETFIELD")
+    InstructionBuilder getField(String className, String fieldName, Class fieldType);
+
+    /** Expects the stack to contain the instance to update, and the value to store into the field. */
+    @Opcodes("PUTFIELD")
+    InstructionBuilder putField(String className, String fieldName, String typeName);
+
+    @Opcodes("PUTFIELD")
+    InstructionBuilder putField(String className, String fieldName, Class fieldType);
+
+    /**
+     * Loads a value from an array object, which must be the top element of the stack.
+     * 
+     * @param index
+     *            into the array
+     * @param elementType
+     *            the type name of the elements of the array
+     *            <strong>Note: currently only reference types (objects and arrays) are supported, not
+     *            primitives</strong>
+     */
+    @Opcodes("LDC, AALOAD")
+    InstructionBuilder loadArrayElement(int index, String elementType);
+
+    /**
+     * Adds a check that the object on top of the stack is assignable to the indicated class.
+     * 
+     * @param className
+     *            class to cast to
+     */
+    @Opcodes("CHECKCAST")
+    InstructionBuilder checkcast(String className);
+
+    @Opcodes("CHECKCAST")
+    InstructionBuilder checkcast(Class clazz);
+
+    /**
+     * Defines the start of a block that can have exception handlers and finally blocks applied.
+     * Continue using this InstructionBuilder to define code inside the block, then call
+     * methods on the InstructionBlock to define the end of the block and set up handlers.
+     * 
+     * @param tryCallback
+     *            generates the code that is "inside" the <code>try</code>
+     * @param catchCallback
+     *            generates <code>catch</code> and <code>finally</code> blocks
+     */
+    InstructionBuilder startTryCatch(TryCatchCallback catchCallback);
+
+    /**
+     * Creates a new, uninitialized instance of the indicated class. This should be followed
+     * by code to call the new instance's constructor.
+     * 
+     * @param className
+     *            of class to instantiate
+     */
+    @Opcodes("NEW")
+    InstructionBuilder newInstance(String className);
+
+    /**
+     * A convenience version of {@link #newInstance(String)} used when the class is known
+     * at build time.
+     * 
+     * @param clazz
+     *            to instantiate
+     */
+    @Opcodes("NEW")
+    InstructionBuilder newInstance(Class clazz);
+
+    /**
+     * Invokes a constructor on a class. The instance should already be on the stack, followed
+     * by the right number and type of parameters. Note that a constructor acts like a void method,
+     * so you will often follow the sequence: newInstance(), dupe(0), invokeConstructor() so that a reference
+     * to the instance is left on the stack.F
+     * 
+     * @param className
+     *            the class containing the constructor
+     * @param argumentTypes
+     *            java type names for each argument of the constructor
+     * @return
+     */
+    @Opcodes("INVOKESPECIAL")
+    InstructionBuilder invokeConstructor(String className, String... argumentTypes);
+
+    @Opcodes("INVOKESPECIAL")
+    InstructionBuilder invokeConstructor(Class clazz, Class... argumentTypes);
+
+    /**
+     * Duplicates the top` object on the stack, placing the result at some depth.
+     * 
+     * @param depth
+     *            0 (DUP), 1 (DUP_X1) or 2 (DUP_X2)
+     * @return
+     */
+    @Opcodes("DUP, DUP_X1, DUP_X2")
+    InstructionBuilder dupe(int depth);
+
+    /**
+     * Discards the top value on the stack. Assumes the value is a single word value: an object reference, or a small
+     * primitive) and not a double or long.
+     */
+    InstructionBuilder pop();
+
+    /**
+     * Swaps the top element of the stack with the next element down. Note that this can cause problems if the top
+     * element on the stack
+     * is a long or double.
+     * 
+     * @return
+     */
+    @Opcodes("SWAP")
+    InstructionBuilder swap();
+
+    /**
+     * Loads a constant value
+     * 
+     * @param constant
+     *            a non-null Integer, Float, Double, Long, String.
+     */
+    @Opcodes("LDC")
+    InstructionBuilder loadConstant(Object constant);
+
+    /**
+     * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array)
+     * but not a primitive type.
+     * 
+     * @param typeName
+     *            Java class name
+     */
+    @Opcodes("LDC")
+    InstructionBuilder loadTypeConstant(String typeName);
+
+    /**
+     * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array)
+     * but not a primitive type.
+     * 
+     * @param type
+     *            Java type to load as a constant
+     */
+    @Opcodes("LDC")
+    InstructionBuilder loadTypeConstant(Class type);
+
+    /**
+     * Casts the object on top of the stack to the indicated type. For primitive types, casts to the wrapper type
+     * and invokes the appropriate unboxing static method call, leaving a primitive type value on the stack.
+     * 
+     * @param typeName
+     *            to cast or unbox to
+     */
+    @Opcodes("CHECKCAST, INVOKEVIRTUAL")
+    InstructionBuilder castOrUnbox(String typeName);
+
+    /**
+     * Throws an exception with a fixed message. Assumes the exception class includes a constructor that takes a single
+     * string.
+     * 
+     * @param className
+     *            name of exception class to instantiate
+     * @param message
+     *            message (passed as first and only parameter to constructor)
+     */
+    @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW")
+    InstructionBuilder throwException(String className, String message);
+
+    @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW")
+    InstructionBuilder throwException(Class<? extends Throwable> exceptionType, String message);
+
+    /**
+     * Throws the exception on the top of the stack.
+     */
+    @Opcodes("ATHROW")
+    InstructionBuilder throwException();
+
+    /**
+     * Starts a switch statement.
+     * 
+     * @param min
+     *            the minimum value to match against
+     * @param max
+     *            the maximum value to match against
+     */
+    @Opcodes("TABLESWITCH")
+    InstructionBuilder startSwitch(int min, int max, SwitchCallback callback);
+
+    /**
+     * Starts a block where the given name is active.
+     * 
+     * @param name
+     *            name of local variable
+     * @param type
+     *            type of local variable
+     * @param callback
+     *            generates code used when variable is in effect
+     */
+    InstructionBuilder startVariable(String name, String type, InstructionBuilderCallback callback);
+
+    /**
+     * Stores the value on top of the stack to a local variable (previously defined by
+     * {@link #startVariable(String, String, InstructionBuilderCallback)}.
+     */
+    @Opcodes("ASTORE, ISTORE, LSTORE, FSTORE, DSTORE")
+    InstructionBuilder storeVariable(String name);
+
+    /**
+     * Loads a value from a local variable and pushes it onto the stack. The variable must have been previously defined
+     * by {@link #startVariable(String, String, InstructionBuilderCallback)}.
+     */
+    @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD")
+    InstructionBuilder loadVariable(String name);
+
+    /**
+     * Checks if the top value on the stack is zero and invokes the code from one of the two callbacks.
+     * 
+     * @param ifTrue
+     *            callback to generate code for the case where the top value on the stack is zero (may be null)
+     * @param ifFalse
+     *            callback to generate code for the case where the top value on the stack is non-zero
+     *            (may be null)
+     */
+    @Opcodes("IFEQ, GOTO")
+    InstructionBuilder ifZero(InstructionBuilderCallback ifTrue, InstructionBuilderCallback ifFalse);
+
+    /**
+     * Checks if the top value on the stack is null and invokes the code from one of the two callbacks.
+     * 
+     * @param ifTrue
+     *            callback to generate code for the case where the top value on the stack is null (may be null)
+     * @param ifFalse
+     *            callback to generate code for the case where the top value on the stack is not null
+     *            (may be null)
+     */
+    @Opcodes("IFNULL, GOTO")
+    InstructionBuilder ifNull(InstructionBuilderCallback ifTrue, InstructionBuilderCallback ifFalse);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilderCallback.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilderCallback.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilderCallback.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilderCallback.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,22 @@
+// 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 in various places to allow some code to be constructed under controlled circumstances. */
+public interface InstructionBuilderCallback
+{
+    /** Invoked by the builder to allow the callback to build some code. */
+    void doBuild(InstructionBuilder builder);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodAdvice.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodAdvice.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodAdvice.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodAdvice.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;
+
+/**
+ * MethodAdvice is a special callback that is threaded into the implementation of a method.
+ * The advice recieves a {@link MethodInvocation}, which gives the advice the ability to change
+ * parameters or return values or thrown exceptions. In many cases, new behavior is added around the method invocation
+ * with affecting it; common examples include logging, null checks, transaction management, or security checks.
+ */
+public interface MethodAdvice
+{
+    /**
+     * Advice the method, usually invoking {@link MethodInvocation#proceed()} at some point.
+     * 
+     * @param invocation
+     */
+    void advise(MethodInvocation invocation);
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodDescription.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodDescription.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodDescription.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodDescription.java Tue Mar 29 17:24:31 2011
@@ -0,0 +1,228 @@
+// 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.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+
+/**
+ * Describes a {@link PlasticMethod} in terms of a method name, a set of modifiers
+ * (public, private, static, final, etc.), a return type, types of method arguments,
+ * and types of checked exceptions. Types are represented as Java source names:
+ * either names of primitives ("void", "byte", "long") or fully qualified class names ("java.lang.Object",
+ * "java.lang.Runnable"). ASM refers to this as "class name".
+ * <p>
+ * MethodDescriptions are immutable, and properly implement equals() and hashCode(); they are often used as keys in
+ * Maps.
+ * <p>
+ * The natural sort order for a MethodDescription is ascending order by method name, then descending order by number of
+ * parameters (for the same name). Sort order is not currently specified for overrides of the same method with the same
+ * number of parameters.
+ * <p>
+ * TODO: Handling generic types.
+ */
+public class MethodDescription implements Comparable<MethodDescription>
+{
+    /**
+     * The full set of modifier flags for the method.
+     */
+    public final int modifiers;
+
+    /** The Java source name for the return type, e.g., "void", "short", "java.util.Map", "java.lang.String[]". */
+    public final String returnType;
+
+    /** The name of the method. */
+    public final String methodName;
+
+    /**
+     * A non-null array of Java source names for arguments. Do not modify
+     * the contents of this array.
+     */
+    public final String[] argumentTypes;
+
+    /** A non-null array of Java source names for checked exceptions. Do not modify the contents of this array. */
+    public final String[] checkedExceptionTypes;
+
+    /**
+     * Convenience constructor for public methods that have no checked exceptions.
+     * 
+     * @param returnType
+     *            return type as type name
+     * @param methodName
+     *            name of method
+     * @param argumentTypes
+     *            type names for arguments
+     */
+    public MethodDescription(String returnType, String methodName, String... argumentTypes)
+    {
+        this(Modifier.PUBLIC, returnType, methodName, argumentTypes, null);
+    }
+
+    /**
+     * @param modifiers
+     * @param returnType
+     *            Java source name for the return type
+     * @param methodName
+     * @param argumentTypes
+     *            may be null
+     * @param checkedExceptionTypes
+     *            may be null
+     */
+    public MethodDescription(int modifiers, String returnType, String methodName, String[] argumentTypes,
+            String[] checkedExceptionTypes)
+    {
+        assert PlasticInternalUtils.isNonBlank(returnType);
+        assert PlasticInternalUtils.isNonBlank(methodName);
+
+        this.modifiers = modifiers;
+        this.returnType = returnType;
+        this.methodName = methodName;
+
+        this.argumentTypes = PlasticInternalUtils.orEmpty(argumentTypes);
+        this.checkedExceptionTypes = PlasticInternalUtils.orEmpty(checkedExceptionTypes);
+    }
+
+    public MethodDescription withModifiers(int newModifiers)
+    {
+        return new MethodDescription(newModifiers, returnType, methodName, argumentTypes, checkedExceptionTypes);
+    }
+
+    /** Creates a MethodDescription from a Java Method. */
+    public MethodDescription(Method method)
+    {
+        this(method.getModifiers(), toTypeName(method.getReturnType()), method.getName(), toTypeNames(method
+                .getParameterTypes()), toTypeNames(method.getExceptionTypes()));
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static String toTypeName(Class type)
+    {
+        return type.getName();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static String[] toTypeNames(Class[] types)
+    {
+        String[] result = new String[types.length];
+
+        for (int i = 0; i < types.length; i++)
+            result[i] = toTypeName(types[i]);
+
+        return result;
+    }
+
+    public int compareTo(MethodDescription o)
+    {
+        int result = methodName.compareTo(o.methodName);
+
+        if (result == 0)
+            result = o.argumentTypes.length - argumentTypes.length;
+
+        return result;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+
+        result = prime * result + Arrays.hashCode(argumentTypes);
+        result = prime * result + Arrays.hashCode(checkedExceptionTypes);
+        result = prime * result + methodName.hashCode();
+        result = prime * result + modifiers;
+        result = prime * result + returnType.hashCode();
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+
+        MethodDescription other = (MethodDescription) obj;
+
+        if (!methodName.equals(other.methodName))
+            return false;
+
+        // TODO: I think this tripped me up in Tapestry at some point, as
+        // there were modifiers that cause some problem, such as abstract
+        // or deprecated or something. May need a mask of modifiers we
+        // care about for equals()/hashCode() purposes.
+
+        if (modifiers != other.modifiers)
+            return false;
+
+        if (!returnType.equals(other.returnType))
+            return false;
+
+        if (!Arrays.equals(argumentTypes, other.argumentTypes))
+            return false;
+
+        if (!Arrays.equals(checkedExceptionTypes, other.checkedExceptionTypes))
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder();
+
+        // TODO: Not 100% sure that methodNode.access is exactly the same
+        // as modifiers. We'll have to see.
+
+        if (modifiers != 0)
+            builder.append(Modifier.toString(modifiers)).append(" ");
+
+        builder.append(returnType).append(" ").append(methodName).append("(");
+
+        String sep = "";
+
+        for (String name : argumentTypes)
+        {
+            builder.append(sep);
+            builder.append(name);
+
+            sep = ", ";
+        }
+
+        builder.append(")");
+
+        sep = " throws ";
+
+        for (String name : checkedExceptionTypes)
+        {
+            builder.append(sep);
+            builder.append(name);
+
+            sep = ", ";
+        }
+
+        return builder.toString();
+    }
+
+    // TODO: convienience methods: isStatic(), isPrivate()
+}

Added: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodHandle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodHandle.java?rev=1086644&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodHandle.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/MethodHandle.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.lang.reflect.Method;
+
+/**
+ * Similiar to {@link Method}, this allows a method of a Plastic class to be invoked regardless of visibility. Plastic
+ * ensures that reflection is not necessary.
+ */
+public interface MethodHandle
+{
+    /**
+     * Invokes the method for this handle on the instance.
+     * 
+     * @param instance
+     *            the instance containing the method to invoke
+     * @param arguments
+     *            the arguments, if any, to pass to the method. Wrapper types will be unwrapped as necessary
+     *            to perform the invocation.
+     * @return result object encapsulating the actual return value or the checked exception thrown by the method
+     * @throws ClassCastException
+     *             if instance is not the correct type for this method.
+     * @throws RuntimeException
+     *             if the actual method throws a runtime exception
+     */
+    MethodInvocationResult invoke(Object instance, Object... arguments);
+}



Mime
View raw message