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/09/27 23:17:12 UTC

svn commit: r1176625 - in /tapestry/tapestry5/trunk: plastic/src/main/java/org/apache/tapestry5/internal/plastic/ plastic/src/main/java/org/apache/tapestry5/plastic/ plastic/src/test/groovy/org/apache/tapestry5/plastic/ plastic/src/test/java/testinterf...

Author: hlship
Date: Tue Sep 27 21:17:11 2011
New Revision: 1176625

URL: http://svn.apache.org/viewvc?rev=1176625&view=rev
Log:
TAP5-1673: Tapestry 5.2 supported references to public static fields of classes; this is not supported by 5.3

Added:
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/StaticFieldAccess.groovy
    tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/Access.java
    tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/StaticFieldHolder.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PublicStaticFieldBean.java
Modified:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/Lockable.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassPropertyAdapter.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PropertyAdapter.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImplTest.java

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java Tue Sep 27 21:17:11 2011
@@ -14,36 +14,26 @@
 
 package org.apache.tapestry5.internal.plastic;
 
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
 import org.apache.tapestry5.internal.plastic.InstructionBuilderState.LVInfo;
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
-import org.apache.tapestry5.plastic.Condition;
-import org.apache.tapestry5.plastic.InstructionBuilder;
-import org.apache.tapestry5.plastic.InstructionBuilderCallback;
-import org.apache.tapestry5.plastic.LocalVariable;
-import org.apache.tapestry5.plastic.LocalVariableCallback;
-import org.apache.tapestry5.plastic.MethodDescription;
-import org.apache.tapestry5.plastic.PlasticField;
-import org.apache.tapestry5.plastic.PlasticMethod;
-import org.apache.tapestry5.plastic.PlasticUtils;
-import org.apache.tapestry5.plastic.SwitchCallback;
-import org.apache.tapestry5.plastic.TryCatchCallback;
-import org.apache.tapestry5.plastic.WhenCallback;
-import org.apache.tapestry5.plastic.WhileCallback;
+import org.apache.tapestry5.plastic.*;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
 
 @SuppressWarnings("rawtypes")
 public class InstructionBuilderImpl extends Lockable implements Opcodes, InstructionBuilder
 {
     private static final int[] DUPE_OPCODES = new int[]
-    { DUP, DUP_X1, DUP_X2 };
+            {DUP, DUP_X1, DUP_X2};
 
-    /** Maps from condition to opcode to jump to the false code block. */
+    /**
+     * Maps from condition to opcode to jump to the false code block.
+     */
     private static final Map<Condition, Integer> conditionToOpcode = new HashMap<Condition, Integer>();
 
     static
@@ -126,8 +116,7 @@ public class InstructionBuilderImpl exte
         {
             v.visitInsn(ACONST_NULL);
             v.visitInsn(ARETURN);
-        }
-        else
+        } else
         {
             switch (type)
             {
@@ -222,7 +211,7 @@ public class InstructionBuilderImpl exte
     }
 
     public InstructionBuilder invokeVirtual(String className, String returnType, String methodName,
-            String... argumentTypes)
+                                            String... argumentTypes)
     {
         check();
 
@@ -232,7 +221,7 @@ public class InstructionBuilderImpl exte
     }
 
     public InstructionBuilder invokeInterface(String interfaceName, String returnType, String methodName,
-            String... argumentTypes)
+                                              String... argumentTypes)
     {
         check();
 
@@ -337,6 +326,38 @@ public class InstructionBuilderImpl exte
         return this;
     }
 
+    public InstructionBuilder getStaticField(String className, String fieldName, String typeName)
+    {
+        check();
+
+        v.visitFieldInsn(GETSTATIC, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
+
+        return this;
+    }
+
+    public InstructionBuilder getStaticField(String className, String fieldName, Class fieldType)
+    {
+        check();
+
+        return getStaticField(className, fieldName, cache.toTypeName(fieldType));
+    }
+
+    public InstructionBuilder putStaticField(String className, String fieldName, Class fieldType)
+    {
+        check();
+
+        return putStaticField(className, fieldName, cache.toTypeName(fieldType));
+    }
+
+    public InstructionBuilder putStaticField(String className, String fieldName, String typeName)
+    {
+        check();
+
+        v.visitFieldInsn(PUTSTATIC, cache.toInternalName(className), fieldName, cache.toDesc(typeName));
+
+        return this;
+    }
+
     public InstructionBuilder getField(PlasticField field)
     {
         check();
@@ -378,8 +399,7 @@ public class InstructionBuilderImpl exte
         if (type == null)
         {
             v.visitInsn(AALOAD);
-        }
-        else
+        } else
         {
             throw new RuntimeException("Access to non-object arrays is not yet supported.");
         }

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/Lockable.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/Lockable.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/Lockable.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/Lockable.java Tue Sep 27 21:17:11 2011
@@ -23,12 +23,20 @@ public class Lockable
 {
     private boolean locked;
 
+    /**
+     * Checks to see if the object has been locked.
+     *
+     * @throw IllegalStateException if {@link #lock()} has been invoked.
+     */
     protected void check()
     {
         if (locked)
             throw new IllegalStateException(toString() + " has been locked and can no longer be used.");
     }
 
+    /**
+     * Invokes {@link #check()}, then sets the locked flag. Subsequent calls to {@link #check()} will fail.
+     */
     protected void lock()
     {
         check();

Modified: 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=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java Tue Sep 27 21:17:11 2011
@@ -21,9 +21,9 @@ import java.lang.reflect.Method;
  * 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>
+ * <p/>
  * Most methods return the same instance of InstructionBuilder, allowing for a "fluid" API.
- * <p>
+ * <p/>
  * More complex functionality, such as {@linkplain #startTryCatch(TryCatchCallback)
  * try/catch blocks}, is more like a DSL (domain specific language), 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
@@ -33,7 +33,9 @@ import java.lang.reflect.Method;
 @SuppressWarnings("rawtypes")
 public interface InstructionBuilder
 {
-    /** Returns the default value for the method, which may be null, or a specific primitive value. */
+    /**
+     * 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();
 
@@ -53,9 +55,8 @@ public interface InstructionBuilder
      * Loads an argument onto the stack, using the opcode appropriate to the argument's type. In addition
      * this automatically adjusts for arguments of primitive type long or double (which take up two
      * local variable indexes, rather than one as for all other types)
-     * 
-     * @param index
-     *            to argument (0 is the first argument, not this)
+     *
+     * @param index to argument (0 is the first argument, not this)
      */
     @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD")
     InstructionBuilder loadArgument(int index);
@@ -71,11 +72,9 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @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);
@@ -94,7 +93,7 @@ public interface InstructionBuilder
      */
     @Opcodes("INVOKEINTERFACE")
     InstructionBuilder invokeInterface(String interfaceName, String returnType, String methodName,
-            String... argumentTypes);
+                                       String... argumentTypes);
 
     /**
      * Automatically invokes an interface or virtual method. Remember to use {@link #invokeConstructor(Class, Class...)}
@@ -132,9 +131,8 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @param typeName possibly primitive type name
      */
     @Opcodes("INVOKEVIRTUAL")
     InstructionBuilder unboxPrimitive(String typeName);
@@ -142,23 +140,19 @@ public interface InstructionBuilder
     /**
      * Loads an instance field onto the stack. The object containing the field should already be loaded onto the stack
      * (usually, via {@link #loadThis()}).
-     * 
-     * @param className
-     *            name of class containing the field
-     * @param fieldName
-     *            name of the field
-     * @param typeName
-     *            type of field
+     *
+     * @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 an instance field onto the stack. The plastic class instance containing the field should already be loaded
+     * Loads an instance or static field onto the stack. The plastic class instance containing the field should already be loaded
      * onto the stack (usually, via {@link #loadThis()}).
-     * 
-     * @param field
-     *            identifies name, type and container of field to load
+     *
+     * @param field identifies name, type and container of field to load
      */
     @Opcodes("GETFIELD")
     InstructionBuilder getField(PlasticField field);
@@ -166,18 +160,60 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @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. */
+    /**
+     * Gets a static field; does not consume a value from the stack, but pushes the fields' value onto the stack.
+     *
+     * @param className name of class containing the field
+     * @param fieldName name of the field
+     * @param fieldType type of field
+     */
+    @Opcodes("GETSTATIC")
+    InstructionBuilder getStaticField(String className, String fieldName, Class fieldType);
+
+    /**
+     * Gets a static field; does not consume a value from the stack, but pushes the fields' value onto the stack.
+     *
+     * @param className name of class containing the field
+     * @param fieldName name of the field
+     * @param typeName  type of field
+     */
+    @Opcodes("GETSTATIC")
+    InstructionBuilder getStaticField(String className, String fieldName,
+                                      String typeName);
+
+
+    /**
+     * Sets a static field; the new field value should be on top of the stack.
+     *
+     * @param className name of class containing the field
+     * @param fieldName name of the field
+     * @param fieldType type of field
+     */
+    @Opcodes("PUTSTATIC")
+    InstructionBuilder putStaticField(String className, String fieldName, Class fieldType);
+
+    /**
+     * Sets a static field; the new field value should be on top of the stack.
+     *
+     * @param className name of class containing the field
+     * @param fieldName name of the field
+     * @param typeName  type of field
+     */
+    @Opcodes("PUTSTATIC")
+    InstructionBuilder putStaticField(String className, String fieldName,
+                                      String typeName);
+
+    /**
+     * 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);
 
@@ -186,13 +222,11 @@ public interface InstructionBuilder
 
     /**
      * Loads a value from an array object, which must be the top element of the stack.
-     * 
-     * @param index
-     *            constant 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>
+     *
+     * @param index       constant 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);
@@ -206,9 +240,8 @@ public interface InstructionBuilder
 
     /**
      * Adds a check that the object on top of the stack is assignable to the indicated class.
-     * 
-     * @param className
-     *            class to cast to
+     *
+     * @param className class to cast to
      */
     @Opcodes("CHECKCAST")
     InstructionBuilder checkcast(String className);
@@ -220,18 +253,16 @@ public interface InstructionBuilder
      * 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 tryCatchCallback
-     *            allows generation of try, catch, and finally clauses
+     *
+     * @param tryCatchCallback allows generation of try, catch, and finally clauses
      */
     InstructionBuilder startTryCatch(TryCatchCallback tryCatchCallback);
 
     /**
      * 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
+     *
+     * @param className of class to instantiate
      */
     @Opcodes("NEW")
     InstructionBuilder newInstance(String className);
@@ -239,9 +270,8 @@ public interface InstructionBuilder
     /**
      * A convenience version of {@link #newInstance(String)} used when the class is known
      * at build time.
-     * 
-     * @param clazz
-     *            to instantiate
+     *
+     * @param clazz to instantiate
      */
     @Opcodes("NEW")
     InstructionBuilder newInstance(Class clazz);
@@ -251,11 +281,9 @@ public interface InstructionBuilder
      * 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
+     *
+     * @param className     the class containing the constructor
+     * @param argumentTypes java type names for each argument of the constructor
      */
     @Opcodes("INVOKESPECIAL")
     InstructionBuilder invokeConstructor(String className, String... argumentTypes);
@@ -265,24 +293,27 @@ public interface InstructionBuilder
 
     /**
      * Duplicates the top object on the stack, placing the result at some depth.
-     * 
-     * @param depth
-     *            0 (DUP), 1 (DUP_X1) or 2 (DUP_X2)
+     *
+     * @param depth 0 (DUP), 1 (DUP_X1) or 2 (DUP_X2)
      */
     @Opcodes("DUP, DUP_X1, DUP_X2")
     InstructionBuilder dupe(int depth);
 
-    /** Duplicates a wide value (a primitive long or double). */
+    /**
+     * Duplicates a wide value (a primitive long or double).
+     */
     @Opcodes("DUP2")
     InstructionBuilder dupeWide();
 
-    /** Pops a wide value (a primitive long or double). */
+    /**
+     * Pops a wide value (a primitive long or double).
+     */
     @Opcodes("POP2")
     InstructionBuilder popWide();
 
     /**
      * Duplicates the top object on the stack. Commonly used with {@link #when(Condition, WhenCallback)}.
-     * 
+     *
      * @see #dupe(int)
      */
     @Opcodes("DUP")
@@ -304,9 +335,8 @@ public interface InstructionBuilder
 
     /**
      * Loads a constant value
-     * 
-     * @param constant
-     *            Integer, Float, Double, Long, String or null
+     *
+     * @param constant Integer, Float, Double, Long, String or null
      */
     @Opcodes("LDC, ICONST_*, LCONST_*, FCONST_*, DCONST_*, ACONST_NULL")
     InstructionBuilder loadConstant(Object constant);
@@ -314,9 +344,8 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @param typeName Java class name
      */
     @Opcodes("LDC")
     InstructionBuilder loadTypeConstant(String typeName);
@@ -324,9 +353,8 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @param type Java type to load as a constant
      */
     @Opcodes("LDC")
     InstructionBuilder loadTypeConstant(Class type);
@@ -334,9 +362,8 @@ public interface InstructionBuilder
     /**
      * 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
+     *
+     * @param typeName to cast or unbox to
      */
     @Opcodes("CHECKCAST, INVOKEVIRTUAL")
     InstructionBuilder castOrUnbox(String typeName);
@@ -344,11 +371,9 @@ public interface InstructionBuilder
     /**
      * 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)
+     *
+     * @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);
@@ -364,22 +389,18 @@ public interface InstructionBuilder
 
     /**
      * Starts a switch statement.
-     * 
-     * @param min
-     *            the minimum value to match against
-     * @param max
-     *            the maximum value to match against
+     *
+     * @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 type
-     *            type of local variable
-     * @param callback
-     *            generates code used when variable is in effect
+     *
+     * @param type     type of local variable
+     * @param callback generates code used when variable is in effect
      */
     InstructionBuilder startVariable(String type, LocalVariableCallback callback);
 
@@ -400,11 +421,9 @@ public interface InstructionBuilder
     /**
      * Executes conditional code based on a {@link Condition}. The testing opcodes all pop
      * the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}.
-     * 
-     * @param condition
-     *            defines true and false cases
-     * @param callback
-     *            provides code for true and false blocks
+     *
+     * @param condition defines true and false cases
+     * @param callback  provides code for true and false blocks
      * @return this builder
      */
     @Opcodes("IFEQ, etc., GOTO")
@@ -414,13 +433,11 @@ public interface InstructionBuilder
      * Simplified version of {@link #when(Condition, WhenCallback)} that
      * simply executes the callback code when the condition is true and does nothing
      * if the condition is false (the more general case).
-     * <p>
+     * <p/>
      * The testing opcodes all pop the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}.
-     * 
-     * @param condition
-     *            to evaluate
-     * @param ifTrue
-     *            generates code for when condition is true
+     *
+     * @param condition to evaluate
+     * @param ifTrue    generates code for when condition is true
      * @return this builder
      */
     @Opcodes("IFEQ, etc., GOTO")
@@ -431,7 +448,7 @@ public interface InstructionBuilder
      * code is executed, then the condition is evaluated (which will consume at least the top value on the stack).
      * When the condition is false, the loop is exited. When the condition is true, the code defined by
      * {@link WhileCallback#buildBody(InstructionBuilder)} is executed, and then a GOTO back to the test code.
-     * 
+     *
      * @param condition
      * @param callback
      * @return this builder
@@ -443,10 +460,9 @@ public interface InstructionBuilder
      * Expects an array to be the top value on the stack. Iterates the array.
      * The callback generates code that will have each successive value from the array
      * as the top value on the stack. Creates a variable to store the loop index.
-     * 
-     * @param callback
-     *            to invoke. The element will be the top value on the stack. The callback is responsible
-     *            for removing it from the stack.
+     *
+     * @param callback to invoke. The element will be the top value on the stack. The callback is responsible
+     *                 for removing it from the stack.
      * @return this builder
      */
     @Opcodes("IINC, ARRAYLENGTH, IFEQ, etc., GOTO")
@@ -454,13 +470,15 @@ public interface InstructionBuilder
 
     /**
      * Increments a local integer variable.
-     * 
+     *
      * @return this builder
      */
     @Opcodes("IINC")
     InstructionBuilder increment(LocalVariable variable);
 
-    /** Expects the top object on the stack to be an array. Replaces it with the length of that array. */
+    /**
+     * Expects the top object on the stack to be an array. Replaces it with the length of that array.
+     */
     @Opcodes("ARRAYLENGTH")
     InstructionBuilder arrayLength();
 
@@ -468,7 +486,7 @@ public interface InstructionBuilder
      * Special comparison logic for primitive float, double and long. Expect two matching wide values
      * on the stack. Reduces the two wide values to a single int value: -1, 0, or 1 depending on whether the deeper
      * value is less than, equal to, or greater than the top value on the stack.
-     * 
+     *
      * @param typeName
      */
     @Opcodes("LCMP, FCMPL, DCMPL")

Modified: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy (original)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/FieldAnnotationAccess.groovy Tue Sep 27 21:17:11 2011
@@ -1,15 +1,14 @@
 package org.apache.tapestry5.plastic
 
-import org.apache.tapestry5.plastic.PlasticManager;
+import testannotations.Maybe
 
-import spock.lang.Specification
-import testannotations.Maybe;
+class FieldAnnotationAccess extends AbstractPlasticSpecification
+{
 
-class FieldAnnotationAccess extends AbstractPlasticSpecification {
-
-    def "locate field by annotation"() {
+    def "read static field"()
+    {
         setup:
-        def pc  = mgr.getPlasticClass("testsubjects.AnnotationSubject")
+        def pc = mgr.getPlasticClass("testsubjects.AnnotationSubject")
 
         when:
         def fields = pc.getFieldsWithAnnotation(Maybe.class)

Added: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/StaticFieldAccess.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/StaticFieldAccess.groovy?rev=1176625&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/StaticFieldAccess.groovy (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/StaticFieldAccess.groovy Tue Sep 27 21:17:11 2011
@@ -0,0 +1,38 @@
+package org.apache.tapestry5.plastic
+
+import testinterfaces.Access
+
+class StaticFieldAccess extends AbstractPlasticSpecification
+{
+    def "static fields are readable and writable"()
+    {
+        setup:
+        def pc = mgr.getPlasticClass("testsubjects.StaticFieldHolder")
+
+        pc.introduceInterface(Access.class)
+
+        pc.introduceMethod(new MethodDescription("java.lang.Object", "read")).changeImplementation({
+            InstructionBuilder b ->
+            b.getStaticField("testsubjects.StaticFieldHolder", "VALUE", String.class).returnResult()
+        } as InstructionBuilderCallback)
+
+        pc.introduceMethod(new MethodDescription("void", "write", "java.lang.Object")).changeImplementation({
+            InstructionBuilder b ->
+            b.loadArgument(0).checkcast(String.class)
+            b.putStaticField("testsubjects.StaticFieldHolder", "VALUE", String.class).returnResult()
+        } as InstructionBuilderCallback)
+
+        Access o = pc.createInstantiator().newInstance()
+
+        expect:
+
+        o.read().is(o.VALUE)
+
+        when:
+        o.write "newvalue"
+
+        then:
+
+        o.VALUE == "newvalue"
+    }
+}

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/Access.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/Access.java?rev=1176625&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/Access.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testinterfaces/Access.java Tue Sep 27 21:17:11 2011
@@ -0,0 +1,8 @@
+package testinterfaces;
+
+public interface Access
+{
+    Object read();
+
+    void write(Object newValue);
+}

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/StaticFieldHolder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/StaticFieldHolder.java?rev=1176625&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/StaticFieldHolder.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/StaticFieldHolder.java Tue Sep 27 21:17:11 2011
@@ -0,0 +1,6 @@
+package testsubjects;
+
+public class StaticFieldHolder
+{
+    public static String VALUE = "initial";
+}

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=1176625&r1=1176624&r2=1176625&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 Tue Sep 27 21:17:11 2011
@@ -40,9 +40,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
+import java.lang.reflect.*;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -502,13 +500,13 @@ public class PropertyConduitSourceImpl i
         {
             if (adapter.getWriteMethod() != null)
             {
-                implementSetter(activeType, adapter.getWriteMethod());
+                implementSetter(adapter.getWriteMethod());
                 return;
             }
 
-            if (adapter.getField() != null)
+            if (adapter.getField() != null && adapter.isUpdate())
             {
-                implementSetter(activeType, adapter.getField());
+                implementSetter(adapter.getField());
                 return;
             }
 
@@ -516,8 +514,30 @@ public class PropertyConduitSourceImpl i
                     rootType.getName());
         }
 
-        private void implementSetter(Type activeType, final Field field)
+        private boolean isStatic(Member member)
+        {
+            return Modifier.isStatic(member.getModifiers());
+        }
+
+        private void implementSetter(final Field field)
         {
+            if (isStatic(field))
+            {
+                plasticClass.introduceMethod(ConduitMethods.SET, new InstructionBuilderCallback()
+                {
+                    public void doBuild(InstructionBuilder builder)
+                    {
+                        builder.loadArgument(1).castOrUnbox(PlasticUtils.toTypeName(field.getType()));
+
+                        builder.putStaticField(field.getDeclaringClass().getName(), field.getName(), field.getType());
+
+                        builder.returnResult();
+                    }
+                });
+
+                return;
+            }
+
             plasticClass.introduceMethod(ConduitMethods.SET, new InstructionBuilderCallback()
             {
                 public void doBuild(InstructionBuilder builder)
@@ -533,7 +553,7 @@ public class PropertyConduitSourceImpl i
             });
         }
 
-        private void implementSetter(Type activeType, final Method writeMethod)
+        private void implementSetter(final Method writeMethod)
         {
             plasticClass.introduceMethod(ConduitMethods.SET, new InstructionBuilderCallback()
             {
@@ -557,13 +577,13 @@ public class PropertyConduitSourceImpl i
         {
             if (adapter.getReadMethod() != null)
             {
-                implementGetter(activeType, adapter.getReadMethod());
+                implementGetter(adapter.getReadMethod());
                 return;
             }
 
             if (adapter.getField() != null)
             {
-                implementGetter(activeType, adapter.getField());
+                implementGetter(adapter.getField());
                 return;
             }
 
@@ -571,8 +591,27 @@ public class PropertyConduitSourceImpl i
                     rootType.getName());
         }
 
-        private void implementGetter(final Type activeType, final Field field)
+        private void implementGetter(final Field field)
         {
+            if (isStatic(field))
+            {
+                plasticClass.introduceMethod(ConduitMethods.GET, new InstructionBuilderCallback()
+                {
+                    public void doBuild(InstructionBuilder builder)
+                    {
+                        builder.getStaticField(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();
+                    }
+                });
+
+                return;
+            }
+
             plasticClass.introduceMethod(ConduitMethods.GET, new InstructionBuilderCallback()
             {
                 public void doBuild(InstructionBuilder builder)
@@ -590,7 +629,7 @@ public class PropertyConduitSourceImpl i
             });
         }
 
-        private void implementGetter(final Type activeType, final Method readMethod)
+        private void implementGetter(final Method readMethod)
         {
             plasticClass.introduceMethod(ConduitMethods.GET, new InstructionBuilderCallback()
             {
@@ -598,8 +637,6 @@ public class PropertyConduitSourceImpl i
                 {
                     invokeNavigateMethod(builder);
 
-                    Type actualType = GenericsUtils.extractActualType(activeType, readMethod);
-
                     invokeMethod(builder, readMethod, null, 0);
 
                     boxIfPrimitive(builder, conduitPropertyType);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java Tue Sep 27 21:17:11 2011
@@ -839,4 +839,35 @@ public class PropertyConduitSourceImplTe
         assertSame(list.get(0), bean);
         assertNull(list.get(1));
     }
+
+    /**
+     * TAP5-1673
+     */
+    @Test
+    public void public_static_fields_are_accessible()
+    {
+        PropertyConduit pc = source.create(PublicStaticFieldBean.class, "value");
+
+        assertSame(pc.get(null), PublicStaticFieldBean.VALUE);
+
+        pc.set(null, "new-value");
+
+        assertEquals(PublicStaticFieldBean.VALUE, "new-value");
+    }
+
+    @Test
+    public void final_static_fields_are_read_only()
+    {
+        PropertyConduit pc = source.create(PublicStaticFieldBean.class, "read_only");
+
+        try
+        {
+            pc.set(null, "new-value");
+            unreachable();
+        } catch (RuntimeException ex)
+        {
+            assertEquals(ex.getMessage(),
+                    "Expression 'read_only' for class org.apache.tapestry5.internal.services.PublicStaticFieldBean is read-only.");
+        }
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PublicStaticFieldBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PublicStaticFieldBean.java?rev=1176625&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PublicStaticFieldBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PublicStaticFieldBean.java Tue Sep 27 21:17:11 2011
@@ -0,0 +1,8 @@
+package org.apache.tapestry5.internal.services;
+
+public class PublicStaticFieldBean
+{
+    public static String VALUE = "initial value";
+
+    public static final String READ_ONLY = "read-only";
+}

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java Tue Sep 27 21:17:11 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 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.
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
@@ -23,10 +22,11 @@ import org.apache.tapestry5.ioc.services
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+
 public class ClassPropertyAdapterImpl implements ClassPropertyAdapter
 {
     private final Map<String, PropertyAdapter> adapters = newCaseInsensitiveMap();
@@ -56,13 +56,10 @@ public class ClassPropertyAdapterImpl im
             adapters.put(pa.getName(), pa);
         }
 
-        // Now, add any public fields that do not conflict
+        // Now, add any public fields (even if static) that do not conflict
 
         for (Field f : beanType.getFields())
         {
-            if(Modifier.isStatic(f.getModifiers()))
-                continue;
-            
             String name = f.getName();
 
             if (!adapters.containsKey(name))

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java Tue Sep 27 21:17:11 2011
@@ -20,9 +20,7 @@ import org.apache.tapestry5.ioc.services
 import org.apache.tapestry5.ioc.services.PropertyAdapter;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.lang.reflect.*;
 import java.util.List;
 
 public class PropertyAdapterImpl implements PropertyAdapter
@@ -39,6 +37,7 @@ public class PropertyAdapterImpl impleme
 
     private final boolean castRequired;
 
+    // Synchronized by this; lazily initialized
     private AnnotationProvider annotationProvider;
 
     private final Field field;
@@ -46,7 +45,7 @@ public class PropertyAdapterImpl impleme
     private final Class declaringClass;
 
     PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod,
-            Method writeMethod)
+                        Method writeMethod)
     {
         this.classAdapter = classAdapter;
         this.name = name;
@@ -105,7 +104,12 @@ public class PropertyAdapterImpl impleme
 
     public boolean isUpdate()
     {
-        return field != null || writeMethod != null;
+        return writeMethod != null || (field != null && !isFinal(field));
+    }
+
+    private boolean isFinal(Member member)
+    {
+        return Modifier.isFinal(member.getModifiers());
     }
 
     public Object get(Object instance)
@@ -121,12 +125,10 @@ public class PropertyAdapterImpl impleme
                 return readMethod.invoke(instance);
             else
                 return field.get(instance);
-        }
-        catch (InvocationTargetException ex)
+        } catch (InvocationTargetException ex)
         {
             fail = ex.getTargetException();
-        }
-        catch (Exception ex)
+        } catch (Exception ex)
         {
             fail = ex;
         }
@@ -149,12 +151,10 @@ public class PropertyAdapterImpl impleme
                 field.set(instance, value);
 
             return;
-        }
-        catch (InvocationTargetException ex)
+        } catch (InvocationTargetException ex)
         {
             fail = ex.getTargetException();
-        }
-        catch (Exception ex)
+        } catch (Exception ex)
         {
             fail = ex;
         }
@@ -189,7 +189,8 @@ public class PropertyAdapterImpl impleme
 
             Class cursor = getBeanType();
 
-            out: while (cursor != null)
+            out:
+            while (cursor != null)
             {
                 for (Field f : cursor.getDeclaredFields())
                 {

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassPropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassPropertyAdapter.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassPropertyAdapter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassPropertyAdapter.java Tue Sep 27 21:17:11 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 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.
@@ -26,7 +26,8 @@ import java.util.List;
 public interface ClassPropertyAdapter
 {
     /**
-     * Returns the names of all properties, sorted into alphabetic order.
+     * Returns the names of all properties, sorted into alphabetic order. This includes true properties
+     * (as defined in the JavaBeans specification), but also public fields. Starting in Tapestry 5.3, even public static fields are included.
      */
     List<String> getPropertyNames();
 

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PropertyAdapter.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PropertyAdapter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PropertyAdapter.java Tue Sep 27 21:17:11 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2010 The Apache Software Foundation
+// Copyright 2006, 2008, 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.
@@ -24,8 +24,8 @@ import java.lang.reflect.Method;
  * when searching for annotations, the read method (if present) is checked first, followed by the write method, followed
  * by the underlying field (when the property name matches the field name).
  * <p/>
- * Starting in release 5.2, this property may actually be a public field.
- * 
+ * Starting in release 5.2, this property may actually be a public field. In 5.3, it may be a public static field.
+ *
  * @see org.apache.tapestry5.ioc.services.ClassPropertyAdapter
  */
 @SuppressWarnings("unchecked")
@@ -47,7 +47,7 @@ public interface PropertyAdapter extends
     public Method getReadMethod();
 
     /**
-     * Returns true if the property is writeable (i.e., has a setter method or is a public field).
+     * Returns true if the property is writeable (i.e., has a setter method or is a non-final field).
      */
     boolean isUpdate();
 
@@ -58,24 +58,19 @@ public interface PropertyAdapter extends
 
     /**
      * Reads the property value.
-     * 
-     * @param instance
-     *            to read from
-     * @throws UnsupportedOperationException
-     *             if the property is write only
+     *
+     * @param instance to read from
+     * @throws UnsupportedOperationException if the property is write only
      */
     Object get(Object instance);
 
     /**
      * Updates the property value. The provided value must not be null if the property type is primitive, and must
      * otherwise be of the proper type.
-     * 
-     * @param instance
-     *            to update
-     * @param value
-     *            new value for the property
-     * @throws UnsupportedOperationException
-     *             if the property is read only
+     *
+     * @param instance to update
+     * @param value    new value for the property
+     * @throws UnsupportedOperationException if the property is read only
      */
     void set(Object instance, Object value);
 
@@ -104,22 +99,22 @@ public interface PropertyAdapter extends
     Class getBeanType();
 
     /**
-     * Returns true if the property is actually a public field.
-     * 
+     * Returns true if the property is actually a public field (possibly, a public static field).
+     *
      * @since 5.2
      */
     boolean isField();
 
     /**
      * Returns the field if the property is a public field or null if the property is accessed via the read method.
-     * 
+     *
      * @since 5.2
      */
     Field getField();
 
     /**
      * The class in which the property (or public field) is defined.
-     * 
+     *
      * @since 5.2
      */
     Class getDeclaringClass();

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImplTest.java?rev=1176625&r1=1176624&r2=1176625&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImplTest.java Tue Sep 27 21:17:11 2011
@@ -41,8 +41,8 @@ public class PropertyAccessImplTest exte
 
     public static class Bean
     {
-        public static Double PI = 3.14;
-        
+        public static final Double PI = 3.14;
+
         @DataType("fred")
         @Validate("field-value-overridden")
         private int value;
@@ -250,6 +250,7 @@ public class PropertyAccessImplTest exte
     {
         // abstract class implements method from interface
         private String other;
+
         public String getOtherValue()
         {
             return other;
@@ -266,19 +267,23 @@ public class PropertyAccessImplTest exte
         private String value;
         private int intValue;
 
-        public ConcreteBean(int intValue) {
+        public ConcreteBean(int intValue)
+        {
             this.intValue = intValue;
         }
 
-        public String getValue() {
+        public String getValue()
+        {
             return value;
         }
 
-        public void setValue(String v) {
+        public void setValue(String v)
+        {
             value = v;
         }
 
-        public int getIntValue() {
+        public int getIntValue()
+        {
             return intValue;
         }
     }
@@ -341,8 +346,7 @@ public class PropertyAccessImplTest exte
             access.get(b, "zaphod");
 
             unreachable();
-        }
-        catch (IllegalArgumentException ex)
+        } catch (IllegalArgumentException ex)
         {
             assertEquals(ex.getMessage(), "Class " + CLASS_NAME + "$Bean does not "
                     + "contain a property named 'zaphod'.");
@@ -358,8 +362,7 @@ public class PropertyAccessImplTest exte
         {
             access.set(b, "class", null);
             unreachable();
-        }
-        catch (UnsupportedOperationException ex)
+        } catch (UnsupportedOperationException ex)
         {
             assertEquals(ex.getMessage(), "Class " + CLASS_NAME
                     + "$Bean does not provide an mutator ('setter') method for property 'class'.");
@@ -375,8 +378,7 @@ public class PropertyAccessImplTest exte
         {
             access.get(b, "writeOnly");
             unreachable();
-        }
-        catch (UnsupportedOperationException ex)
+        } catch (UnsupportedOperationException ex)
         {
             assertEquals(ex.getMessage(), "Class " + CLASS_NAME
                     + "$Bean does not provide an accessor ('getter') method for property 'writeOnly'.");
@@ -392,8 +394,7 @@ public class PropertyAccessImplTest exte
         {
             access.get(b, "failure");
             unreachable();
-        }
-        catch (RuntimeException ex)
+        } catch (RuntimeException ex)
         {
             assertEquals(ex.getMessage(), "Error reading property 'failure' of PropertyUtilsExceptionBean: getFailure");
         }
@@ -408,8 +409,7 @@ public class PropertyAccessImplTest exte
         {
             access.set(b, "failure", false);
             unreachable();
-        }
-        catch (RuntimeException ex)
+        } catch (RuntimeException ex)
         {
             assertEquals(ex.getMessage(), "Error updating property 'failure' of PropertyUtilsExceptionBean: setFailure");
         }
@@ -424,8 +424,7 @@ public class PropertyAccessImplTest exte
         {
             access.get(b, "google");
             unreachable();
-        }
-        catch (RuntimeException ex)
+        } catch (RuntimeException ex)
         {
             assertEquals(ex.getMessage(), "java.lang.RuntimeException: This is the UglyBean.");
         }
@@ -455,7 +454,7 @@ public class PropertyAccessImplTest exte
         ClassPropertyAdapter cpa = access.getAdapter(Bean.class);
 
         assertEquals(cpa.toString(), "<ClassPropertyAdaptor " + CLASS_NAME
-                + "$Bean : class, readOnly, value, writeOnly>");
+                + "$Bean : PI, class, readOnly, value, writeOnly>");
     }
 
     @Test
@@ -508,7 +507,30 @@ public class PropertyAccessImplTest exte
     {
         ClassPropertyAdapter cpa = access.getAdapter(Bean.class);
 
-        assertEquals(cpa.getPropertyNames(), Arrays.asList("class", "readOnly", "value", "writeOnly"));
+        assertEquals(cpa.getPropertyNames(), Arrays.asList("PI", "class", "readOnly", "value", "writeOnly"));
+    }
+
+    @Test
+    public void read_static_field()
+    {
+        PropertyAdapter adapter = access.getAdapter(Bean.class).getPropertyAdapter("pi");
+
+        assertSame(adapter.get(null), Bean.PI);
+    }
+
+    @Test
+    public void static_final_fields_may_not_be_changed()
+    {
+        PropertyAdapter adapter = access.getAdapter(Bean.class).getPropertyAdapter("pi");
+
+        try
+        {
+            adapter.set(null, 3.0d);
+            unreachable();
+        } catch (RuntimeException ex)
+        {
+            assertMessageContains(ex, "Can not set static final", "org.apache.tapestry5.ioc.internal.services.PropertyAccessImplTest$Bean.PI");
+        }
     }
 
     @Test