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/04/14 00:24:08 UTC

svn commit: r1091960 - in /tapestry/tapestry5/trunk/tapestry-core/src/main: java/org/apache/tapestry5/internal/services/ resources/org/apache/tapestry5/internal/services/

Author: hlship
Date: Wed Apr 13 22:24:07 2011
New Revision: 1091960

URL: http://svn.apache.org/viewvc?rev=1091960&view=rev
Log:
TAP5-853: Rework and simplify PropertyConduitSourceImpl

Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java?rev=1091960&r1=1091959&r2=1091960&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java Wed Apr 13 22:24:07 2011
@@ -137,59 +137,6 @@ public class PropertyConduitSourceImpl i
     private final AnnotationProvider nullAnnotationProvider = new NullAnnotationProvider();
 
     /**
-     * Describes all the gory details of one term (one property or method
-     * invocation) from within the expression.
-     */
-    private interface ExpressionTermInfo extends AnnotationProvider
-    {
-
-        /**
-         * The method to invoke to read the property value, or null.
-         */
-        Method getReadMethod();
-
-        /**
-         * The method to invoke to write the property value, or null. Always
-         * null for method terms (which are inherently
-         * read-only).
-         */
-        Method getWriteMethod();
-
-        /**
-         * The return type of the method, or the type of the property.
-         */
-        Type getType();
-
-        /**
-         * True if an explicit cast to the return type is needed (typically
-         * because of generics).
-         */
-        boolean isCastRequired();
-
-        /**
-         * Returns a user-presentable name identifying the property or method
-         * name.
-         */
-        String getDescription();
-
-        /**
-         * Returns the name of the property, if exists. This is also the name of the public field.
-         */
-        String getPropertyName();
-
-        /**
-         * Returns true if the term is actually a public field.
-         */
-        boolean isField();
-
-        /**
-         * Returns the Field if the term is a public field.
-         */
-        Field getField();
-
-    }
-
-    /**
      * How are null values in intermdiate terms to be handled?
      */
     private enum NullHandling
@@ -203,33 +150,55 @@ public class PropertyConduitSourceImpl i
          * Add code to check for null and short-circuit (i.e., the "?."
          * safe-dereference operator)
          */
-        ALLOW,
-
-        /**
-         * Add no null check at all.
-         */
-        IGNORE
+        ALLOW
     }
 
     /**
      * One term in an expression. Expressions start with some root type and each term advances
      * to a new type.
      */
-    private class PlasticTerm
+    private class Term
     {
         /**
          * The generic type of the term.
          */
         final Type type;
 
+        final Class genericType;
+
+        /** Describes the term, for use in error messages. */
+        final String description;
+
+        final AnnotationProvider annotationProvider;
+
         /** Callback that will implement the term. */
         final InstructionBuilderCallback callback;
 
-        PlasticTerm(Type type, InstructionBuilderCallback callback)
+        Term(Type type, Class genericType, String description, AnnotationProvider annotationProvider,
+                InstructionBuilderCallback callback)
         {
             this.type = type;
+            this.genericType = genericType;
+            this.description = description;
+            this.annotationProvider = annotationProvider;
             this.callback = callback;
         }
+
+        Term(Type type, String description, AnnotationProvider annotationProvider, InstructionBuilderCallback callback)
+        {
+            this(type, GenericsUtils.asClass(type), description, annotationProvider, callback);
+        }
+
+        Term(Type type, String description, InstructionBuilderCallback callback)
+        {
+            this(type, description, null, callback);
+        }
+
+        /** Returns a clone of this Term with a new callback. */
+        Term withCallback(InstructionBuilderCallback newCallback)
+        {
+            return new Term(type, genericType, description, annotationProvider, newCallback);
+        }
     }
 
     private final PropertyAccess access;
@@ -379,7 +348,7 @@ public class PropertyConduitSourceImpl i
 
             while (!isLeaf(node))
             {
-                PlasticTerm term = analyzeDerefNode(activeType, node);
+                Term term = analyzeDerefNode(activeType, node);
 
                 callbacks.add(term.callback);
 
@@ -413,14 +382,19 @@ public class PropertyConduitSourceImpl i
                 }
             });
 
-            implementAccessors(activeClass, node);
+            implementAccessors(activeType, node);
         }
 
-        private void implementAccessors(Class activeType, Tree node)
+        private void implementAccessors(Type activeType, Tree node)
         {
             switch (node.getType())
             {
                 case IDENTIFIER:
+
+                    implementPropertyAccessors(activeType, node);
+
+                    return;
+
                 case INVOKE:
 
                     // So, at this point, we have the navigation method written
@@ -429,14 +403,7 @@ public class PropertyConduitSourceImpl i
                     // ready to use the navigation
                     // method to implement get() and set().
 
-                    ExpressionTermInfo info = infoForMember(activeType, node);
-
-                    implementSetter(activeType, info);
-                    implementGetter(activeType, info, node);
-
-                    conduitPropertyType = GenericsUtils.asClass(info.getType());
-                    conduitPropertyName = info.getPropertyName();
-                    annotationProvider = info;
+                    implementMethodAccessors(activeType, node);
 
                     return;
 
@@ -475,6 +442,160 @@ public class PropertyConduitSourceImpl i
             }
         }
 
+        public void implementMethodAccessors(final Type activeType, final Tree invokeNode)
+        {
+            final Term term = buildInvokeTerm(activeType, invokeNode);
+
+            implementNoOpSetter();
+
+            conduitPropertyName = term.description;
+            conduitPropertyType = term.genericType;
+            annotationProvider = term.annotationProvider;
+
+            plasticClass.introduceMethod(GET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeNavigateMethod(builder);
+
+                    term.callback.doBuild(builder);
+
+                    boxIfPrimitive(builder, conduitPropertyType);
+
+                    builder.returnResult();
+                }
+            });
+
+            implementNoOpSetter();
+        }
+
+        public void implementPropertyAccessors(Type activeType, Tree identifierNode)
+        {
+            String propertyName = identifierNode.getText();
+
+            PropertyAdapter adapter = findPropertyAdapter(activeType, propertyName);
+
+            implementGetter(activeType, adapter);
+            implementSetter(activeType, adapter);
+
+            conduitPropertyName = propertyName;
+            annotationProvider = adapter;
+        }
+
+        private void implementSetter(Type activeType, PropertyAdapter adapter)
+        {
+            if (adapter.getWriteMethod() != null)
+            {
+                implementSetter(activeType, adapter.getWriteMethod());
+                return;
+            }
+
+            if (adapter.getField() != null)
+            {
+                implementSetter(activeType, adapter.getField());
+                return;
+            }
+
+            implementNoOpMethod(SET, "Expression %s for class %s is read-only.", expression, rootType.getName());
+        }
+
+        private void implementSetter(Type activeType, final Field field)
+        {
+            plasticClass.introduceMethod(SET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeNavigateMethod(builder);
+
+                    builder.loadArgument(1).castOrUnbox(PlasticUtils.toTypeName(field.getType()));
+
+                    builder.putField(field.getDeclaringClass().getName(), field.getName(), field.getType());
+
+                    builder.returnResult();
+                }
+            });
+        }
+
+        private void implementSetter(Type activeType, final Method writeMethod)
+        {
+            plasticClass.introduceMethod(SET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeNavigateMethod(builder);
+
+                    Class propertyType = writeMethod.getParameterTypes()[0];
+                    String propertyTypeName = PlasticUtils.toTypeName(propertyType);
+
+                    builder.loadArgument(1).castOrUnbox(propertyTypeName);
+
+                    builder.invoke(writeMethod);
+
+                    builder.returnResult();
+                }
+            });
+        }
+
+        private void implementGetter(Type activeType, PropertyAdapter adapter)
+        {
+            if (adapter.getReadMethod() != null)
+            {
+                implementGetter(activeType, adapter.getReadMethod());
+                return;
+            }
+
+            if (adapter.getField() != null)
+            {
+                implementGetter(activeType, adapter.getField());
+                return;
+            }
+
+            implementNoOpMethod(GET, "Expression %d for class %s is write-only.", expression, rootType.getName());
+        }
+
+        private void implementGetter(final Type activeType, final Field field)
+        {
+            plasticClass.introduceMethod(GET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeNavigateMethod(builder);
+
+                    Type actualType = GenericsUtils.extractActualType(activeType, field);
+                    conduitPropertyType = GenericsUtils.asClass(actualType);
+
+                    builder.getField(field.getDeclaringClass().getName(), field.getName(), field.getType());
+
+                    // Cast not necessary here since the return type of get() is Object
+
+                    boxIfPrimitive(builder, field.getType());
+
+                    builder.returnResult();
+                }
+            });
+        }
+
+        private void implementGetter(final Type activeType, final Method readMethod)
+        {
+            plasticClass.introduceMethod(GET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeNavigateMethod(builder);
+
+                    Type actualType = GenericsUtils.extractActualType(activeType, readMethod);
+
+                    invokeMethod(builder, readMethod, null, 0);
+
+                    conduitPropertyType = GenericsUtils.asClass(actualType);
+
+                    boxIfPrimitive(builder, conduitPropertyType);
+
+                    builder.returnResult();
+                }
+            });
+        }
+
         private void implementRangeOpGetter(final Tree rangeNode)
         {
             plasticClass.introduceMethod(GET, new InstructionBuilderCallback()
@@ -505,7 +626,7 @@ public class PropertyConduitSourceImpl i
             {
                 public void doBuild(InstructionBuilder builder)
                 {
-                    Class expressionType = implementNotExpression(builder, node);
+                    Type expressionType = implementNotExpression(builder, node);
 
                     // Yes, we know this will always be the case, for now.
 
@@ -538,8 +659,10 @@ public class PropertyConduitSourceImpl i
          *            defines the expression
          * @return the expression type
          */
-        private Class implementSubexpression(InstructionBuilder builder, Class activeType, Tree node)
+        private Type implementSubexpression(InstructionBuilder builder, Type activeType, Tree node)
         {
+            Term term;
+
             while (node != null)
             {
                 switch (node.getType())
@@ -554,9 +677,11 @@ public class PropertyConduitSourceImpl i
                             activeType = rootType;
                         }
 
-                        ExpressionTermInfo info = infoForMember(activeType, node);
+                        term = buildTerm(activeType, node);
+
+                        term.callback.doBuild(builder);
 
-                        return evaluateTerm(builder, activeType, node, info);
+                        return term.type;
 
                     case INTEGER:
 
@@ -586,7 +711,7 @@ public class PropertyConduitSourceImpl i
                             activeType = rootType;
                         }
 
-                        PlasticTerm term = analyzeDerefNode(activeType, node);
+                        term = analyzeDerefNode(activeType, node);
 
                         term.callback.doBuild(builder);
 
@@ -638,7 +763,7 @@ public class PropertyConduitSourceImpl i
             });
         }
 
-        private Class implementListConstructor(InstructionBuilder builder, Tree listNode)
+        private Type implementListConstructor(InstructionBuilder builder, Tree listNode)
         {
             // First, create an empty instance of ArrayList
 
@@ -651,9 +776,9 @@ public class PropertyConduitSourceImpl i
             {
                 builder.dupe(); // the ArrayList
 
-                Class expressionType = implementSubexpression(builder, null, listNode.getChild(i));
+                Type expressionType = implementSubexpression(builder, null, listNode.getChild(i));
 
-                boxIfPrimitive(builder, expressionType);
+                boxIfPrimitive(builder, GenericsUtils.asClass(expressionType));
 
                 // Add the value to the array, then pop off the returned boolean
                 builder.invoke(ArrayListMethods.ADD).pop();
@@ -662,42 +787,6 @@ public class PropertyConduitSourceImpl i
             return ArrayList.class;
         }
 
-        private void implementSetter(final Class activeType, final ExpressionTermInfo info)
-        {
-            final Method method = info.getWriteMethod();
-
-            if (method == null && !info.isField())
-            {
-                implementNoOpSetter();
-                return;
-            }
-
-            plasticClass.introduceMethod(SET, new InstructionBuilderCallback()
-            {
-                public void doBuild(InstructionBuilder builder)
-                {
-                    invokeNavigateMethod(builder);
-
-                    String typeName = PlasticUtils.toTypeName(GenericsUtils.asClass(info.getType()));
-
-                    builder.loadArgument(1).castOrUnbox(typeName);
-
-                    if (info.isField())
-                    {
-                        builder.putField(PlasticUtils.toTypeName(activeType), info.getField().getName(), typeName);
-                    }
-                    else
-                    {
-                        // Invoke the setter method
-                        // TODO: unbox to primitive?
-                        builder.invoke(method);
-                    }
-
-                    builder.returnResult();
-                }
-            });
-        }
-
         private void implementNoOpSetter()
         {
             implementNoOpMethod(SET, "Expression '%s' for class %s is read-only.", expression, rootType.getName());
@@ -717,67 +806,6 @@ public class PropertyConduitSourceImpl i
         }
 
         /**
-         * Implements the get() method, using the navigate method.
-         * 
-         * @param activeType
-         *            the type containing the property to read (may be the root type for simple
-         *            property expressions)
-         * @param info
-         *            describes the property to read
-         */
-        private void implementGetter(final Class activeType, final ExpressionTermInfo info, final Tree node)
-        {
-            if (info.getReadMethod() == null && !info.isField())
-            {
-                implementNoOpMethod(GET, "Expression %s for class %s is write-only.", expression, rootType.getName());
-                return;
-            }
-
-            plasticClass.introduceMethod(GET, new InstructionBuilderCallback()
-            {
-                public void doBuild(InstructionBuilder builder)
-                {
-                    invokeNavigateMethod(builder);
-
-                    Class termType = evaluateTerm(builder, activeType, node, info);
-
-                    boxIfPrimitive(builder, termType);
-
-                    builder.returnResult();
-                }
-            });
-        }
-
-        /**
-         * Extends the builder with the code to evaluate a term (which may
-         * 
-         * @param builder
-         * @param activeType
-         *            current type
-         * @param termNode
-         *            the parse Tree node for the term (IDENTIFIER or INVOKE)
-         * @param info
-         *            about the expression term
-         * @return the new active type
-         */
-        public Class evaluateTerm(InstructionBuilder builder, Class activeType, Tree termNode, ExpressionTermInfo info)
-        {
-            Class termType = GenericsUtils.asClass(info.getType());
-            String termTypeName = PlasticUtils.toTypeName(termType);
-
-            if (info.isField())
-            {
-                builder.getField(PlasticUtils.toTypeName(activeType), info.getPropertyName(), termTypeName);
-            }
-            else
-            {
-                invokeMethod(builder, info.getReadMethod(), termNode, 1);
-            }
-
-            return termType;
-        }
-
-        /**
          * Invokes a method that may take parameters. The children of the invokeNode are subexpressions
          * to be evaluated, and potentially coerced, so that they may be passed to the method.
          * 
@@ -804,19 +832,16 @@ public class PropertyConduitSourceImpl i
 
             for (int i = 0; i < parameterTypes.length; i++)
             {
-                Class expressionType = implementSubexpression(builder, null, node.getChild(i + childOffset));
+                Type expressionType = implementSubexpression(builder, null, node.getChild(i + childOffset));
 
                 // The value left on the stack is not primitive, and expressionType represents
                 // its real type.
 
                 Class parameterType = parameterTypes[i];
 
-                if (!parameterType.isAssignableFrom(expressionType))
+                if (!parameterType.isAssignableFrom(GenericsUtils.asClass(expressionType)))
                 {
-                    if (expressionType.isPrimitive())
-                    {
-                        builder.boxPrimitive(expressionType.getName());
-                    }
+                    boxIfPrimitive(builder, expressionType);
 
                     builder.loadThis().getField(delegateField);
                     builder.swap().loadTypeConstant(PlasticUtils.toWrapperType(parameterType));
@@ -849,7 +874,7 @@ public class PropertyConduitSourceImpl i
          * @return a term indicating the type of the expression to this point, and a {@link InstructionBuilderCallback}
          *         to advance the evaluation of the expression form the previous value to the current
          */
-        private PlasticTerm analyzeDerefNode(Type activeType, Tree node)
+        private Term analyzeDerefNode(Type activeType, Tree node)
         {
             // The first child is the term.
 
@@ -860,43 +885,20 @@ public class PropertyConduitSourceImpl i
             return buildTerm(activeType, term, allowNull ? NullHandling.ALLOW : NullHandling.FORBID);
         }
 
-        private PlasticTerm buildTerm(Type activeType, final Tree term, final NullHandling nullHandling)
+        private Term buildTerm(Type activeType, Tree term, final NullHandling nullHandling)
         {
             assertNodeType(term, IDENTIFIER, INVOKE);
 
-            // Get info about this property or method.
-
-            final ExpressionTermInfo info = infoForMember(activeType, term);
-
-            final Method method = info.getReadMethod();
-            final Class activeClass = GenericsUtils.asClass(activeType);
+            final Term simpleTerm = buildTerm(activeType, term);
 
-            if (method == null && !info.isField())
-                throw new RuntimeException(String.format(
-                        "Property '%s' of class %s is not readable (it has no read accessor method).",
-                        info.getDescription(), activeClass.getName()));
+            if (simpleTerm.genericType.isPrimitive())
+                return simpleTerm;
 
-            Type termType = info.getType();
-
-            final Class termClass = GenericsUtils.asClass(termType);
-
-            InstructionBuilderCallback callback = new InstructionBuilderCallback()
+            return simpleTerm.withCallback(new InstructionBuilderCallback()
             {
                 public void doBuild(InstructionBuilder builder)
                 {
-                    if (info.isField())
-                    {
-                        String typeName = PlasticUtils.toTypeName(termClass);
-
-                        builder.getField(info.getField().getDeclaringClass().getName(), info.getField().getName(),
-                                PlasticUtils.toTypeName(info.getField().getType()));
-
-                        builder.unboxPrimitive(typeName);
-                    }
-                    else
-                    {
-                        invokeMethod(builder, method, term, 1);
-                    }
+                    simpleTerm.callback.doBuild(builder);
 
                     builder.dupe().when(Condition.NULL, new InstructionBuilderCallback()
                     {
@@ -914,7 +916,7 @@ public class PropertyConduitSourceImpl i
 
                                 case FORBID:
 
-                                    builder.loadConstant(info.getDescription());
+                                    builder.loadConstant(simpleTerm.description);
                                     builder.loadConstant(expression);
                                     builder.loadArgument(0);
 
@@ -927,15 +929,8 @@ public class PropertyConduitSourceImpl i
                             }
                         }
                     });
-
-                    if (info.isCastRequired())
-                    {
-                        builder.checkcast(termClass);
-                    }
                 }
-            };
-
-            return new PlasticTerm(termType, callback);
+            });
         }
 
         private void assertNodeType(Tree node, int... expected)
@@ -965,21 +960,48 @@ public class PropertyConduitSourceImpl i
             return new RuntimeException(message);
         }
 
-        private ExpressionTermInfo infoForMember(Type activeType, Tree node)
+        private Term buildTerm(Type activeType, Tree termNode)
         {
-            if (node.getType() == INVOKE)
-                return infoForInvokeNode(activeType, node);
+            switch (termNode.getType())
+            {
+                case INVOKE:
 
-            return infoForPropertyOrPublicField(activeType, node);
+                    return buildInvokeTerm(activeType, termNode);
+
+                case IDENTIFIER:
+
+                    return buildPropertyAccessTerm(activeType, termNode);
+
+                default:
+                    throw unexpectedNodeType(termNode, INVOKE, IDENTIFIER);
+            }
         }
 
-        private ExpressionTermInfo infoForPropertyOrPublicField(Type activeType, Tree node)
+        private Term buildPropertyAccessTerm(Type activeType, Tree termNode)
         {
-            String propertyName = node.getText();
+            String propertyName = termNode.getText();
+
+            PropertyAdapter adapter = findPropertyAdapter(activeType, propertyName);
+
+            // Prefer the accessor over the field
+
+            if (adapter.getReadMethod() != null) { return buildGetterMethodAccessTerm(activeType, propertyName,
+                    adapter.getReadMethod()); }
+
+            if (adapter.getField() != null) { return buildPublicFieldAccessTerm(activeType, propertyName,
+                    adapter.getField()); }
+
+            throw new RuntimeException(String.format(
+                    "Property '%s' of class %s is not readable (it has no read accessor method).", adapter.getName(),
+                    adapter.getBeanType().getName()));
+        }
+
+        public PropertyAdapter findPropertyAdapter(Type activeType, String propertyName)
+        {
+            Class activeClass = GenericsUtils.asClass(activeType);
 
-            final Class activeClass = GenericsUtils.asClass(activeType);
             ClassPropertyAdapter classAdapter = access.getAdapter(activeClass);
-            final PropertyAdapter adapter = classAdapter.getPropertyAdapter(propertyName);
+            PropertyAdapter adapter = classAdapter.getPropertyAdapter(propertyName);
 
             if (adapter == null)
             {
@@ -989,147 +1011,100 @@ public class PropertyConduitSourceImpl i
                         "Class %s does not contain a property (or public field) named '%s'.", className, propertyName),
                         new AvailableValues("Properties (and public fields)", names));
             }
+            return adapter;
+        }
 
-            final Type type;
-            final boolean isCastRequired;
-            if (adapter.getField() != null)
-            {
-                type = GenericsUtils.extractActualType(activeType, adapter.getField());
-                isCastRequired = !type.equals(adapter.getField().getType());
-            }
-            else if (adapter.getReadMethod() != null)
-            {
-                type = GenericsUtils.extractActualType(activeType, adapter.getReadMethod());
-                isCastRequired = !type.equals(adapter.getReadMethod().getReturnType());
-            }
-            else
-            {
-                type = adapter.getType();
-                isCastRequired = adapter.isCastRequired();
-            }
+        private Term buildGetterMethodAccessTerm(final Type activeType, String propertyName, final Method readMethod)
+        {
+            Type returnType = GenericsUtils.extractActualType(activeType, readMethod);
 
-            return new ExpressionTermInfo()
+            return new Term(returnType, propertyName, new InstructionBuilderCallback()
             {
-                public Method getReadMethod()
-                {
-                    return adapter.getReadMethod();
-                }
-
-                public Method getWriteMethod()
+                public void doBuild(InstructionBuilder builder)
                 {
-                    return adapter.getWriteMethod();
-                }
+                    invokeMethod(builder, readMethod, null, 0);
 
-                public Type getType()
-                {
-                    return type;
-                }
+                    Type genericType = GenericsUtils.extractActualType(activeType, readMethod);
 
-                public boolean isCastRequired()
-                {
-                    return isCastRequired;
+                    castToGenericType(builder, readMethod.getReturnType(), genericType);
                 }
+            });
+        }
 
-                public String getDescription()
-                {
-                    return adapter.getName();
-                }
+        private Term buildPublicFieldAccessTerm(Type activeType, String propertyName, final Field field)
+        {
+            final Type fieldType = GenericsUtils.extractActualType(activeType, field);
 
-                public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+            return new Term(fieldType, propertyName, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
                 {
-                    return adapter.getAnnotation(annotationClass);
-                }
+                    Class rawFieldType = field.getType();
 
-                public String getPropertyName()
-                {
-                    return adapter.getName();
-                }
+                    builder.getField(field.getDeclaringClass().getName(), field.getName(),
+                            PlasticUtils.toTypeName(rawFieldType));
 
-                public boolean isField()
-                {
-                    return adapter.isField();
+                    castToGenericType(builder, rawFieldType, fieldType);
                 }
 
-                public Field getField()
-                {
-                    return adapter.getField();
-                }
-            };
+            });
         }
 
-        private ExpressionTermInfo infoForInvokeNode(Type activeType, Tree node)
+        /**
+         * Casts the results of a field read or method invocation based on generic information.
+         * 
+         * @param builder
+         *            used to add instructions
+         * @param rawType
+         *            the simple type (often Object) of the field (or method return type)
+         * @param genericType
+         *            the generic Type, from which parameterizations can be determined
+         */
+        private void castToGenericType(InstructionBuilder builder, Class rawType, final Type genericType)
         {
-            String methodName = node.getChild(0).getText();
-
-            int parameterCount = node.getChildCount() - 1;
-
-            final Class activeClass = GenericsUtils.asClass(activeType);
-            try
+            if (!genericType.equals(rawType))
             {
-                final Method method = findMethod(activeClass, methodName, parameterCount);
+                Class castType = GenericsUtils.asClass(genericType);
+                builder.checkcast(castType);
+            }
+        }
 
-                if (method.getReturnType().equals(void.class))
-                    throw new RuntimeException(String.format("Method %s.%s() returns void.", activeClass.getName(),
-                            methodName));
+        private Term buildInvokeTerm(final Type activeType, final Tree invokeNode)
+        {
+            String methodName = invokeNode.getChild(0).getText();
 
-                final Type genericType = GenericsUtils.extractActualType(activeType, method);
+            int parameterCount = invokeNode.getChildCount() - 1;
 
-                return new ExpressionTermInfo()
-                {
-                    public Method getReadMethod()
-                    {
-                        return method;
-                    }
-
-                    public Method getWriteMethod()
-                    {
-                        return null;
-                    }
-
-                    public Type getType()
-                    {
-                        return genericType;
-                    }
+            Class activeClass = GenericsUtils.asClass(activeType);
 
-                    public boolean isCastRequired()
-                    {
-                        return genericType != method.getReturnType();
-                    }
+            final Method method = findMethod(activeClass, methodName, parameterCount);
 
-                    public String getDescription()
-                    {
-                        return new MethodSignature(method).getUniqueId();
-                    }
+            if (method.getReturnType().equals(void.class))
+                throw new RuntimeException(String.format("Method %s.%s() returns void.", activeClass.getName(),
+                        methodName));
 
-                    public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
-                    {
-                        return method.getAnnotation(annotationClass);
-                    }
+            Type returnType = GenericsUtils.extractActualType(activeType, method);
 
-                    public String getPropertyName()
-                    {
-                        return null;
-                    }
+            return new Term(returnType, new MethodSignature(method).getUniqueId(), new AnnotationProvider()
+            {
+                public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+                {
+                    return method.getAnnotation(annotationClass);
+                }
+            }, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    invokeMethod(builder, method, invokeNode, 1);
 
-                    public boolean isField()
-                    {
-                        return false;
-                    }
+                    Type genericType = GenericsUtils.extractActualType(activeType, method);
 
-                    public Field getField()
-                    {
-                        return null;
-                    }
-                };
-            }
-            catch (NoSuchMethodException ex)
-            {
-                throw new RuntimeException(String.format("No public method '%s()' in class %s.", methodName,
-                        activeClass.getName()));
-            }
+                    castToGenericType(builder, method.getReturnType(), genericType);
+                }
+            });
         }
 
-        private Method findMethod(Class activeType, String methodName, int parameterCount) throws NoSuchMethodException
+        private Method findMethod(Class activeType, String methodName, int parameterCount)
         {
             for (Method method : activeType.getMethods())
             {
@@ -1143,7 +1118,13 @@ public class PropertyConduitSourceImpl i
             if (activeType != Object.class)
                 return findMethod(Object.class, methodName, parameterCount);
 
-            throw new NoSuchMethodException(ServicesMessages.noSuchMethod(activeType, methodName));
+            throw new RuntimeException(String.format("Class %s does not contain a method named '%s()'.", activeType,
+                    methodName));
+        }
+
+        public void boxIfPrimitive(InstructionBuilder builder, Type termType)
+        {
+            boxIfPrimitive(builder, GenericsUtils.asClass(termType));
         }
 
         public void boxIfPrimitive(InstructionBuilder builder, Class termType)
@@ -1154,7 +1135,7 @@ public class PropertyConduitSourceImpl i
 
         public Class implementNotExpression(InstructionBuilder builder, final Tree notNode)
         {
-            Class expressionType = implementSubexpression(builder, null, notNode.getChild(0));
+            Type expressionType = implementSubexpression(builder, null, notNode.getChild(0));
 
             boxIfPrimitive(builder, expressionType);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java?rev=1091960&r1=1091959&r2=1091960&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java Wed Apr 13 22:24:07 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 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.
@@ -285,10 +285,6 @@ public class ServicesMessages
         return MESSAGES.format("parameter-binding-must-not-be-empty", parameterName);
     }
 
-    public static String noSuchMethod(Class clazz, String methodName)
-    {
-        return MESSAGES.format("no-such-method", ClassFabUtils.toJavaClassName(clazz), methodName);
-    }
 
     public static String forbidInstantiateComponentClass(String className)
     {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties?rev=1091960&r1=1091959&r2=1091960&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties Wed Apr 13 22:24:07 2011
@@ -1,4 +1,4 @@
-# Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
+# Copyright 2006, 2007, 2008, 2009, 2010, 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.
@@ -69,7 +69,6 @@ page-pool-exausted=The page pool for pag
   or increasing the soft wait (symbol tapestry.page-pool.soft-wait) to trade away some throughput for more efficient use of page instances.
 no-translator-for-type=No translator is defined for type %s.  Registered types: %s.
 parameter-binding-must-not-be-empty=Parameter '%s' must have a non-empty binding.
-no-such-method=Class %s does not contain a method named '%s()'.
 forbid-instantiate-component-class=Component class %s may not be instantiated directly.  You should use an @InjectPage or @InjectComponent annotation instead.
 event-not-handled=Request event '%s' (on component %s) was not handled; you must provide a matching event handler method in the component or in one of its containers.
 document-missing-html-root=The root element of the rendered document was <%s>, not <html>. A root element of <html> is needed when linking JavaScript and stylesheet resources.