You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by mi...@apache.org on 2005/09/06 20:14:48 UTC

svn commit: r279050 - in /jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components: For.jwc ForBean.java

Author: mindbridge
Date: Tue Sep  6 11:14:42 2005
New Revision: 279050

URL: http://svn.apache.org/viewcvs?rev=279050&view=rev
Log:
Modifying the For component to perform matching of 
string representations by default. The component will
now try to find values in 'source' and 'fullSource'
that have identical string representations to the ones stored
in the hidden fields. Unsqueezing would be used only as a 
last resort.

Modified:
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/For.jwc
    jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/ForBean.java

Modified: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/For.jwc
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/For.jwc?rev=279050&r1=279049&r2=279050&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/For.jwc (original)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/components/For.jwc Tue Sep  6 11:14:42 2005
@@ -97,7 +97,17 @@
     Only active in a form. If provided, the parameter is automatically updated 
     before a rewind with the list of primary keys stored in the form. 
     The parameter is updated right before the iterations begin in a rewind and 
-    could be used to preload the relevant objects in a provided 'converter'.
+    could be used to preload the relevant objects in the provided 'converter'.
+    </description>
+  </parameter>
+  
+  <parameter name="match" default-value="true">
+    <description>
+    Only active in a form. This parameter allows the matching of the squeezed 
+    representation of the values with that of the values in 'source'. It guarantees 
+    that the values iterated upon are physically identical to the ones provided. 
+    The method is often slower than simple unsqueezing, but it eliminates a number 
+    of potential pitfalls. Please disable with caution.
     </description>
   </parameter>
   
@@ -108,6 +118,8 @@
     and cause exceptions as a result. Please use with caution.
     </description>
   </parameter>
+  
+  <property name="repToValueMap" initial-value="new java.util.HashMap()"/>
   
   <inject property="dataSqueezer" object="service:tapestry.data.DataSqueezer"/>
   <inject property="valueConverter" object="service:tapestry.coerce.ValueConverter"/>

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=279050&r1=279049&r2=279050&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 Tue Sep  6 11:14:42 2005
@@ -16,7 +16,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -36,53 +35,54 @@
  * @author mb
  */
 public abstract class ForBean extends AbstractFormComponent {
+	// constants
     private static final char DESC_VALUE = 'V';
 	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
 	public abstract Object getSource();
 	public abstract Object getFullSource();
     public abstract String getElement();
-    public abstract boolean getVolatile();
-    public abstract Object getDefaultValue();
-    public abstract String getPrimaryKey();
-    public abstract IPrimaryKeyConverter getConverter();
     public abstract String getKeyExpression();
+    public abstract IPrimaryKeyConverter getConverter();
+    public abstract Object getDefaultValue();
+    public abstract boolean getMatch();
+    public abstract boolean getVolatile();
 
     // properties
-    public abstract Map getPrimaryKeyMap();
-    public abstract void setPrimaryKeyMap(Map primaryKeys);
-
-    public abstract List getSourcePrimaryKeys();
-    public abstract void setSourcePrimaryKeys(List sourcePrimaryKeys);
-    
-    public abstract List getSavedSourceData();
-    public abstract void setSavedSourceData(List sourceData);
+    public abstract Iterator getSourceIterator();
+    public abstract void setSourceIterator(Iterator sourceIterator);
     
     public abstract Iterator getFullSourceIterator();
     public abstract void setFullSourceIterator(Iterator fullSourceIterator);
     
+    public abstract Map getRepToValueMap();
+    public abstract void setRepToValueMap(Map repToValue);
+    
     // 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.
      *
      **/
-
     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
     {
     	// Clear the cache between rewind and render.
     	// This allows the value of 'source' to be changed by the form listeners.
-    	setSavedSourceData(null);
+    	setSourceIterator(null);
     	
         // form may be null if component is not located in a form
         IForm form = (IForm) cycle.getAttribute(TapestryUtils.FORM_ATTRIBUTE);
@@ -141,6 +141,87 @@
     
     
     /**
+     *  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.
+     * 
+     **/
+    
+    public int getIndex()
+    {
+        if (!_rendering)
+            throw Tapestry.createRenderOnlyPropertyException(this, "index");
+        
+        return _index;
+    }
+
+    public boolean isDisabled()
+    {
+        return false;
+    }
+
+    /**
+     * Updates the index and value output parameters if bound.
+     */
+    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);
+    }
+
+    /**
+     * Updates the primaryKeys parameter if bound.
+     */
+    protected void updatePrimaryKeysParameter(String[] stringReps)
+    {
+    	IBinding primaryKeysBinding = getBinding("primaryKeys");
+    	if (primaryKeysBinding == null)
+    		return;
+
+    	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:
@@ -157,32 +238,14 @@
      **/
     private Iterator getData(IRequestCycle cycle, IForm form) {
         if (form == null || getVolatile())
-        	return getSourceData().iterator();
+        	return getSourceIteratorValue();
         
         String name = form.getElementId(this);
         if (cycle.isRewinding())
         	return getStoredData(cycle, name);
        	return storeSourceData(form, name);
     }
-    
-    /**
-     *  Returns a {@link java.util.List} containing the values provided 
-     *  by the identified source binding.
-     *
-     *  @return a list with the values to iterate upon. 
-     *  null if conversion cannot be performed. 
-     **/
-    protected List getSourceData()
-    {
-    	List sourceData = getSavedSourceData();
-    	if (sourceData == null) {
-    		Object source = getSource();
-    		sourceData = (List) getValueConverter().coerceValue(source, List.class);
-    		setSavedSourceData(sourceData);
-    	}
-    	return sourceData;
-    }
-    
+
     /**
      *  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.
@@ -193,38 +256,19 @@
      **/
     protected Iterator getStoredData(IRequestCycle cycle, String name)
     {
-        String[] submittedPrimaryKeys = cycle.getParameters(name);
-        String pkDesc = submittedPrimaryKeys[0];
-
-        // unsqueeze data
-        List data = new ArrayList(submittedPrimaryKeys.length-1);
-
-        List pks = null;
-    	IBinding primaryKeysBinding = getBinding("primaryKeys");
-    	if (primaryKeysBinding != null)
-    		pks = new ArrayList(submittedPrimaryKeys.length-1);
-        for (int i = 1; i < submittedPrimaryKeys.length; i++) {
-        	String stringRep = submittedPrimaryKeys[i];
-			Object value = getDataSqueezer().unsqueeze(stringRep);
-			data.add(value);
-			if (primaryKeysBinding != null && i <= pkDesc.length() && pkDesc.charAt(i-1) == DESC_PRIMARY_KEY)
-				pks.add(value);
-		}
-
-        // update the binding with the list of primary keys
-    	if (primaryKeysBinding != null)
-    		primaryKeysBinding.setObject(pks);
+        String[] stringReps = cycle.getParameters(name);
         
-    	// convert from primary keys to data
-        for (int i = 0; i < data.size(); i++) {
-        	if (i <= pkDesc.length() && pkDesc.charAt(i) == DESC_PRIMARY_KEY) {
-        		Object pk = data.get(i);
-        		Object value = getValueFromPrimaryKey(pk);
-        		data.set(i, value);
-        	}
+        updatePrimaryKeysParameter(stringReps);
+        
+        int valueCount = stringReps.length;
+        List values = new ArrayList(valueCount);
+        for (int i = 0; i < valueCount; i++) {
+			String rep = stringReps[i];
+			Object value = getValueFromStringRep(rep);
+			values.add(value);
 		}
         
-        return data.iterator();
+        return values.iterator();
     }
     
     /**
@@ -238,104 +282,46 @@
      **/
     protected Iterator storeSourceData(IForm form, String name)
     {
-    	List sourceData = getSourceData();
-		if (sourceData == null)
-			return null;
-		
-    	List sourcePrimaryKeys = evaluateSourcePrimaryKeys();
-		if (sourcePrimaryKeys == null)
-			return null;
+        List values = new ArrayList();
     	
-		// store primary keys
-        form.addHiddenValue(name, sourcePrimaryKeys.get(0).toString());
-        for (int i = 1; i < sourcePrimaryKeys.size(); i++) {
-			Object pk = sourcePrimaryKeys.get(i);
-			String stringRep = getDataSqueezer().squeeze(pk);
-			form.addHiddenValue(name, stringRep);
-		}
-        
-    	return sourceData.iterator();
-    }
-
-    /**
-     * Converts the values in the 'source' parameter to primary keys
-     * and returns a list containing the primary keys or the values themselves
-     * if a primary key cannot be extracted. The first element of the array is a
-     * string that shows whether a particular element is a primary key or a value. 
-     * The method also stores the evaluated primary keys in a map that can be used
-     * to determine the value that a particular primary key represents.  
-     *  
-     * @return an array consisting of the primary keys of the source values or 
-     * the values themselves if a primary key cannot be found. The first element
-     * of the array is a string describing whether a particular element is
-     * a primary key or a value.
-     */
-    private List evaluateSourcePrimaryKeys()
-    {
-    	// check if the result is already cached to avoid evaluating again
-    	List sourcePrimaryKeys = getSourcePrimaryKeys();
-    	if (sourcePrimaryKeys != null)
-    		return sourcePrimaryKeys;
-    	
-    	List sourceData = getSourceData();
-		if (sourceData == null)
-			return null;
-    	
-		// extract primary keys from data
-		StringBuffer pkDesc = new StringBuffer(sourceData.size());
-		sourcePrimaryKeys = new ArrayList(sourceData.size()+1);
-		sourcePrimaryKeys.add(pkDesc);
-		for (Iterator it = sourceData.iterator(); it.hasNext();) {
-			Object value = it.next();
-			
-			Object pk = getPrimaryKeyFromValue(value);
-			if (pk == null) {
-				pkDesc.append(DESC_VALUE);
-				pk = value;
-			}
-			else {
-				pkDesc.append(DESC_PRIMARY_KEY);
-			}
-			sourcePrimaryKeys.add(pk);
-		}
+        Iterator it = getSourceIteratorValue();
+    	while (it.hasNext()) {
+    		Object value = it.next();
+    		values.add(value);
+    		
+    		String rep = getStringRepFromValue(value);
+    		form.addHiddenValue(name, rep);
+    	}
     	
-		setSourcePrimaryKeys(sourcePrimaryKeys);
-		
-		return sourcePrimaryKeys;
+    	return values.iterator();
     }
+
     
     /**
-     * Converts the values in the 'source' parameter to primary keys if possible.
-     * Stores the evaluated primary keys in a map to determine the value 
-     * that a particular primary key represents.  
+     * Returns the string representation of the value.
      *  
-     * @return the map from primary keys to their corresponding objects 
+     * The first letter of the string representation shows whether a value
+     * or a primary key is being described.
+     * 
+     * @param value
+     * @return
      */
-    private Map fillSourcePrimaryKeysMap()
-    {
-    	// check if the result is already cached to avoid evaluating again
-    	Map primaryKeyMap = getPrimaryKeyMap();
-    	if (primaryKeyMap != null)
-    		return primaryKeyMap;
+    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);
     	
-    	List sourceData = getSourceData();
-		if (sourceData == null)
-			return null;
-    	
-		// extract primary keys from data
-		primaryKeyMap = new HashMap();
-		for (Iterator it = sourceData.iterator(); it.hasNext();) {
-			Object value = it.next();
-			Object pk = getPrimaryKeyFromValue(value);
-			if (pk != null)
-				primaryKeyMap.put(pk, value);
-		}
-    	
-		setPrimaryKeyMap(primaryKeyMap);
-		
-		return primaryKeyMap;
+    	return rep;
     }
-    
+
     /**
      * Returns the primary key of the given value. 
      * Uses the 'keyExpression' or the 'converter' (if either is provided).
@@ -343,158 +329,231 @@
      * @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.
      */
-    private Object getPrimaryKeyFromValue(Object value) {
+    protected Object getPrimaryKeyFromValue(Object value) {
     	if (value == null)
     		return null;
     	
-    	Object primaryKey = null;
-    	
-		String keyExpression = getKeyExpression();
-		if (keyExpression != null)
-			primaryKey = getExpressionEvaluator().read(value, keyExpression);
-	
-		if (primaryKey == null) {
-	    	IPrimaryKeyConverter converter = getConverter();
-	    	if (converter != null)
-	    		primaryKey = converter.getPrimaryKey(value);
-		}
+    	Object primaryKey = getKeyExpressionFromValue(value);
+		if (primaryKey == null)
+			primaryKey = getConverterFromValue(value);
 
     	return primaryKey;
     }
     
     /**
-     * Returns a value that corresponds to the provided primary key.
-     * Uses the 'keyExpression' or the 'converter' (if either is provided).
-     * If 'keyExpression' is defined, it extracts the primary keys of all values 
-     * in 'source' until a match is found. If there is no match, it does the same 
-     * with 'fullSource'. If that does not help either, 'converter' is used.
-     * Finally, the 'defaultValue' is returned as a last resort.
+     * Uses the 'keyExpression' parameter to determine the primary key of the given value
      * 
-     * @param primaryKey The primary key that identifies the value 
-     * @return A value with an identical primary key, or null if such is not found.
+     * @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.
      */
-    private Object getValueFromPrimaryKey(Object primaryKey) {
+    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.
+     */
+    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.
+     * 
+     * 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. 
+     * 
+     * @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(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(rep, COMPLETE_REP_SOURCE);
+			if (value != null)
+				return value;
+		}
 
-    	Map primaryKeyMap = fillSourcePrimaryKeysMap();
-    	if (primaryKeyMap != null)
-    		value = primaryKeyMap.get(primaryKey);
-    	
-    	if (value == null) {
-	    	// if fullSource is defined, try to get the object in that way
-			Object fullSource = getFullSource();
-			if (fullSource != null)
-	        	value = findPrimaryKeyMatchInFullSource(primaryKey, fullSource);
+		// 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(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) {
-	    	IPrimaryKeyConverter converter = getConverter();
-	    	if (converter != null)
-	    		value = converter.getValue(primaryKey);
-    	}
-
     	if (value == null)
     		value = getDefaultValue();
-
+    	
     	return value;
     }
     
     /**
-     * Iterates over the fullSource parameter until a value with a matching primary key is found.
-     * The primary keys generated and the fullSource iterator are stored in properties 
-     * to avoid repeated evaluation.
-     * 
-     * @param primaryKey the primary key to be matched
-     * @param fullSource the provided list of objects
-     * @return an object with a matching primary key, or null if such is not found
+     * 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(String rep, RepSource repSource) {
+    	Map repToValueMap = getRepToValueMap();
+    	
+    	Object value = repToValueMap.get(rep);
+    	if (value != null)
+    		return value;
+
+		Iterator it = getSourceIteratorValue();
+		value = findValueWithStringRepInIterator(rep, repSource, it);
+    	if (value != null)
+    		return value;
+		
+		it = getFullSourceIteratorValue();
+		value = findValueWithStringRepInIterator(rep, repSource, it);
+   		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
      */
-    private Object findPrimaryKeyMatchInFullSource(Object primaryKey, Object fullSource)
+    protected Object findValueWithStringRepInIterator(String rep, RepSource repSource, Iterator it) {
+    	Map repToValueMap = getRepToValueMap();
+    	
+		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 the cached 'source' iterator. The value is initialized
+     * with the iterator provided by the 'source' parameter  
+     * 
+     * @return the cached 'source' iterator
+     */
+    protected Iterator getSourceIteratorValue()
     {
-    	Map primaryKeyMap = getPrimaryKeyMap();
-    	if (primaryKeyMap == null)
-    		primaryKeyMap = new HashMap();
+		Iterator it = getSourceIterator();
+		if (it == null) {
+			it = (Iterator) getValueConverter().coerceValue(getSource(), Iterator.class);
+			if (it == null)
+				it = Collections.EMPTY_LIST.iterator();
+			setSourceIterator(it);
+		}
     	
+		return it;
+    }
+    
+    /**
+     * Returns the cached 'fullSource' iterator. The value is initialized
+     * with the iterator provided by the 'fullSource' parameter  
+     * 
+     * @return the cached 'fullSource' iterator
+     */
+    protected Iterator getFullSourceIteratorValue()
+    {
 		Iterator it = getFullSourceIterator();
 		if (it == null) {
-			it = (Iterator) getValueConverter().coerceValue(fullSource, Iterator.class);
+			it = (Iterator) getValueConverter().coerceValue(getFullSource(), Iterator.class);
 			if (it == null)
 				it = Collections.EMPTY_LIST.iterator();
-		}
-		
-		try { 
-			while (it.hasNext()) {
-	    		Object sourceValue = it.next();
-	    		if (sourceValue == null)
-	    			continue;
-	    		
-	        	Object sourcePrimaryKey = getPrimaryKeyFromValue(sourceValue);
-	        	if (sourcePrimaryKey != null)
-	        		primaryKeyMap.put(sourcePrimaryKey, sourceValue);
-	        	
-	        	if (primaryKey.equals(sourcePrimaryKey)) {
-	        		return sourceValue;
-	        	}
-			}
-			
-			return null;
-		}
-		finally {
 			setFullSourceIterator(it);
-			setPrimaryKeyMap(primaryKeyMap);
 		}
+    	
+		return it;
     }
     
     /**
-     * Updates the index and value output parameters if bound.
+     * An interface that provides the string representation of a given value
      */
-    private void updateOutputParameters()
-    {
-    	IBinding indexBinding = getBinding("index");
-    	if (indexBinding != null)
-    		indexBinding.setObject(new Integer(_index));
-
-    	IBinding valueBinding = getBinding("value");
-    	if (valueBinding != null)
-    		valueBinding.setObject(_value);
+    protected interface RepSource {
+    	String getStringRep(Object value);
     }
     
     /**
-     *  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;
+     * 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);
+    	}
     }
-
-    /**
-     *  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;
-    }
-
-    public boolean isDisabled()
-    {
-        return false;
+    /**
+     * 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);
+    	}
     }
-
-	// Do nothing in those methods, but make the JVM happy
-    protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) { }
-    protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) { }
 }



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