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 2009/02/11 03:22:18 UTC

svn commit: r743195 - in /tapestry/tapestry5/trunk: src/site/ src/site/apt/ tapestry-core/src/main/java/org/apache/tapestry5/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ tapestry-core/src/main/java/org/apache/tapestry5/internal...

Author: hlship
Date: Wed Feb 11 02:22:17 2009
New Revision: 743195

URL: http://svn.apache.org/viewvc?rev=743195&view=rev
Log:
TAP5-165: Components which use PrimaryKeyEncoder should be changed to use ValueEncoder, and PrimaryKeyEncoder should be deprecated

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoder.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoderTest.java
Modified:
    tapestry/tapestry5/trunk/src/site/apt/upgrade.apt
    tapestry/tapestry5/trunk/src/site/site.xml
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/EventConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PrimaryKeyEncoder.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/AjaxFormLoop.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Loop.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/GridFormEncoderDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/DateFieldAjaxFormLoop.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormInjectorDemo.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GridFormEncoderDemo.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ToDoList.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoderTest.java

Modified: tapestry/tapestry5/trunk/src/site/apt/upgrade.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/upgrade.apt?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/upgrade.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/upgrade.apt Wed Feb 11 02:22:17 2009
@@ -15,6 +15,24 @@
 
 Release 5.1.0.0
 
+* Primary Key Encoder
+
+  <<This is the change between releases that is most likely to affect your upgrade.>>
+
+  The {{{apidocs/org/apache/tapestry5/PrimaryKeyEncoder.html}PrimaryKeyEncoder}}
+  interface has been deprecated and will be removed in a later release.
+  See   {{{https://issues.apache.org/jira/browse/TAP5-165}TAP5-165}} for the rationale.
+
+  You may see type coercion errors on pages where you have specified the encoder parameter of
+  the Grid, Loop or AjaxFormLoop components as a PrimaryKeyEncoder.
+  These errors indicate that Tapestry was unable to automatically convert your PrimaryKeyEncoder instance into
+  a {{{apidocs/org/apache/tapestry5/ValueEncoder.html}ValueEncoder}}. Generally, the only change is to invoke the new constructor for
+  {{{apdiocs/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.html}DefaultPrimaryKeyEncoder}}, to identify
+  the type of key used.
+
+  If you don't use DefaultPrimaryKeyEncoder, you will see compile errors about the new method, getKeyType().
+  You will have to change your code to implement that new method.
+  
 * Performance Improvements
 
   As part of the changes related to
@@ -28,10 +46,11 @@
 * Tapestry/Spring
 
   There have been some significant changes to the {{{tapestry-spring/}tapestry-spring}} module, to
-  support injection of Tapestry services into Springbeans.
+  support injection of Tapestry services into Spring beans. You may find you need to add some new configuration
+  to revert to the Tapestry 5.0 behavior.
 
 * Session Persisted Objects
-
+                      
   Tapestry is now more aggressive about automatically re-storing any session persisted object
   back into the session at the end of the request (this used to only apply to application state objects).  See the 
   {{{guide/persist.html}persistent page data}} notes for more details.
@@ -39,7 +58,8 @@
 * Module Classes
 
   Many questionable practices in Tapestry module classes that used to produce warnings
-  have been changed to fail early with exceptions. The rationale is that the warnings would be ignored,
+  have been changed to fail early (that is, throw exceptions). The rationale is that the warnings
+  are almost always ignored,
   resulting in more difficult to diagnose runtime errors. 
 
   Extra public methods on module classes (methods that do not define services, contribute to services,
@@ -47,3 +67,7 @@
 
 
 
+
+
+
+

Modified: tapestry/tapestry5/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/site.xml?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/trunk/src/site/site.xml Wed Feb 11 02:22:17 2009
@@ -51,7 +51,7 @@
         </menu>
 
         <menu name="Upgrade Notes">
-            <item name="From Tapestry 5" href="upgrade.html"/>
+            <item name="From Tapestry 5.0" href="upgrade.html"/>
             <item name="From Tapestry 4" href="upgrade4.html"/>
             <item name="Upgrade Notes (5.0)" href="upgrade5.0.html"/>
             <item name="Release Notes (5.0)" href="release-notes-5.0.html"/>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/EventConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/EventConstants.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/EventConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/EventConstants.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-//  Copyright 2008 The Apache Software Foundation
+//  Copyright 2008, 2009 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.
@@ -123,4 +123,13 @@
      * also be visible in the {@link org.apache.tapestry5.PrimaryKeyEncoder encoder parameter}.
      */
     public static final String ADD_ROW = "addRow";
+
+    /**
+     * Event triggered by the {@link org.apache.tapestry5.corelib.components.Loop} component to inform its container of
+     * all the values that were supplied from the client during a form submission. The event handler method should have
+     * a single parameter, of type Object[] or type List, to receive the values.
+     *
+     * @since 5.1.0.0
+     */
+    public static final String SYNCHRONIZE_VALUES = "synchronizeValues";
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PrimaryKeyEncoder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PrimaryKeyEncoder.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PrimaryKeyEncoder.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PrimaryKeyEncoder.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 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.
@@ -27,6 +27,12 @@
  * @param <K> the type of the primary key, used to identify the value (which must be serializable)
  * @param <V> the type of value identified by the key
  * @see org.apache.tapestry5.ValueEncoder
+ * @deprecated This interface overlaps with {@link org.apache.tapestry5.ValueEncoder} and has been deprecated in release
+ *             5.1. The interface itself will be removed in a later release of Tapestry. The components that used this
+ *             interface ({@link org.apache.tapestry5.corelib.components.AjaxFormLoop}, {@link
+ *             org.apache.tapestry5.corelib.components.Grid}, {@link org.apache.tapestry5.corelib.components.GridRows}
+ *             and {@link org.apache.tapestry5.corelib.components.Loop}) have been changed to expect ValueEncoder
+ *             instead, and an automatic coercion from PrimaryKeyEncoder to ValueEncoder has been provided.
  */
 public interface PrimaryKeyEncoder<K extends Serializable, V>
 {
@@ -55,4 +61,13 @@
      * @return the value object for the key
      */
     V toValue(K key);
+
+    /**
+     * Returns the type of key. This is primarily used when Tapestry must convert an existing PrimaryKeyConverter into a
+     * {@link org.apache.tapestry5.ValueEncoder}.
+     *
+     * @return key type or null if not known
+     * @since 5.1.0.0
+     */
+    Class<K> getKeyType();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/AjaxFormLoop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/AjaxFormLoop.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/AjaxFormLoop.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/AjaxFormLoop.java Wed Feb 11 02:22:17 2009
@@ -26,7 +26,6 @@
 import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.services.*;
 
-import java.io.Serializable;
 import java.util.Collections;
 import java.util.Iterator;
 
@@ -99,7 +98,7 @@
      * Required parameter used to convert server-side objects (provided from the source) into client-side ids and back.
      */
     @Parameter(required = true, allowNull = false)
-    private PrimaryKeyEncoder encoder;
+    private ValueEncoder<Object> encoder;
 
     @InjectComponent
     private ClientElement rowInjector;
@@ -159,11 +158,7 @@
 
         public void addRemoveRowTrigger(String clientId)
         {
-            Serializable id = idForCurrentValue();
-
-            String idType = id.getClass().getName();
-
-            Link link = resources.createEventLink("triggerRemoveRow", id, idType);
+            Link link = resources.createEventLink("triggerRemoveRow", toClientValue());
 
             String asURI = link.toAbsoluteURI();
 
@@ -184,26 +179,26 @@
 
 
     /**
-     * Action for synchronizing the current element of the loop by recording its client value / primary key.
+     * Action for synchronizing the current element of the loop by recording its client value.
      */
     static class SyncValue implements ComponentAction<AjaxFormLoop>
     {
-        private final Serializable id;
+        private final String clientValue;
 
-        public SyncValue(Serializable id)
+        public SyncValue(String clientValue)
         {
-            this.id = id;
+            this.clientValue = clientValue;
         }
 
         public void execute(AjaxFormLoop component)
         {
-            component.syncValue(id);
+            component.syncValue(clientValue);
         }
 
         @Override
         public String toString()
         {
-            return String.format("AjaxFormLoop.SyncValue[%s]", id);
+            return String.format("AjaxFormLoop.SyncValue[%s]", clientValue);
         }
     }
 
@@ -274,13 +269,13 @@
 
     @SuppressWarnings({ "unchecked" })
     @Log
-    private void syncValue(Serializable id)
+    private void syncValue(String clientValue)
     {
-        Object value = encoder.toValue(id);
+        Object value = encoder.toValue(clientValue);
 
         if (value == null)
             throw new RuntimeException(
-                    String.format("Unable to convert serialized id '%s' back into an object.", id));
+                    String.format("Unable to convert client value '%s' back into a server-side object.", clientValue));
 
         this.value = value;
     }
@@ -296,21 +291,22 @@
 
     private void syncCurrentValue()
     {
-        Serializable id = idForCurrentValue();
+        String id = toClientValue();
 
-        // Add the command that restores value from the value id,
+        // Add the command that restores value from the value clientValue,
         // when the form is submitted.
 
         formSupport.store(this, new SyncValue(id));
     }
 
     /**
-     * Uses the {@link org.apache.tapestry5.PrimaryKeyEncoder} to convert the current row value to an id.
+     * Uses the {@link org.apache.tapestry5.ValueEncoder} to convert the current server-side value to a client-side
+     * value.
      */
     @SuppressWarnings({ "unchecked" })
-    private Serializable idForCurrentValue()
+    private String toClientValue()
     {
-        return encoder.toKey(value);
+        return encoder.toClient(value);
     }
 
 
@@ -396,8 +392,7 @@
         if (value == null)
             throw new IllegalArgumentException(
                     String.format("Event handler for event 'addRow' from %s should have returned a non-null value.",
-                                  resources.getCompleteId())
-            );
+                                  resources.getCompleteId()));
 
 
         renderingInjector = true;
@@ -418,13 +413,9 @@
     }
 
     @Log
-    Object onTriggerRemoveRow(String rowId, String idTypeName)
+    Object onTriggerRemoveRow(String rowId)
     {
-        Class idType = componentClassCache.forName(idTypeName);
-
-        Serializable coerced = (Serializable) typeCoercer.coerce(rowId, idType);
-
-        Object value = encoder.toValue(coerced);
+        Object value = encoder.toValue(rowId);
 
         resources.triggerEvent(EventConstants.REMOVE_ROW, new Object[] { value }, null);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 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,10 +26,7 @@
 import org.apache.tapestry5.internal.services.ClientBehaviorSupport;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.internal.util.Defense;
-import org.apache.tapestry5.services.BeanModelSource;
-import org.apache.tapestry5.services.ComponentEventResultProcessor;
-import org.apache.tapestry5.services.FormSupport;
-import org.apache.tapestry5.services.Request;
+import org.apache.tapestry5.services.*;
 
 import java.io.IOException;
 import java.util.Collections;
@@ -88,7 +85,7 @@
      * provided to override the default cell renderer for a particular column ... the components within the block can
      * use the property bound to the row parameter to know what they should render.
      */
-    @Parameter
+    @Parameter(principal = true)
     private Object row;
 
     /**
@@ -206,12 +203,12 @@
     private boolean inPlace;
 
     /**
-     * Changes how state is recorded into the form to store the {@linkplain org.apache.tapestry5.PrimaryKeyEncoder#toKey(Object)
-     * primary key} for each row (rather than the index), and restore the {@linkplain
-     * org.apache.tapestry5.PrimaryKeyEncoder#toValue(java.io.Serializable) row values} from the primary keys.
+     * Changes how state is recorded into the form to store the {@linkplain org.apache.tapestry5.ValueEncoder#toClient(Object)
+     * client value} for each row (rather than the index), and restore the {@linkplain
+     * org.apache.tapestry5.ValueEncoder#toValue(String)row values} from the client value.
      */
     @Parameter
-    private PrimaryKeyEncoder encoder;
+    private ValueEncoder encoder;
 
     /**
      * The name of the psuedo-zone that encloses the Grid.
@@ -245,7 +242,7 @@
                     "index=inherit:columnIndex",
                     "lean=inherit:lean",
                     "overrides=overrides",
-                    "zone=zone"})
+                    "zone=zone" })
     private GridColumns columns;
 
     @Component(
@@ -259,14 +256,14 @@
                     "overrides=overrides",
                     "volatile=inherit:volatile",
                     "encoder=inherit:encoder",
-                    "lean=inherit:lean"})
+                    "lean=inherit:lean" })
     private GridRows rows;
 
     @Component(parameters = {
             "source=dataSource",
             "rowsPerPage=rowsPerPage",
             "currentPage=currentPage",
-            "zone=zone"})
+            "zone=zone" })
     private GridPager pager;
 
     @Component(parameters = "to=pagerTop")
@@ -301,6 +298,14 @@
     @Environmental
     private ComponentEventResultProcessor componentEventResultProcessor;
 
+    @Inject
+    private ComponentDefaultProvider defaultsProvider;
+
+    ValueEncoder defaultEncoder()
+    {
+        return defaultsProvider.defaultValueEncoder("row", resources);
+    }
+
     /**
      * A version of GridDataSource that caches the availableRows property. This addresses TAPESTRY-2245.
      */

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/GridRows.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 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.
@@ -28,8 +28,8 @@
 package org.apache.tapestry5.corelib.components;
 
 import org.apache.tapestry5.ComponentAction;
-import org.apache.tapestry5.PrimaryKeyEncoder;
 import org.apache.tapestry5.PropertyOverrides;
+import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.annotations.Environmental;
 import org.apache.tapestry5.annotations.Parameter;
 import org.apache.tapestry5.annotations.Property;
@@ -41,7 +41,6 @@
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.FormSupport;
 
-import java.io.Serializable;
 import java.util.List;
 
 /**
@@ -52,7 +51,7 @@
  * form render and the form submission, this can cause unexpected results, including applying changes to the wrong
  * objects.
  */
-@SuppressWarnings({"unchecked"})
+@SuppressWarnings({ "unchecked" })
 public class GridRows
 {
     private int startRow;
@@ -87,52 +86,26 @@
     }
 
     /**
-     * This action is used when a {@link org.apache.tapestry5.PrimaryKeyEncoder} is provided.
+     * This action is used when a {@link org.apache.tapestry5.ValueEncoder} is provided.
      */
-    static class SetupForRowByKey implements ComponentAction<GridRows>
+    static class SetupForRowWithClientValue implements ComponentAction<GridRows>
     {
-        private final Serializable rowKey;
+        private final String clientValue;
 
-        SetupForRowByKey(Serializable rowKey)
+        SetupForRowWithClientValue(String clientValue)
         {
-            this.rowKey = rowKey;
+            this.clientValue = clientValue;
         }
 
         public void execute(GridRows component)
         {
-            component.setupForRowByKey(rowKey);
+            component.setupForRowWithClientValue(clientValue);
         }
 
         @Override
         public String toString()
         {
-            return String.format("GridRows.SetupForRowByKey[%s]", rowKey);
-        }
-    }
-
-
-    /**
-     * This action is also associated with the {@link org.apache.tapestry5.PrimaryKeyEncoder}; it allows the PKE to be
-     * informed of the series of keys to expect with the form submission.
-     */
-    static class PrepareForKeys implements ComponentAction<GridRows>
-    {
-        private List<Serializable> storedKeys;
-
-        public PrepareForKeys(List<Serializable> storedKeys)
-        {
-            this.storedKeys = storedKeys;
-        }
-
-        public void execute(GridRows component)
-        {
-            component.prepareForKeys(storedKeys);
-        }
-
-        @Override
-        public String toString()
-        {
-            return "GridRows.PrepareForKeys" + storedKeys.toString();
+            return String.format("GridRows.SetupForRowWithClientValue[%s]", clientValue);
         }
     }
 
@@ -192,12 +165,13 @@
     private boolean volatileState;
 
     /**
-     * Changes how state is recorded into the form to store the {@linkplain org.apache.tapestry5.PrimaryKeyEncoder#toKey(Object)
-     * primary key} for each row (rather than the index), and restore the {@linkplain
-     * org.apache.tapestry5.PrimaryKeyEncoder#toValue(java.io.Serializable) row values} from the primary keys.
+     * Changes how state is recorded into the form to store the {@linkplain org.apache.tapestry5.ValueEncoder#toClient(Object)
+     * client value} for each row (rather than the index), and restore the {@linkplain
+     * org.apache.tapestry5.ValueEncoder#toValue(String) row values} from the client value.
      */
     @Parameter
-    private PrimaryKeyEncoder encoder;
+    private ValueEncoder encoder;
+
 
     /**
      * Optional output parameter (only set during rendering) that identifies the current row index. This is the index on
@@ -230,8 +204,6 @@
     @Property(write = false)
     private PropertyModel columnModel;
 
-    private List<Serializable> encodedPrimaryKeys;
-
     public String getRowClass()
     {
         List<String> classes = CollectionFactory.newList();
@@ -298,16 +270,6 @@
 
         recordStateByIndex = recordingStateInsideForm && (encoder == null);
         recordStateByEncoder = recordingStateInsideForm && (encoder != null);
-
-        if (recordStateByEncoder)
-        {
-            encodedPrimaryKeys = CollectionFactory.newList();
-
-            // As we render, we'll fill in encodedPrimaryKeys.  That's ok, because nothing is serialized
-            // until later.  When the form is submitted, this will give us a chance to inform
-            // the PKE about the keys to expect.
-            formSupport.store(this, new PrepareForKeys(encodedPrimaryKeys));
-        }
     }
 
     /**
@@ -320,24 +282,15 @@
 
     /**
      * Callback method that bypasses the data source and converts a primary key back into a row value (via {@link
-     * org.apache.tapestry5.PrimaryKeyEncoder#toValue(java.io.Serializable)}).
+     * org.apache.tapestry5.ValueEncoder#toValue(String)}).
      */
-    void setupForRowByKey(Serializable rowKey)
+    void setupForRowWithClientValue(String clientValue)
     {
-        row = encoder.toValue(rowKey);
+        row = encoder.toValue(clientValue);
 
         if (row == null)
             throw new IllegalArgumentException(
-                    String.format("%s returned null for key %s.", encoder, rowKey));
-    }
-
-    /**
-     * Callback method that allows the primary key encoder to prepare for the keys that will be resolved to row values
-     * in this request.
-     */
-    private void prepareForKeys(List<Serializable> storedKeys)
-    {
-        encoder.prepareForKeys(storedKeys);
+                    String.format("%s returned null for client value '%s'.", encoder, clientValue));
     }
 
 
@@ -360,10 +313,8 @@
 
             if (recordStateByEncoder)
             {
-                Serializable key = encoder.toKey(row);
-                encodedPrimaryKeys.add(key);
-
-                formSupport.store(this, new SetupForRowByKey(key));
+                String key = encoder.toClient(row);
+                formSupport.store(this, new SetupForRowWithClientValue(key));
             }
         }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Loop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Loop.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Loop.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Loop.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 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.
@@ -18,6 +18,7 @@
 import org.apache.tapestry5.annotations.*;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.services.ComponentDefaultProvider;
 import org.apache.tapestry5.services.FormSupport;
 import org.apache.tapestry5.services.Heartbeat;
 
@@ -32,6 +33,9 @@
  * For a non-volatile Loop inside the form, the Loop stores a series of commands that start and end heartbeats and store
  * state (either as full objects when there the encoder parameter is not bound, or as client-side objects when there is
  * an encoder).
+ * <p/>
+ * When the Loop is used inside a Form, it will generate an {@link org.apache.tapestry5.EventConstants#SYNCHRONIZE_VALUES}
+ * event to inform its container what values were submitted and in what order.
  */
 @SupportsInformalParameters
 public class Loop
@@ -144,57 +148,58 @@
     /**
      * Restores the value using a stored primary key via {@link PrimaryKeyEncoder#toValue(Serializable)}.
      */
-    static class RestoreStateViaEncodedPrimaryKey implements ComponentAction<Loop>
+    static class RestoreStateFromStoredClientValue implements ComponentAction<Loop>
     {
-        private static final long serialVersionUID = -2422790241589517336L;
+        private final String clientValue;
 
-        private final Serializable primaryKey;
-
-        public RestoreStateViaEncodedPrimaryKey(final Serializable primaryKey)
+        public RestoreStateFromStoredClientValue(final String clientValue)
         {
-            this.primaryKey = primaryKey;
+            this.clientValue = clientValue;
         }
 
         public void execute(Loop component)
         {
-            component.restoreStateViaEncodedPrimaryKey(primaryKey);
+            component.restoreStateFromStoredClientValue(clientValue);
         }
 
         @Override
         public String toString()
         {
-            return String.format("Loop.RestoreStateViaEncodedPrimaryKey[%s]", primaryKey);
+            return String.format("Loop.RestoreStateFromStoredClientValue[%s]", clientValue);
         }
     }
 
     /**
-     * Stores a list of keys to be passed to {@link PrimaryKeyEncoder#prepareForKeys(List)}.
+     * Start of processing event that allows the Loop to set up internal bookeeping, to track which values have come up
+     * in the form submission.
      */
-    static class PrepareForKeys implements ComponentAction<Loop>
+    static final ComponentAction<Loop> PREPARE_FOR_SUBMISSION = new ComponentAction<Loop>()
     {
-        private static final long serialVersionUID = -6515255627142956828L;
-
-        /**
-         * The variable is final, the contents are mutable while the Loop renders.
-         */
-        private final List<Serializable> keys;
+        public void execute(Loop component)
+        {
+            component.prepareForSubmission();
+        }
 
-        public PrepareForKeys(final List<Serializable> keys)
+        @Override
+        public String toString()
         {
-            this.keys = keys;
+            return "Loop.PrepareForSubmission";
         }
+    };
 
+    static final ComponentAction<Loop> NOTIFY_CONTAINER = new ComponentAction<Loop>()
+    {
         public void execute(Loop component)
         {
-            component.prepareForKeys(keys);
+            component.notifyContainer();
         }
 
         @Override
         public String toString()
         {
-            return "Loop.PrepareForKeys" + keys;
+            return "Loop.NotifyContainer";
         }
-    }
+    };
 
     /**
      * Defines the collection of values for the loop to iterate over. If not specified, defaults to a property of the
@@ -204,11 +209,12 @@
     private Iterable<?> source;
 
     /**
-     * Optional primary key converter; if provided and inside a form and not volatile, then each iterated value is
-     * converted and stored into the form.
+     * Optional value converter; if provided (or defaulted) and inside a form and not volatile, then each iterated value
+     * is converted and stored into the form. A default for this is calculated from the type of the property bound to
+     * the value parameter.
      */
     @Parameter
-    private PrimaryKeyEncoder<Serializable, Object> encoder;
+    private ValueEncoder<Object> encoder;
 
     /**
      * If true and the Loop is enclosed by a Form, then the normal state saving logic is turned off. Defaults to false,
@@ -230,7 +236,7 @@
     /**
      * The current value, set before the component renders its body.
      */
-    @Parameter
+    @Parameter(principal = true)
     private Object value;
 
     /**
@@ -255,14 +261,28 @@
     @Inject
     private ComponentResources resources;
 
+    @Inject
+    private ComponentDefaultProvider defaultProvider;
+
     private Block cleanupBlock;
 
+    /**
+     * Objects that have been recovered via {@link org.apache.tapestry5.ValueEncoder#toValue(String)} during the
+     * processing of the loop. These are sent to the container via an event.
+     */
+    private List<Object> synchonizedValues;
+
 
     String defaultElement()
     {
         return resources.getElementName();
     }
 
+    ValueEncoder defaultEncoder()
+    {
+        return defaultProvider.defaultValueEncoder("value", resources);
+    }
+
     @SetupRender
     boolean setup()
     {
@@ -272,6 +292,9 @@
 
         storeRenderStateInForm = formSupport != null && !volatileState;
 
+        if (storeRenderStateInForm)
+            formSupport.store(this, PREPARE_FOR_SUBMISSION);
+
         // Only render the body if there is something to iterate over
 
         boolean hasContent = iterator != null && iterator.hasNext();
@@ -279,16 +302,6 @@
         if (formSupport != null && hasContent)
         {
             formSupport.store(this, volatileState ? SETUP_FOR_VOLATILE : RESET_INDEX);
-
-            if (encoder != null)
-            {
-                List<Serializable> keyList = CollectionFactory.newList();
-
-                // We'll keep updating the _keyList while the Loop renders, the values will "lock
-                // down" when the Form serializes all the data.
-
-                formSupport.store(this, new PrepareForKeys(keyList));
-            }
         }
 
         cleanupBlock = hasContent ? null : empty;
@@ -298,21 +311,17 @@
         return hasContent;
     }
 
+
     /**
      * Returns the empty block, or null, after the render has finished. It will only be the empty block (which itself
      * may be null) if the source was null or empty.
      */
     Block cleanupRender()
     {
-        return cleanupBlock;
-    }
-
-    private void prepareForKeys(List<Serializable> keys)
-    {
-        // Again, the encoder existed when we rendered, we better have another available
-        // when the enclosing Form is submitted.
+        if (storeRenderStateInForm)
+            formSupport.store(this, NOTIFY_CONTAINER);
 
-        encoder.prepareForKeys(keys);
+        return cleanupBlock;
     }
 
     private void setupForVolatile()
@@ -344,8 +353,9 @@
             }
             else
             {
-                Serializable primaryKey = encoder.toKey(value);
-                formSupport.store(this, new RestoreStateViaEncodedPrimaryKey(primaryKey));
+                String clientValue = encoder.toClient(value);
+
+                formSupport.store(this, new RestoreStateFromStoredClientValue(clientValue));
             }
         }
 
@@ -405,14 +415,28 @@
     /**
      * Restores state previously encoded by the Loop and stored into the Form.
      */
-    private void restoreStateViaEncodedPrimaryKey(Serializable primaryKey)
+    private void restoreStateFromStoredClientValue(String clientValue)
     {
-        // We assume that if a encoder is available when we rendered, that one will be available
-        // when the form is submitted. TODO: Check for this.
+        // We assume that if an encoder is available when we rendered, that one will be available
+        // when the form is submitted.
 
-        Object restoredValue = encoder.toValue(primaryKey);
+        Object restoredValue = encoder.toValue(clientValue);
 
         restoreState(restoredValue);
+
+        synchonizedValues.add(restoredValue);
+    }
+
+    private void prepareForSubmission()
+    {
+        synchonizedValues = CollectionFactory.newList();
+    }
+
+    private void notifyContainer()
+    {
+        Object[] values = synchonizedValues.toArray();
+
+        resources.triggerEvent(EventConstants.SYNCHRONIZE_VALUES, values, null);
     }
 
     // For testing:

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoder.java?rev=743195&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoder.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoder.java Wed Feb 11 02:22:17 2009
@@ -0,0 +1,84 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.util;
+
+import org.apache.tapestry5.PrimaryKeyEncoder;
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.ioc.services.Coercion;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.util.DefaultPrimaryKeyEncoder;
+
+import java.io.Serializable;
+
+/**
+ * This is a key part of the plan to eliminate {@link org.apache.tapestry5.PrimaryKeyEncoder}.
+ *
+ * @since 5.1.0.0
+ */
+@SuppressWarnings({ "unchecked" })
+public class PrimaryKeyEncoder2ValueEncoder implements Coercion<PrimaryKeyEncoder, ValueEncoder>
+{
+    // The magic of proxies: a coercion within TypeCoercer can use TypeCoercer as part of its job!
+    private final TypeCoercer coercer;
+
+    public PrimaryKeyEncoder2ValueEncoder(TypeCoercer coercer)
+    {
+        this.coercer = coercer;
+    }
+
+    public ValueEncoder coerce(final PrimaryKeyEncoder input)
+    {
+        final Class keyType = input.getKeyType();
+
+        if (keyType == null)
+        {
+            String message = String.format("Unable to extract primary key type from %s. " +
+                    "This represents a change from Tapestry 5.0 to Tapestry 5.1.", input);
+
+            if (input instanceof DefaultPrimaryKeyEncoder)
+                message +=
+                        " Class DefaultPrimaryKeyEncoder now includes a constructor for specifying the key type. " +
+                                "You should change the code that instantiates the encoder.";
+            else
+                message += " You should ensure that the getKeyType() method returns the correct Class.";
+
+            throw new RuntimeException(message);
+        }
+
+        return new ValueEncoder()
+        {
+            public String toClient(Object value)
+            {
+                Object key = input.toKey(value);
+
+                return coercer.coerce(key, String.class);
+            }
+
+            public Object toValue(String clientValue)
+            {
+                Serializable key = (Serializable) coercer.coerce(clientValue, keyType);
+
+                return input.toValue(key);
+            }
+
+            @Override
+            public String toString()
+            {
+                return String.format("<ValueEncoder coercion wrapper around PrimaryKeyEncoder[%s]>",
+                                     keyType.getName());
+            }
+        };
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Wed Feb 11 02:22:17 2009
@@ -32,6 +32,7 @@
 import org.apache.tapestry5.internal.services.*;
 import org.apache.tapestry5.internal.transform.*;
 import org.apache.tapestry5.internal.translator.*;
+import org.apache.tapestry5.internal.util.PrimaryKeyEncoder2ValueEncoder;
 import org.apache.tapestry5.internal.util.RenderableAsBlock;
 import org.apache.tapestry5.internal.util.StringRenderable;
 import org.apache.tapestry5.ioc.*;
@@ -802,10 +803,10 @@
      * component) to {@link org.apache.tapestry5.ComponentResources} <li>String to {@link
      * org.apache.tapestry5.corelib.data.BlankOption} <li> {@link org.apache.tapestry5.ComponentResources} to {@link
      * org.apache.tapestry5.PropertyOverrides} <li>String to {@link org.apache.tapestry5.Renderable} <li>{@link
-     * org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.Block} <li>String to {@link
-     * java.text.DateFormat}</ul>
+     * org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.Block} <li>String to {@link java.text.DateFormat}
+     * <li>{@link org.apache.tapestry5.PrimaryKeyEncoder} to {@link org.apache.tapestry5.ValueEncoder}</ul>
      */
-    public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration)
+    public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration, @Builtin TypeCoercer coercer)
     {
         add(configuration, ComponentResources.class, PropertyOverrides.class,
             new Coercion<ComponentResources, PropertyOverrides>()
@@ -908,6 +909,8 @@
                 return new SimpleDateFormat(input);
             }
         });
+
+        add(configuration, PrimaryKeyEncoder.class, ValueEncoder.class, new PrimaryKeyEncoder2ValueEncoder(coercer));
     }
 
     /**
@@ -1026,7 +1029,7 @@
                                                                   UpdateListenerHub updateListenerHub,
 
                                                                   @ClasspathProvider AssetFactory classpathAssetFactory,
-                                                                  
+
                                                                   ClasspathURLConverter classpathURLConverter)
     {
         ValidationMessagesSourceImpl service = new ValidationMessagesSourceImpl(configuration,

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoder.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2009 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.
@@ -32,6 +32,7 @@
  *
  * @param <K> the key type (which must be serializable)
  * @param <V> the value type
+ * @deprecated See deprecation notes for {@link org.apache.tapestry5.PrimaryKeyEncoder}.
  */
 public class DefaultPrimaryKeyEncoder<K extends Serializable, V> implements PrimaryKeyEncoder<K, V>
 {
@@ -43,6 +44,31 @@
 
     private K currentKey;
 
+    private final Class<K> keyType;
+
+    /**
+     * Compatibility with 5.0: new encoder, key type unknown. You <em>will</em> want to use the other constructor and
+     * specify the key type.
+     */
+    public DefaultPrimaryKeyEncoder()
+    {
+        this(null);
+    }
+
+    /**
+     * @since 5.1.0.0
+     */
+    public DefaultPrimaryKeyEncoder(Class<K> keyType)
+    {
+        this.keyType = keyType;
+    }
+
+
+    public Class<K> getKeyType()
+    {
+        return keyType;
+    }
+
     /**
      * Adds a new key/value pair to the encoder.
      */

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/GridFormEncoderDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/GridFormEncoderDemo.tml?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/GridFormEncoderDemo.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/GridFormEncoderDemo.tml Wed Feb 11 02:22:17 2009
@@ -9,7 +9,6 @@
         <t:errors/>
 
         <table t:id="grid" t:type="Grid" source="items" row="item" pagerposition="top"
-               encoder="encoder"
                add="id" reorder="id,title,urgency"
                rowsperpage="5">
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/DateFieldAjaxFormLoop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/DateFieldAjaxFormLoop.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/DateFieldAjaxFormLoop.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/DateFieldAjaxFormLoop.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-//  Copyright 2008 The Apache Software Foundation
+//  Copyright 2008, 2009 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.
@@ -45,7 +45,6 @@
     {
         List<DateHolder> result = CollectionFactory.newList(database.values());
 
-
         Collections.sort(result, new Comparator<DateHolder>()
         {
             public int compare(DateHolder o1, DateHolder o2)
@@ -59,7 +58,8 @@
 
     public PrimaryKeyEncoder<Integer, DateHolder> getDateHolderConverter()
     {
-        DefaultPrimaryKeyEncoder<Integer, DateHolder> result = new DefaultPrimaryKeyEncoder<Integer, DateHolder>();
+        DefaultPrimaryKeyEncoder<Integer, DateHolder> result =
+                new DefaultPrimaryKeyEncoder<Integer, DateHolder>(Integer.class);
 
         for (DateHolder dh : getDateHolders())
         {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormInjectorDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormInjectorDemo.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormInjectorDemo.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormInjectorDemo.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2009 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.
@@ -68,6 +68,11 @@
             {
                 return DB.get(key);
             }
+
+            public Class<Long> getKeyType()
+            {
+                return Long.class;
+            }
         };
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GridFormEncoderDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GridFormEncoderDemo.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GridFormEncoderDemo.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GridFormEncoderDemo.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-//  Copyright 2008 The Apache Software Foundation
+//  Copyright 2008, 2009 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,11 +14,8 @@
 
 package org.apache.tapestry5.integration.app1.pages;
 
-import org.apache.tapestry5.PrimaryKeyEncoder;
 import org.apache.tapestry5.annotations.InjectComponent;
 import org.apache.tapestry5.corelib.components.Grid;
-import org.apache.tapestry5.integration.app1.data.ToDoItem;
-import org.apache.tapestry5.util.DefaultPrimaryKeyEncoder;
 
 public class GridFormEncoderDemo extends GridFormDemo
 {
@@ -31,15 +28,4 @@
             grid.getSortModel().updateSort("title");
     }
 
-    public PrimaryKeyEncoder<Long, ToDoItem> getEncoder()
-    {
-        DefaultPrimaryKeyEncoder<Long, ToDoItem> result = new DefaultPrimaryKeyEncoder<Long, ToDoItem>();
-
-        for (ToDoItem item : getItems())
-        {
-            result.add(item.getId(), item);
-        }
-
-        return result;
-    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ToDoList.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ToDoList.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ToDoList.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ToDoList.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2009 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.
@@ -65,7 +65,7 @@
     {
         List<ToDoItem> items = database.findAll();
 
-        encoder = new DefaultPrimaryKeyEncoder<Long, ToDoItem>();
+        encoder = new DefaultPrimaryKeyEncoder<Long, ToDoItem>(long.class);
 
         for (ToDoItem item : items)
         {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java Wed Feb 11 02:22:17 2009
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.integration.app1.data.ToDoItem;
 import org.apache.tapestry5.integration.app1.data.Track;
 import org.apache.tapestry5.internal.services.GenericValueEncoderFactory;
 import org.apache.tapestry5.ioc.Configuration;
@@ -206,9 +207,10 @@
     }
 
     public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration,
-                                                    final MusicLibrary library)
+                                                    final MusicLibrary library,
+                                                    final ToDoDatabase todoDatabase)
     {
-        ValueEncoder<Track> encoder = new ValueEncoder<Track>()
+        ValueEncoder<Track> trackEncoder = new ValueEncoder<Track>()
         {
             public String toClient(Track value)
             {
@@ -224,7 +226,24 @@
         };
 
 
-        configuration.add(Track.class, GenericValueEncoderFactory.create(encoder));
+        configuration.add(Track.class, GenericValueEncoderFactory.create(trackEncoder));
+
+        ValueEncoder<ToDoItem> todoEncoder = new ValueEncoder<ToDoItem>()
+        {
+            public String toClient(ToDoItem value)
+            {
+                return String.valueOf(value.getId());
+            }
+
+            public ToDoItem toValue(String clientValue)
+            {
+                long id = Long.parseLong(clientValue);
+
+                return todoDatabase.get(id);
+            }
+        };
+
+        configuration.add(ToDoItem.class, GenericValueEncoderFactory.create(todoEncoder));
     }
 
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoderTest.java?rev=743195&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoderTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/PrimaryKeyEncoder2ValueEncoderTest.java Wed Feb 11 02:22:17 2009
@@ -0,0 +1,125 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.util;
+
+import org.apache.tapestry5.PrimaryKeyEncoder;
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.util.DefaultPrimaryKeyEncoder;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class PrimaryKeyEncoder2ValueEncoderTest extends InternalBaseTestCase
+{
+
+    private PrimaryKeyEncoder2ValueEncoder coercion;
+
+    @BeforeClass
+    public void setup()
+    {
+        TypeCoercer coercer = getService(TypeCoercer.class);
+
+        coercion = new PrimaryKeyEncoder2ValueEncoder(coercer);
+    }
+
+    @Test
+    public void key_type_is_known()
+    {
+        PrimaryKeyEncoder pke = newMock(PrimaryKeyEncoder.class);
+
+        Object value = new Object();
+        Long primaryKey = new Long(99);
+
+        expect(pke.getKeyType()).andReturn(Long.class);
+
+        expect(pke.toKey(value)).andReturn(primaryKey);
+
+        expect(pke.toValue(primaryKey)).andReturn(value);
+
+        replay();
+
+        ValueEncoder ve = coercion.coerce(pke);
+
+        assertEquals(ve.toClient(value), "99");
+        assertEquals(ve.toValue("99"), value);
+
+        verify();
+    }
+
+    @Test
+    public void unknown_key_type()
+    {
+        PrimaryKeyEncoder pke = new PrimaryKeyEncoder()
+        {
+            public Serializable toKey(Object value)
+            {
+                return null;
+            }
+
+            public void prepareForKeys(List keys)
+            {
+            }
+
+            public Object toValue(Serializable key)
+            {
+                return null;
+            }
+
+            public Class getKeyType()
+            {
+                return null;
+            }
+
+            @Override
+            public String toString()
+            {
+                return "<Dummy>";
+            }
+        };
+
+        try
+        {
+            coercion.coerce(pke);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertMessageContains(ex,
+                                  "Unable to extract primary key type from <Dummy>.",
+                                  "You should ensure that the getKeyType() method returns the correct Class.");
+        }
+    }
+
+    @Test
+    public void unknown_key_type_for_default_pke()
+    {
+        try
+        {
+            coercion.coerce(new DefaultPrimaryKeyEncoder());
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertMessageContains(ex,
+                                  "Class DefaultPrimaryKeyEncoder now includes a constructor for specifying the key type.");
+        }
+    }
+
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoderTest.java?rev=743195&r1=743194&r2=743195&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoderTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/util/DefaultPrimaryKeyEncoderTest.java Wed Feb 11 02:22:17 2009
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2009 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.
@@ -23,11 +23,12 @@
 {
     static class IntStringEncoder extends DefaultPrimaryKeyEncoder<Integer, String>
     {
-
+        public IntStringEncoder()
+        {
+            super(Integer.class);
+        }
     }
 
-    ;
-
     private final int FRED_ID = 1;
 
     private final String FRED = "FRED";