tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject svn commit: r295118 - in /jakarta/tapestry/trunk: ./ framework/src/documentation/content/xdocs/tapestry/ComponentReference/ framework/src/java/org/apache/tapestry/binding/ framework/src/java/org/apache/tapestry/components/ framework/src/java/org/apache...
Date Wed, 05 Oct 2005 21:07:27 GMT
Author: hlship
Date: Wed Oct  5 14:07:19 2005
New Revision: 295118

URL: http://svn.apache.org/viewcvs?rev=295118&view=rev
Log:
Add DefaultPrimaryKeyConverter for use with the For component

Added:
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerBindingFactory.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/DefaultPrimaryKeyConverter.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilMessages.java
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilStrings.properties
    jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/util/DefaultPrimaryKeyConverterTest.java
Modified:
    jakarta/tapestry/trunk/framework/src/documentation/content/xdocs/tapestry/ComponentReference/For.xml
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java
    jakarta/tapestry/trunk/status.xml

Modified: jakarta/tapestry/trunk/framework/src/documentation/content/xdocs/tapestry/ComponentReference/For.xml
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/documentation/content/xdocs/tapestry/ComponentReference/For.xml?rev=295118&r1=295117&r2=295118&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/documentation/content/xdocs/tapestry/ComponentReference/For.xml (original)
+++ jakarta/tapestry/trunk/framework/src/documentation/content/xdocs/tapestry/ComponentReference/For.xml Wed Oct  5 14:07:19 2005
@@ -36,7 +36,9 @@
 </p>
 
 <p>
-  <strong>See also: <link href="&apiroot;/components/ForBean.html">org.apache.tapestry.components.ForBean</link>, &Foreach;</strong> 
+  <strong>See also: <link href="&apiroot;/components/ForBean.html">org.apache.tapestry.components.ForBean</link>, 
+    <link href="&apiroot;/utils/DefaultPrimaryKeyConverter.html">org.apache.tapestry.utils.DefaultPrimaryKeyConverter</link>,
+    &Foreach;</strong> 
 </p>
 
 <section>

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerBindingFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerBindingFactory.java?rev=295118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerBindingFactory.java (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/binding/ListenerBindingFactory.java Wed Oct  5 14:07:19 2005
@@ -0,0 +1,37 @@
+// Copyright 2004, 2005 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.tapestry.binding;
+
+import org.apache.hivemind.Location;
+import org.apache.tapestry.IBinding;
+import org.apache.tapestry.IComponent;
+
+/**
+ * Factory of {@link org.apache.tapestry.binding.ListenerMethodBinding}, mapped to the "listener:"
+ * prefix.
+ * 
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public class ListenerBindingFactory extends AbstractBindingFactory
+{
+
+    public IBinding createBinding(IComponent root, String description, String path,
+            Location location)
+    {
+        return new ListenerMethodBinding(root, path, description, getValueConverter(), location);
+    }
+
+}
\ No newline at end of file

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java?rev=295118&r1=295117&r2=295118&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java Wed Oct  5 14:07:19 2005
@@ -34,39 +34,52 @@
 
 /**
  * @author mb
+ * @since 4.0
+ * @see org.apache.tapestry.components.IPrimaryKeyConverter
+ * @see org.apache.tapestry.util.DefaultPrimaryKeyConverter
  */
-public abstract class ForBean extends AbstractFormComponent {
-	// constants
+public abstract class ForBean extends AbstractFormComponent
+{
+    // constants
     private static final char DESC_VALUE = 'V';
-	private static final char DESC_PRIMARY_KEY = 'P';
+
+    private static final char DESC_PRIMARY_KEY = 'P';
 
     private final RepSource COMPLETE_REP_SOURCE = new CompleteRepSource();
+
     private final RepSource KEY_EXPRESSION_REP_SOURCE = new KeyExpressionRepSource();
-    
-	// parameters
+
+    // parameters
     public abstract String getElement();
+
     public abstract String getKeyExpression();
+
     public abstract IPrimaryKeyConverter getConverter();
+
     public abstract Object getDefaultValue();
+
     public abstract boolean getMatch();
+
     public abstract boolean getVolatile();
 
     // injects
     public abstract DataSqueezer getDataSqueezer();
+
     public abstract ValueConverter getValueConverter();
+
     public abstract ExpressionEvaluator getExpressionEvaluator();
 
     // intermediate members
     private Object _value;
+
     private int _index;
+
     private boolean _rendering;
 
-    
     /**
-     *  Gets the source binding and iterates through
-     *  its values.  For each, it updates the value binding and render's its wrapped elements.
-     *
-     **/
+     * Gets the source binding and iterates through its values. For each, it updates the value
+     * binding and render's its wrapped elements.
+     */
     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
     {
         // form may be null if component is not located in a form
@@ -80,9 +93,9 @@
 
         // Get the data to be iterated upon. Store in form if needed.
         Iterator dataSource = getData(cycle, form);
-        
-        // Do not iterate if dataSource is null. 
-        // The dataSource was either not convertable to Iterator, or was empty. 
+
+        // Do not iterate if dataSource is null.
+        // The dataSource was either not convertable to Iterator, or was empty.
         if (dataSource == null)
             return;
 
@@ -96,12 +109,12 @@
 
             while (dataSource.hasNext())
             {
-            	// Get current value
+                // Get current value
                 _value = dataSource.next();
 
                 // Update output component parameters
                 updateOutputParameters();
-            	
+
                 // Render component
                 if (element != null)
                 {
@@ -123,36 +136,34 @@
             _value = null;
         }
     }
-    
-    
+
     /**
-     *  Returns the most recent value extracted from the source parameter.
-     *
-     *  @throws org.apache.tapestry.ApplicationRuntimeException if the For is not currently rendering.
-     *
-     **/
+     * Returns the most recent value extracted from the source parameter.
+     * 
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if the For is not currently rendering.
+     */
 
     public final Object getValue()
     {
         if (!_rendering)
             throw Tapestry.createRenderOnlyPropertyException(this, "value");
-  
+
         return _value;
     }
 
     /**
-     *  The index number, within the {@link #getSource() source}, of the
-     *  the current value.
-     * 
-     *  @throws org.apache.tapestry.ApplicationRuntimeException if the For is not currently rendering.
+     * The index number, within the {@link #getSource() source}, of the the current value.
      * 
-     **/
-    
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if the For is not currently rendering.
+     */
+
     public int getIndex()
     {
         if (!_rendering)
             throw Tapestry.createRenderOnlyPropertyException(this, "index");
-        
+
         return _index;
     }
 
@@ -166,13 +177,13 @@
      */
     protected void updateOutputParameters()
     {
-    	IBinding indexBinding = getBinding("index");
-    	if (indexBinding != null)
-    		indexBinding.setObject(new Integer(_index));
-
-    	IBinding valueBinding = getBinding("value");
-    	if (valueBinding != null)
-    		valueBinding.setObject(_value);
+        IBinding indexBinding = getBinding("index");
+        if (indexBinding != null)
+            indexBinding.setObject(new Integer(_index));
+
+        IBinding valueBinding = getBinding("value");
+        if (valueBinding != null)
+            valueBinding.setObject(_value);
     }
 
     /**
@@ -180,394 +191,433 @@
      */
     protected void updatePrimaryKeysParameter(String[] stringReps)
     {
-    	IBinding primaryKeysBinding = getBinding("primaryKeys");
-    	if (primaryKeysBinding == null)
-    		return;
+        IBinding primaryKeysBinding = getBinding("primaryKeys");
+        if (primaryKeysBinding == null)
+            return;
+
+        DataSqueezer squeezer = getDataSqueezer();
 
-    	DataSqueezer squeezer = getDataSqueezer();
-    	
         int repsCount = stringReps.length;
         List primaryKeys = new ArrayList(repsCount);
-    	for (int i = 0; i < stringReps.length; i++) {
-			String rep = stringReps[i];
-			if (rep.length() == 0 || rep.charAt(0) != DESC_PRIMARY_KEY)
-				continue;
-			Object primaryKey = squeezer.unsqueeze(rep.substring(1));
-			primaryKeys.add(primaryKey);
-		}
-    	
-    	primaryKeysBinding.setObject(primaryKeys);
-    }
-    
-	// Do nothing in those methods, but make the JVM happy
-    protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) { }
-    protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) { }
-
-    
-    
-    
-    /**
-     * Returns a list with the values to be iterated upon.
-     * 
-     * The list is obtained in different ways:
-     * - If the component is not located in a form or 'volatile' is set to true, 
-     *   then the simply the values passed to 'source' are returned (same as Foreach)
-     * - If the component is in a form, and the form is rewinding, the values stored 
-     *    in the form are returned -- rewind is then always the same as render.
-     * - If the component is in a form, and the form is being rendered, the values
-     *   are stored in the form as Hidden fields. 
+        for (int i = 0; i < stringReps.length; i++)
+        {
+            String rep = stringReps[i];
+            if (rep.length() == 0 || rep.charAt(0) != DESC_PRIMARY_KEY)
+                continue;
+            Object primaryKey = squeezer.unsqueeze(rep.substring(1));
+            primaryKeys.add(primaryKey);
+        }
+
+        primaryKeysBinding.setObject(primaryKeys);
+    }
+
+    // Do nothing in those methods, but make the JVM happy
+    protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle)
+    {
+    }
+
+    protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
+    {
+    }
+
+    /**
+     * Returns a list with the values to be iterated upon. The list is obtained in different ways: -
+     * If the component is not located in a form or 'volatile' is set to true, then the simply the
+     * values passed to 'source' are returned (same as Foreach) - If the component is in a form, and
+     * the form is rewinding, the values stored in the form are returned -- rewind is then always
+     * the same as render. - If the component is in a form, and the form is being rendered, the
+     * values are stored in the form as Hidden fields.
      * 
-     * @param cycle The current request cycle
-     * @param form The form within which the component is located (if any)
+     * @param cycle
+     *            The current request cycle
+     * @param form
+     *            The form within which the component is located (if any)
      * @return An iterator with the values to be cycled upon
-     **/
-    private Iterator getData(IRequestCycle cycle, IForm form) {
+     */
+    private Iterator getData(IRequestCycle cycle, IForm form)
+    {
         if (form == null || getVolatile())
-        	return evaluateSourceIterator();
-        
+            return evaluateSourceIterator();
+
         String name = form.getElementId(this);
         if (cycle.isRewinding())
-        	return getStoredData(cycle, name);
-       	return storeSourceData(form, name);
+            return getStoredData(cycle, name);
+        return storeSourceData(form, name);
     }
 
     /**
-     *  Returns a list of the values stored as Hidden fields in the form.
-     *  A conversion is performed if the primary key of the value is stored.
-     *  
-     *  @param cycle The current request cycle
-     *  @param name The name of the HTTP parameter whether the values 
-     *  @return an iterator with the values stored in the provided Hidden fields
-     **/
+     * Returns a list of the values stored as Hidden fields in the form. A conversion is performed
+     * if the primary key of the value is stored.
+     * 
+     * @param cycle
+     *            The current request cycle
+     * @param name
+     *            The name of the HTTP parameter whether the values
+     * @return an iterator with the values stored in the provided Hidden fields
+     */
     protected Iterator getStoredData(IRequestCycle cycle, String name)
     {
         String[] stringReps = cycle.getParameters(name);
         if (stringReps == null)
-        	return null;
-        
+            return null;
+
         updatePrimaryKeysParameter(stringReps);
-        
+
         Iterator sourceIterator = evaluateSourceIterator();
         Iterator fullSourceIterator = evaluateFullSourceIterator();
         Map repToValueMap = new HashMap();
-        
+
         int valueCount = stringReps.length;
         List values = new ArrayList(valueCount);
-        for (int i = 0; i < valueCount; i++) {
-			String rep = stringReps[i];
-			Object value = getValueFromStringRep(sourceIterator, fullSourceIterator, repToValueMap, rep);
-			values.add(value);
-		}
-        
+        for (int i = 0; i < valueCount; i++)
+        {
+            String rep = stringReps[i];
+            Object value = getValueFromStringRep(
+                    sourceIterator,
+                    fullSourceIterator,
+                    repToValueMap,
+                    rep);
+            values.add(value);
+        }
+
         return values.iterator();
     }
-    
+
     /**
-     *  Stores the provided data in the form and then returns the data as an iterator.
-     *  If the primary key of the value can be determined, 
-     *  then that primary key is saved instead.
-     *   
-     *  @param form The form where the data will be stored
-     *  @param name The name under which the data will be stored
-     *  @return an iterator with the bound values stored in the form 
-     **/
+     * Stores the provided data in the form and then returns the data as an iterator. If the primary
+     * key of the value can be determined, then that primary key is saved instead.
+     * 
+     * @param form
+     *            The form where the data will be stored
+     * @param name
+     *            The name under which the data will be stored
+     * @return an iterator with the bound values stored in the form
+     */
     protected Iterator storeSourceData(IForm form, String name)
     {
         List values = new ArrayList();
-    	
+
         Iterator it = evaluateSourceIterator();
-    	while (it.hasNext()) {
-    		Object value = it.next();
-    		values.add(value);
-    		
-    		String rep = getStringRepFromValue(value);
-    		form.addHiddenValue(name, rep);
-    	}
-    	
-    	return values.iterator();
-    }
-
-    
-    /**
-     * Returns the string representation of the value.
-     *  
-     * The first letter of the string representation shows whether a value
-     * or a primary key is being described.
+        while (it.hasNext())
+        {
+            Object value = it.next();
+            values.add(value);
+
+            String rep = getStringRepFromValue(value);
+            form.addHiddenValue(name, rep);
+        }
+
+        return values.iterator();
+    }
+
+    /**
+     * Returns the string representation of the value. The first letter of the string representation
+     * shows whether a value or a primary key is being described.
      * 
      * @param value
      * @return
      */
-    protected String getStringRepFromValue(Object value) {
-    	String rep;
-    	DataSqueezer squeezer = getDataSqueezer();
-    	
-    	// try to extract the primary key from the value
-    	Object pk = getPrimaryKeyFromValue(value);
-    	if (pk != null)
-    		// Primary key was extracted successfully. 
-    		rep = DESC_PRIMARY_KEY + squeezer.squeeze(pk);
-    	else
-    		// primary key could not be extracted. squeeze value.
-    		rep = DESC_VALUE + squeezer.squeeze(value);
-    	
-    	return rep;
+    protected String getStringRepFromValue(Object value)
+    {
+        String rep;
+        DataSqueezer squeezer = getDataSqueezer();
+
+        // try to extract the primary key from the value
+        Object pk = getPrimaryKeyFromValue(value);
+        if (pk != null)
+            // Primary key was extracted successfully.
+            rep = DESC_PRIMARY_KEY + squeezer.squeeze(pk);
+        else
+            // primary key could not be extracted. squeeze value.
+            rep = DESC_VALUE + squeezer.squeeze(value);
+
+        return rep;
     }
 
     /**
-     * Returns the primary key of the given value. 
-     * Uses the 'keyExpression' or the 'converter' (if either is provided).
+     * Returns the primary key of the given value. Uses the 'keyExpression' or the 'converter' (if
+     * either is provided).
      * 
-     * @param value The value from which the primary key should be extracted
+     * @param value
+     *            The value from which the primary key should be extracted
      * @return The primary key of the value, or null if such cannot be extracted.
      */
-    protected Object getPrimaryKeyFromValue(Object value) {
-    	if (value == null)
-    		return null;
-    	
-    	Object primaryKey = getKeyExpressionFromValue(value);
-		if (primaryKey == null)
-			primaryKey = getConverterFromValue(value);
+    protected Object getPrimaryKeyFromValue(Object value)
+    {
+        if (value == null)
+            return null;
 
-    	return primaryKey;
+        Object primaryKey = getKeyExpressionFromValue(value);
+        if (primaryKey == null)
+            primaryKey = getConverterFromValue(value);
+
+        return primaryKey;
     }
-    
+
     /**
      * Uses the 'keyExpression' parameter to determine the primary key of the given value
      * 
-     * @param value The value from which the primary key should be extracted
-     * @return The primary key of the value as defined by 'keyExpression', 
-     * or null if such cannot be extracted.
-     */
-    protected Object getKeyExpressionFromValue(Object value) {
-    	String keyExpression = getKeyExpression();
-		if (keyExpression == null)
-			return null;
-		
-		Object primaryKey = getExpressionEvaluator().read(value, keyExpression);
-		return primaryKey;
+     * @param value
+     *            The value from which the primary key should be extracted
+     * @return The primary key of the value as defined by 'keyExpression', or null if such cannot be
+     *         extracted.
+     */
+    protected Object getKeyExpressionFromValue(Object value)
+    {
+        String keyExpression = getKeyExpression();
+        if (keyExpression == null)
+            return null;
+
+        Object primaryKey = getExpressionEvaluator().read(value, keyExpression);
+        return primaryKey;
     }
-    
+
     /**
      * Uses the 'converter' parameter to determine the primary key of the given value
      * 
-     * @param value The value from which the primary key should be extracted
-     * @return The primary key of the value as provided by the converter, 
-     * or null if such cannot be extracted.
+     * @param value
+     *            The value from which the primary key should be extracted
+     * @return The primary key of the value as provided by the converter, or null if such cannot be
+     *         extracted.
      */
-    protected Object getConverterFromValue(Object value) {
-    	IPrimaryKeyConverter converter = getConverter();
-		if (converter == null)
-			return null;
-		
-		Object primaryKey = converter.getPrimaryKey(value);
-		return primaryKey;
+    protected Object getConverterFromValue(Object value)
+    {
+        IPrimaryKeyConverter converter = getConverter();
+        if (converter == null)
+            return null;
+
+        Object primaryKey = converter.getPrimaryKey(value);
+        return primaryKey;
     }
-    
+
     /**
-     * Determines the value that corresponds to the given string representation.
+     * Determines the value that corresponds to the given string representation. If the 'match'
+     * parameter is true, attempt to find a value in 'source' or 'fullSource' that generates the
+     * same string representation. Otherwise, create a new value from the string representation.
      * 
-     * If the 'match' parameter is true, attempt to find a value in 'source' 
-     * or 'fullSource' that generates the same string representation.
+     * @param rep
+     *            the string representation for which a value should be returned
+     * @return the value that corresponds to the provided string representation
+     */
+    protected Object getValueFromStringRep(Iterator sourceIterator, Iterator fullSourceIterator,
+            Map repToValueMap, String rep)
+    {
+        Object value = null;
+        DataSqueezer squeezer = getDataSqueezer();
+
+        // Check if the string rep is empty. If so, just return the default value.
+        if (rep == null || rep.length() == 0)
+            return getDefaultValue();
+
+        // If required, find a value with an equivalent string representation and return it
+        boolean match = getMatch();
+        if (match)
+        {
+            value = findValueWithStringRep(
+                    sourceIterator,
+                    fullSourceIterator,
+                    repToValueMap,
+                    rep,
+                    COMPLETE_REP_SOURCE);
+            if (value != null)
+                return value;
+        }
+
+        // Matching of the string representation was not successful or was disabled.
+        // Use the standard approaches to obtain the value from the rep.
+        char desc = rep.charAt(0);
+        String squeezed = rep.substring(1);
+        switch (desc)
+        {
+            case DESC_VALUE:
+                // If the string rep is just the value itself, unsqueeze it
+                value = squeezer.unsqueeze(squeezed);
+                break;
+
+            case DESC_PRIMARY_KEY:
+                // Perform keyExpression match if not already attempted
+                if (!match && getKeyExpression() != null)
+                    value = findValueWithStringRep(
+                            sourceIterator,
+                            fullSourceIterator,
+                            repToValueMap,
+                            rep,
+                            KEY_EXPRESSION_REP_SOURCE);
+
+                // If 'converter' is defined, try to perform conversion from primary key to value
+                if (value == null)
+                {
+                    IPrimaryKeyConverter converter = getConverter();
+                    if (converter != null)
+                    {
+                        Object pk = squeezer.unsqueeze(squeezed);
+                        value = converter.getValue(pk);
+                    }
+                }
+                break;
+        }
+
+        if (value == null)
+            value = getDefaultValue();
+
+        return value;
+    }
+
+    /**
+     * Attempt to find a value in 'source' or 'fullSource' that generates the provided string
+     * representation. Use the RepSource interface to determine what the string representation of a
+     * particular value is.
      * 
-     * Otherwise, create a new value from the string representation. 
+     * @param rep
+     *            the string representation for which a value should be returned
+     * @param repSource
+     *            an interface providing the string representation of a given value
+     * @return the value in 'source' or 'fullSource' that corresponds to the provided string
+     *         representation
+     */
+    protected Object findValueWithStringRep(Iterator sourceIterator, Iterator fullSourceIterator,
+            Map repToValueMap, String rep, RepSource repSource)
+    {
+        Object value = repToValueMap.get(rep);
+        if (value != null)
+            return value;
+
+        value = findValueWithStringRepInIterator(sourceIterator, repToValueMap, rep, repSource);
+        if (value != null)
+            return value;
+
+        value = findValueWithStringRepInIterator(fullSourceIterator, repToValueMap, rep, repSource);
+        return value;
+    }
+
+    /**
+     * Attempt to find a value in the provided collection that generates the required string
+     * representation. Use the RepSource interface to determine what the string representation of a
+     * particular value is.
      * 
-     * @param rep the string representation for which a value should be returned
-     * @return the value that corresponds to the provided string representation
+     * @param rep
+     *            the string representation for which a value should be returned
+     * @param repSource
+     *            an interface providing the string representation of a given value
+     * @param it
+     *            the iterator of the collection in which a value should be searched
+     * @return the value in the provided collection that corresponds to the required string
+     *         representation
      */
-    protected Object getValueFromStringRep(Iterator sourceIterator, Iterator fullSourceIterator, 
-    		Map repToValueMap, String rep) {
-    	Object value = null;
-    	DataSqueezer squeezer = getDataSqueezer();
-    	
-    	// Check if the string rep is empty. If so, just return the default value.
-    	if (rep == null || rep.length() == 0)
-    		return getDefaultValue();
-    	
-    	// If required, find a value with an equivalent string representation and return it 
-    	boolean match = getMatch();
-		if (match) {
-			value = findValueWithStringRep(sourceIterator, fullSourceIterator, repToValueMap, rep, COMPLETE_REP_SOURCE);
-			if (value != null)
-				return value;
-		}
-
-		// Matching of the string representation was not successful or was disabled. 
-		// Use the standard approaches to obtain the value from the rep. 
-		char desc = rep.charAt(0);
-		String squeezed = rep.substring(1);
-    	switch (desc) {
-    		case DESC_VALUE:
-    			// If the string rep is just the value itself, unsqueeze it
-    			value = squeezer.unsqueeze(squeezed);
-    			break;
-    			
-    		case DESC_PRIMARY_KEY:
-    			// Perform keyExpression match if not already attempted
-    			if (!match && getKeyExpression() != null)
-    				value = findValueWithStringRep(sourceIterator, fullSourceIterator, repToValueMap, rep, KEY_EXPRESSION_REP_SOURCE);
-
-    			// If 'converter' is defined, try to perform conversion from primary key to value 
-    			if (value == null) {
-    				IPrimaryKeyConverter converter = getConverter();
-    				if (converter != null) {
-    					Object pk = squeezer.unsqueeze(squeezed);
-    					value = converter.getValue(pk);
-    				}
-    			}
-    			break;
-    	}
-    	
-    	if (value == null)
-    		value = getDefaultValue();
-    	
-    	return value;
-    }
-    
-    /**
-     * Attempt to find a value in 'source' or 'fullSource' that generates 
-     * the provided string representation. 
-     * 
-     * Use the RepSource interface to determine what the string representation
-     * of a particular value is. 
-     * 
-     * @param rep the string representation for which a value should be returned
-     * @param repSource an interface providing the string representation of a given value
-     * @return the value in 'source' or 'fullSource' that corresponds 
-     * to the provided string representation
-     */
-    protected Object findValueWithStringRep(Iterator sourceIterator, Iterator fullSourceIterator, 
-    		Map repToValueMap, String rep, RepSource repSource) {
-    	Object value = repToValueMap.get(rep);
-    	if (value != null)
-    		return value;
-
-		value = findValueWithStringRepInIterator(sourceIterator, repToValueMap, rep, repSource);
-    	if (value != null)
-    		return value;
-		
-		value = findValueWithStringRepInIterator(fullSourceIterator, repToValueMap, rep, repSource);
-   		return value;
-    }
-
-    /**
-     * Attempt to find a value in the provided collection that generates 
-     * the required string representation. 
-     * 
-     * Use the RepSource interface to determine what the string representation
-     * of a particular value is. 
-     * 
-     * @param rep the string representation for which a value should be returned
-     * @param repSource an interface providing the string representation of a given value
-     * @param it the iterator of the collection in which a value should be searched
-     * @return the value in the provided collection that corresponds 
-     * to the required string representation
-     */
-    protected Object findValueWithStringRepInIterator(Iterator it, Map repToValueMap, String rep, RepSource repSource) {
-		while (it.hasNext()) {
-    		Object sourceValue = it.next();
-    		if (sourceValue == null)
-    			continue;
-    		
-    		String sourceRep = repSource.getStringRep(sourceValue);
-       		repToValueMap.put(sourceRep, sourceValue);
-        	
-        	if (rep.equals(sourceRep))
-        		return sourceValue;
-		}
-		
-		return null;
+    protected Object findValueWithStringRepInIterator(Iterator it, Map repToValueMap, String rep,
+            RepSource repSource)
+    {
+        while (it.hasNext())
+        {
+            Object sourceValue = it.next();
+            if (sourceValue == null)
+                continue;
+
+            String sourceRep = repSource.getStringRep(sourceValue);
+            repToValueMap.put(sourceRep, sourceValue);
+
+            if (rep.equals(sourceRep))
+                return sourceValue;
+        }
+
+        return null;
     }
-        
+
     /**
-     * Returns a new iterator of the values in 'source'. 
+     * Returns a new iterator of the values in 'source'.
      * 
      * @return the 'source' iterator
      */
     protected Iterator evaluateSourceIterator()
     {
-    	Iterator it = null;
-    	Object source = null;
-    	
-    	IBinding sourceBinding = getBinding("source");
-    	if (sourceBinding != null)
-    		source = sourceBinding.getObject();
+        Iterator it = null;
+        Object source = null;
+
+        IBinding sourceBinding = getBinding("source");
+        if (sourceBinding != null)
+            source = sourceBinding.getObject();
 
         if (source != null)
-        	it = (Iterator) getValueConverter().coerceValue(source, Iterator.class);
+            it = (Iterator) getValueConverter().coerceValue(source, Iterator.class);
 
         if (it == null)
-			it = Collections.EMPTY_LIST.iterator();
-    	
-		return it;
+            it = Collections.EMPTY_LIST.iterator();
+
+        return it;
     }
-    
+
     /**
-     * Returns a new iterator of the values in 'fullSource'. 
+     * Returns a new iterator of the values in 'fullSource'.
      * 
      * @return the 'fullSource' iterator
      */
     protected Iterator evaluateFullSourceIterator()
     {
-    	Iterator it = null;
-    	Object fullSource = null;
-    	
-    	IBinding fullSourceBinding = getBinding("fullSource");
-    	if (fullSourceBinding != null)
-    		fullSource = fullSourceBinding.getObject();
+        Iterator it = null;
+        Object fullSource = null;
+
+        IBinding fullSourceBinding = getBinding("fullSource");
+        if (fullSourceBinding != null)
+            fullSource = fullSourceBinding.getObject();
 
         if (fullSource != null)
-        	it = (Iterator) getValueConverter().coerceValue(fullSource, Iterator.class);
+            it = (Iterator) getValueConverter().coerceValue(fullSource, Iterator.class);
 
         if (it == null)
-			it = Collections.EMPTY_LIST.iterator();
-    	
-		return it;
+            it = Collections.EMPTY_LIST.iterator();
+
+        return it;
     }
-    
+
     /**
      * An interface that provides the string representation of a given value
      */
-    protected interface RepSource {
-    	String getStringRep(Object value);
+    protected interface RepSource
+    {
+        String getStringRep(Object value);
     }
-    
+
     /**
-     * An implementation of RepSource that provides the string representation
-     * of the given value using all methods.
+     * An implementation of RepSource that provides the string representation of the given value
+     * using all methods.
      */
-    protected class CompleteRepSource implements RepSource {
-    	public String getStringRep(Object value) {
-    		return getStringRepFromValue(value);
-    	}
+    protected class CompleteRepSource implements RepSource
+    {
+        public String getStringRep(Object value)
+        {
+            return getStringRepFromValue(value);
+        }
     }
-    
+
     /**
-     * An implementation of RepSource that provides the string representation
-     * of the given value using just the 'keyExpression' parameter.
+     * An implementation of RepSource that provides the string representation of the given value
+     * using just the 'keyExpression' parameter.
      */
-    protected class KeyExpressionRepSource implements RepSource {
-    	public String getStringRep(Object value) {
-    		Object pk = getKeyExpressionFromValue(value);
-    		return DESC_PRIMARY_KEY + getDataSqueezer().squeeze(pk);
-    	}
+    protected class KeyExpressionRepSource implements RepSource
+    {
+        public String getStringRep(Object value)
+        {
+            Object pk = getKeyExpressionFromValue(value);
+            return DESC_PRIMARY_KEY + getDataSqueezer().squeeze(pk);
+        }
     }
 
     /**
      * For component can not take focus.
      */
-    protected boolean getCanTakeFocus() {
+    protected boolean getCanTakeFocus()
+    {
         return false;
     }
-    
+
     public String getClientId()
     {
         return null;
     }
-    
+
     public String getDisplayName()
     {
         return null;
     }
-    
-    
+
 }

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/DefaultPrimaryKeyConverter.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/DefaultPrimaryKeyConverter.java?rev=295118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/DefaultPrimaryKeyConverter.java (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/DefaultPrimaryKeyConverter.java Wed Oct  5 14:07:19 2005
@@ -0,0 +1,289 @@
+// Copyright 2005 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.tapestry.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hivemind.ApplicationRuntimeException;
+import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.components.IPrimaryKeyConverter;
+
+/**
+ * Companion to the {@link org.apache.tapestry.components.ForBean For component}, this class is an
+ * implementation of {@link org.apache.tapestry.components.IPrimaryKeyConverter} that performs some
+ * additional handling, such as tracking value sets..
+ * <p>
+ * Value sets are sets of value objects maintained by the converter; the converter will provide a
+ * synthetic read/write boolean property that indicates if the {@link #getLastValue() last value} is
+ * or is not in the set.
+ * <p>
+ * A single built-in value set, {@link #isDeleted()} has a special purpose; it controls whether or
+ * not values are returned from {@link #getValues()}. Subclasses may add additional synthetic
+ * boolean properties and additional sets.
+ * <p>
+ * Why not just store a boolean property in the object itself? Well, deleted is a good example of a
+ * property that is meaningful in the context of an operation, but isn't stored ... once an object
+ * is deleted (from secondary storage, such as a database) there's no place to store such a flag.
+ * The DefaultPrimaryKey converter is used in this context to store transient, operation data ...
+ * such as which values are to be deleted.
+ * <p>
+ * This class can be thought of as a successor to {@link org.apache.tapestry.form.ListEditMap}.
+ * 
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public class DefaultPrimaryKeyConverter implements IPrimaryKeyConverter
+{
+    private final Map _map = new HashMap();
+
+    private final List _keys = new ArrayList();
+
+    // The values added to the Map, in the order they were added.
+    private final List _values = new ArrayList();
+
+    // The last value accessed by getPrimaryKey() or getValue().
+    // Other methods may operate upon this value.
+
+    private Object _lastValue;
+
+    private Set _deletedValues;
+
+    public final void add(Object key, Object value)
+    {
+        Defense.notNull(key, "key");
+        Defense.notNull(value, "value");
+
+        if (_map.containsKey(key))
+            throw new ApplicationRuntimeException(UtilMessages.keyAlreadyExists(key));
+
+        _map.put(key, value);
+
+        _keys.add(key);
+        _values.add(value);
+
+        _lastValue = value;
+    }
+
+    /**
+     * Returns a unmodifiable list of values stored into the converter, in the order in which they
+     * were stored.
+     * 
+     * @return an unmodifiable List
+     * @see #add(Object, Object)
+     */
+    public final List getAllValues()
+    {
+        return Collections.unmodifiableList(_values);
+    }
+
+    /**
+     * Returns a list of all values stored into the converter, with deleted values removed.
+     */
+
+    public final List getValues()
+    {
+        if (isDeletedValuesEmpty())
+            return getAllValues();
+
+        List result = new ArrayList(_values);
+
+        result.removeAll(_deletedValues);
+
+        return result;
+    }
+
+    /**
+     * Returns true if the deleted values set is empty (or null).l
+     */
+    private boolean isDeletedValuesEmpty()
+    {
+        return _deletedValues == null || _deletedValues.isEmpty();
+    }
+
+    /**
+     * Checks to see if the {@link #getLastValue() last value} is, or is not, in the set of deleted
+     * values.
+     */
+    public final boolean isDeleted()
+    {
+        return checkValueSetForLastValue(_deletedValues);
+    }
+
+    /**
+     * Checks the set to see if it contains the {@link #getLastValue() last value}.
+     * 
+     * @param valueSet
+     *            the set to check, which may be null
+     * @return true if the last value is in the set (if non-null)
+     */
+    protected final boolean checkValueSetForLastValue(Set valueSet)
+    {
+        return valueSet != null && valueSet.contains(_lastValue);
+    }
+
+    /**
+     * Adds or removes the {@link #getLastValue() last value} from the
+     * {@link #getDeletedValues() deleted values set}.
+     * 
+     * @param deleted
+     */
+    public final void setDeleted(boolean deleted)
+    {
+        _deletedValues = updateSetForLastValue(_deletedValues, deleted);
+    }
+
+    /**
+     * Updates a value set to add or remove the {@link #getLastValue() last value} to the set. The
+     * logic here will create and return a new Set instance if necessary (that is, if inSet is true
+     * and set is null). The point is to defer the creation of the set until its actually needed.
+     * 
+     * @param set
+     *            the set to update, which may be null
+     * @param inSet
+     *            if true, the last value will be added to the set (creating the set as necessary);
+     *            if false, the last value will be removed
+     * @return the set passed in, or a new Set instance
+     */
+    protected Set updateSetForLastValue(Set set, boolean inSet)
+    {
+        if (inSet)
+        {
+            if (set == null)
+                set = new HashSet();
+
+            set.add(_lastValue);
+
+            return set;
+        }
+
+        if (set != null)
+            set.remove(_lastValue);
+
+        return set;
+    }
+
+    /**
+     * Returns the last active value; this is the value passed to {@link #getPrimaryKey(Object)} or
+     * the value for the key passed to {@link #getValue(Object)}.
+     * <p>
+     * Maintaining <em>value sets</em> involves adding or removing the active value from a set.
+     * 
+     * @return the last active object
+     */
+    public final Object getLastValue()
+    {
+        return _lastValue;
+    }
+
+    /**
+     * Returns an unmodifiable set of all values marked as deleted.
+     */
+
+    public final Set getDeletedValues()
+    {
+        return _deletedValues == null ? Collections.emptySet() : Collections
+                .unmodifiableSet(_deletedValues);
+    }
+
+    /**
+     * Iterates over the keys and values, removing any values (and corresponding keys) that that are
+     * in the deleted set. After invoking this, {@link #getAllValues()} will be the same as
+     * {@link #getValues()}.
+     */
+    public final void removeDeletedValues()
+    {
+        _lastValue = null;
+
+        if (isDeletedValuesEmpty())
+            return;
+
+        int count = _keys.size();
+
+        for (int i = count - 1; i >= 0; i--)
+        {
+            if (_deletedValues.contains(_values.get(i)))
+            {
+                _values.remove(i);
+                Object key = _keys.remove(i);
+
+                _map.remove(key);
+            }
+        }
+    }
+
+    /**
+     * Gets the primary key of an object previously stored in this converter.
+     * 
+     * @param value
+     *            an object previously stored in the converter
+     * @return the corresponding key used to store the object
+     * @throws ApplicationRuntimeException
+     *             if the value was not previously stored
+     * @see #add(Object, Object)
+     */
+    public final Object getPrimaryKey(Object value)
+    {
+        int index = _values.indexOf(value);
+
+        if (index < 0)
+            throw new ApplicationRuntimeException(UtilMessages.valueNotFound(value), value, null,
+                    null);
+
+        _lastValue = value;
+
+        return _keys.get(index);
+    }
+
+    /**
+     * Given a primary key, locates the corresponding object. May invoke
+     * {@link #provideMissingValue(Object)} if no such key has been stored into the converter.
+     * 
+     * @return the object if the key is found, or null otherwise.
+     * @see #add(Object, Object)
+     */
+    public final Object getValue(Object primaryKey)
+    {
+        Object result = _map.get(primaryKey);
+
+        if (result == null)
+            result = provideMissingValue(primaryKey);
+
+        _lastValue = result;
+
+        return result;
+    }
+
+    /**
+     * Invoked by {@link #getValue(Object)} when the key is not found in the converter's map.
+     * Subclasses may override this method to either obtain the corresponding object from secondary
+     * storage, to throw an exception, or to provide a new object instance. This implementation
+     * returns null.
+     * 
+     * @param key
+     *            the key for which an object was requested
+     * @return the object for the key, or null if no object may can be provided
+     */
+    protected Object provideMissingValue(Object key)
+    {
+        return null;
+    }
+
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilMessages.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilMessages.java?rev=295118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilMessages.java (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilMessages.java Wed Oct  5 14:07:19 2005
@@ -0,0 +1,36 @@
+// Copyright 2005 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.tapestry.util;
+
+import org.apache.hivemind.impl.MessageFormatter;
+
+/**
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+class UtilMessages
+{
+    private static final MessageFormatter _formatter = new MessageFormatter(UtilMessages.class);
+
+    static String valueNotFound(Object value)
+    {
+        return _formatter.format("value-not-found", value);
+    }
+
+    static String keyAlreadyExists(Object key)
+    {
+        return _formatter.format("key-already-exists", key);
+    }
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilStrings.properties
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilStrings.properties?rev=295118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilStrings.properties (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/util/UtilStrings.properties Wed Oct  5 14:07:19 2005
@@ -0,0 +1,16 @@
+# Copyright 2005 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.
+
+value-not-found=Value {0} not found.
+key-already-exists=Key ''{0}'' already exists in this primary key converter.
\ No newline at end of file

Added: jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/util/DefaultPrimaryKeyConverterTest.java
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/util/DefaultPrimaryKeyConverterTest.java?rev=295118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/util/DefaultPrimaryKeyConverterTest.java (added)
+++ jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/util/DefaultPrimaryKeyConverterTest.java Wed Oct  5 14:07:19 2005
@@ -0,0 +1,236 @@
+// Copyright 2005 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.tapestry.util;
+
+import java.util.List;
+
+import org.apache.hivemind.ApplicationRuntimeException;
+import org.apache.hivemind.test.HiveMindTestCase;
+
+/**
+ * Tests for {@link org.apache.tapestry.util.DefaultPrimaryKeyConverter}.
+ * 
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public class DefaultPrimaryKeyConverterTest extends HiveMindTestCase
+{
+    /**
+     * Test the starting values of a number of properties.
+     */
+    public void testInitialValues()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        assertTrue(cv.getAllValues().isEmpty());
+        assertTrue(cv.getValues().isEmpty());
+        assertTrue(cv.getDeletedValues().isEmpty());
+        assertNull(cv.getLastValue());
+    }
+
+    public void testAdd()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+
+        assertEquals("flintstone", cv.getLastValue());
+
+        cv.add("barney", "rubble");
+
+        assertEquals("rubble", cv.getLastValue());
+
+        List l = cv.getValues();
+
+        assertEquals("flintstone", l.get(0));
+        assertEquals("rubble", l.get(1));
+    }
+
+    public void testAddDuplicate()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        try
+        {
+            cv.add("fred", "macmurray");
+            unreachable();
+        }
+        catch (ApplicationRuntimeException ex)
+        {
+            assertEquals("Key 'fred' already exists in this primary key converter.", ex
+                    .getMessage());
+        }
+    }
+
+    public void testDelete()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        assertEquals("flintstone", cv.getValue("fred"));
+
+        assertEquals(false, cv.isDeleted());
+
+        assertTrue(cv.getDeletedValues().isEmpty());
+
+        assertEquals("flintstone", cv.getLastValue());
+
+        cv.setDeleted(true);
+
+        assertEquals(true, cv.isDeleted());
+
+        assertTrue(cv.getDeletedValues().contains("flintstone"));
+        assertFalse(cv.getValues().contains("flintstone"));
+        assertTrue(cv.getAllValues().contains("flintstone"));
+
+        cv.setDeleted(false);
+
+        assertFalse(cv.isDeleted());
+
+        assertFalse(cv.getDeletedValues().contains("flintstone"));
+        assertTrue(cv.getValues().contains("flintstone"));
+    }
+
+    public void testGetPrimaryKey()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        assertEquals("fred", cv.getPrimaryKey("flintstone"));
+        assertEquals("flintstone", cv.getLastValue());
+
+        assertEquals("barney", cv.getPrimaryKey("rubble"));
+        assertEquals("rubble", cv.getLastValue());
+    }
+
+    public void testGetPrimaryKeyNotFound()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        try
+        {
+            cv.getPrimaryKey("flintstone");
+            unreachable();
+        }
+        catch (ApplicationRuntimeException ex)
+        {
+            assertEquals("Value flintstone not found.", ex.getMessage());
+        }
+
+    }
+
+    public void testGetValue()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        assertEquals("flintstone", cv.getValue("fred"));
+        assertEquals("flintstone", cv.getLastValue());
+
+        assertEquals("rubble", cv.getValue("barney"));
+        assertEquals("rubble", cv.getLastValue());
+    }
+
+    public void testGetValueNotFound()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        assertEquals(null, cv.getValue("unknown"));
+    }
+
+    public void testGetValueSubclassOverride()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter()
+        {
+            public Object provideMissingValue(Object primaryKey)
+            {
+                assertEquals("fred", primaryKey);
+
+                return "flintstone";
+            }
+        };
+
+        assertEquals("flintstone", cv.getValue("fred"));
+    }
+
+    public void testGetValueSubclassThrowsException()
+    {
+        final RuntimeException re = new ApplicationRuntimeException("flintstone");
+
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter()
+        {
+            public Object provideMissingValue(Object primaryKey)
+            {
+                assertEquals("fred", primaryKey);
+
+                throw re;
+            }
+        };
+
+        try
+        {
+            cv.getValue("fred");
+            unreachable();
+
+        }
+        catch (ApplicationRuntimeException ex)
+        {
+            assertSame(re, ex);
+        }
+    }
+
+    public void testGetDeletedValues()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        assertEquals("fred", cv.getPrimaryKey("flintstone"));
+
+        cv.setDeleted(true);
+
+        assertTrue(cv.getDeletedValues().contains("flintstone"));
+    }
+
+    public void testRemoveDeletedValues()
+    {
+        DefaultPrimaryKeyConverter cv = new DefaultPrimaryKeyConverter();
+
+        cv.add("fred", "flintstone");
+        cv.add("barney", "rubble");
+
+        assertEquals("flintstone", cv.getValue("fred"));
+
+        cv.setDeleted(true);
+
+        cv.removeDeletedValues();
+
+        Object[] values = cv.getValues().toArray();
+        assertEquals(1, values.length);
+        assertEquals("rubble", values[0]);
+
+        assertEquals(cv.getAllValues(), cv.getValues());
+    }
+}

Modified: jakarta/tapestry/trunk/status.xml
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/status.xml?rev=295118&r1=295117&r2=295118&view=diff
==============================================================================
--- jakarta/tapestry/trunk/status.xml (original)
+++ jakarta/tapestry/trunk/status.xml Wed Oct  5 14:07:19 2005
@@ -51,7 +51,8 @@
   </todo>
   <changes>
     <release version="4.0-beta-10" date="unreleased">
-      <action type="fix" dev="HLS" fixes-bug="TAPESTRY-344">Unimplemented abstract method check broken</action>      
+      <action type="fix" dev="HLS" fixes-bug="TAPESTRY-344">Unimplemented abstract method check broken</action>
+      <action type="add" dev="HLS">Add DefaultPrimaryKeyConverter for use with the For component</action>      
     </release>
     <release version="4.0-beta-9" date="Oct 1 2005">
       <action type="fix" dev="DS" fixes-bug="TAPESTRY-663">Document Button component</action>



---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org


Mime
View raw message