mindbridge 2005/08/02 14:39:56
Modified: framework/src/java/org/apache/tapestry/components
ForBean.java For.jwc
Log:
A more efficient version of the For component
Revision Changes Path
1.2 +235 -172
jakarta-tapestry/framework/src/java/org/apache/tapestry/components/ForBean.java
Index: ForBean.java
===================================================================
RCS file:
/home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/components/ForBean.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ForBean.java 4 Jul 2005 21:31:38 -0000 1.1
+++ ForBean.java 2 Aug 2005 21:39:56 -0000 1.2
@@ -16,6 +16,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -39,6 +40,9 @@
public abstract class ForBean extends AbstractFormComponent {
private static final char DESC_VALUE = 'V';
private static final char DESC_PRIMARY_KEY = 'P';
+
+ private static final String PARAMETER_SOURCE = "source";
+ private static final String PARAMETER_FULL_SOURCE = "fullSource";
// parameters
public abstract Object getSource();
@@ -53,6 +57,8 @@
// properties
public abstract Map getPrimaryKeyMap();
public abstract void setPrimaryKeyMap(Map primaryKeys);
+ public abstract Map getSourceIteratorMap();
+ public abstract void setSourceIteratorMap(Map sourceIteratorMap);
// injects
public abstract DataSqueezer getDataSqueezer();
@@ -63,113 +69,7 @@
private Object _value;
private int _index;
private boolean _rendering;
-
- /**
- * Gets the source binding and returns an [EMAIL PROTECTED] Iterator}
- * representing
- * the values identified by the source. Returns an empty [EMAIL
PROTECTED] Iterator}
- * if the binding, or the binding value, is null.
- *
- * <p>Invokes [EMAIL PROTECTED] Tapestry#coerceToIterator(Object)} to
perform
- * the actual conversion.
- *
- **/
-
- protected Iterator getSourceData()
- {
- Object source = getSource();
- if (source == null)
- return null;
-
- Iterator iteratorSource = (Iterator)
getValueConverter().coerceValue(source, Iterator.class);
-
- return iteratorSource;
- }
-
- protected Iterator storeSourceData(IForm form, String name)
- {
- Iterator iteratorSource = getSourceData();
- if (iteratorSource == null)
- return null;
-
- // extract primary keys from data
- StringBuffer pkDesc = new StringBuffer();
- List data = new ArrayList();
- List pks = new ArrayList();
- while (iteratorSource.hasNext()) {
- Object value = iteratorSource.next();
- data.add(value);
-
- Object pk = getPrimaryKeyFromValue(value);
- if (pk == null) {
- pk = value;
- pkDesc.append(DESC_VALUE);
- }
- else
- pkDesc.append(DESC_PRIMARY_KEY);
- pks.add(pk);
- }
-
- // store primary keys
- form.addHiddenValue(name, pkDesc.toString());
- for (Iterator it = pks.iterator(); it.hasNext();) {
- Object pk = it.next();
- try {
- String stringRep =
getDataSqueezer().squeeze(pk);
- form.addHiddenValue(name, stringRep);
- } catch (IOException ex) {
- throw new ApplicationRuntimeException(
- Tapestry.format("For.unable-to-convert-value", pk),
- this,
- null,
- ex);
- }
- }
- return data.iterator();
- }
-
- 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 = new ArrayList(submittedPrimaryKeys.length-1);
- for (int i = 1; i < submittedPrimaryKeys.length; i++) {
- String stringRep = submittedPrimaryKeys[i];
- try {
- Object value =
getDataSqueezer().unsqueeze(stringRep);
- data.add(value);
- if (i <= pkDesc.length() && pkDesc.charAt(i-1)
== DESC_PRIMARY_KEY)
- pks.add(value);
- } catch (IOException ex) {
- throw new ApplicationRuntimeException(
- Tapestry.format("For.unable-to-convert-string",
stringRep),
- this,
- null,
- ex);
- }
- }
-
- // update the binding with the list of primary keys
- IBinding primaryKeysBinding = getBinding("primaryKeys");
- if (primaryKeysBinding != null)
- primaryKeysBinding.setObject(pks);
-
- // 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);
- }
- }
-
- return data.iterator();
- }
-
/**
* Gets the source binding and iterates through
* its values. For each, it updates the value binding and render's its
wrapped elements.
@@ -187,21 +87,8 @@
if (cycleRewinding && form != null && !form.isRewinding())
return;
- boolean bInForm = (form != null && !getVolatile());
-
- String name = "";
- if (form != null)
- name = form.getElementId(this);
-
// Get the data to be iterated upon. Store in form if needed.
- Iterator dataSource;
- if (!bInForm)
- dataSource = getSourceData();
- else if (cycleRewinding)
- dataSource = getStoredData(cycle, name);
- else
- dataSource = storeSourceData(form, name);
-
+ Iterator dataSource = getData(cycle, form);
// Do not iterate if dataSource is null.
// The dataSource was either not convertable to Iterator, or was
empty.
@@ -222,13 +109,7 @@
_value = dataSource.next();
// Update output component parameters
- IBinding indexBinding = getBinding("index");
- if (indexBinding != null)
- indexBinding.setObject(new Integer(_index));
-
- IBinding valueBinding = getBinding("value");
- if (valueBinding != null)
- valueBinding.setObject(_value);
+ updateOutputParameters();
// Render component
if (element != null)
@@ -251,31 +132,160 @@
_value = null;
}
}
-
- private Object restoreValue(IForm form, String name, Object primaryKey)
+
+
+ /**
+ * 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)
+ * @return An iterator with the values to be cycled upon
+ **/
+ private Iterator getData(IRequestCycle cycle, IForm form) {
+ if (form == null || getVolatile())
+ return getSource(PARAMETER_SOURCE);
+
+ String name = form.getElementId(this);
+ if (cycle.isRewinding())
+ return getStoredData(cycle, name);
+ else
+ return storeSourceData(form, name);
+ }
+
+ /**
+ * Returns an [EMAIL PROTECTED] Iterator} containing the values
provided
+ * by the identified source binding.
+ *
+ * @param parameter The name of the source binding
+ * @return an iterator with the bound values. null if the parameter is
not bound
+ * or the conversion cannot be performed
+ **/
+ protected Iterator getSource(String parameter)
+ {
+ IBinding binding = getBinding(parameter);
+ if (binding == null)
+ return null;
+
+ Object data = binding.getObject();
+ return (Iterator) getValueConverter().coerceValue(data,
Iterator.class);
+ }
+
+ /**
+ * 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)
{
- return getValueFromPrimaryKey(primaryKey);
+ String[] submittedPrimaryKeys = cycle.getParameters(name);
+ String pkDesc = submittedPrimaryKeys[0];
+
+ // unsqueeze data
+ List data = new ArrayList(submittedPrimaryKeys.length-1);
+ List pks = new ArrayList(submittedPrimaryKeys.length-1);
+ for (int i = 1; i < submittedPrimaryKeys.length; i++) {
+ String stringRep = submittedPrimaryKeys[i];
+ try {
+ Object value =
getDataSqueezer().unsqueeze(stringRep);
+ data.add(value);
+ if (i <= pkDesc.length() && pkDesc.charAt(i-1)
== DESC_PRIMARY_KEY)
+ pks.add(value);
+ } catch (IOException ex) {
+ throw new ApplicationRuntimeException(
+ Tapestry.format("For.unable-to-convert-string",
stringRep),
+ this,
+ null,
+ ex);
+ }
+ }
+
+ // update the binding with the list of primary keys
+ IBinding primaryKeysBinding = getBinding("primaryKeys");
+ if (primaryKeysBinding != null)
+ primaryKeysBinding.setObject(pks);
+
+ // 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);
+ }
+ }
+
+ return data.iterator();
}
- private void storeValue(IForm form, String name, Object value)
+ /**
+ * 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)
{
- Object convertedValue = getPrimaryKeyFromValue(value);
+ Iterator iteratorSource = getSource(PARAMETER_SOURCE);
+ if (iteratorSource == null)
+ return null;
- try
- {
- String externalValue =
getDataSqueezer().squeeze(convertedValue);
- form.addHiddenValue(name, externalValue);
- }
- catch (IOException ex)
- {
- throw new ApplicationRuntimeException(
- Tapestry.format("For.unable-to-convert-value", value),
- this,
- null,
- ex);
- }
+ // extract primary keys from data
+ StringBuffer pkDesc = new StringBuffer();
+ List data = new ArrayList();
+ List pks = new ArrayList();
+ while (iteratorSource.hasNext()) {
+ Object value = iteratorSource.next();
+ data.add(value);
+
+ Object pk = getPrimaryKeyFromValue(value);
+ if (pk == null) {
+ pk = value;
+ pkDesc.append(DESC_VALUE);
+ }
+ else
+ pkDesc.append(DESC_PRIMARY_KEY);
+ pks.add(pk);
+ }
+
+ // store primary keys
+ form.addHiddenValue(name, pkDesc.toString());
+ for (Iterator it = pks.iterator(); it.hasNext();) {
+ Object pk = it.next();
+ try {
+ String stringRep =
getDataSqueezer().squeeze(pk);
+ form.addHiddenValue(name, stringRep);
+ } catch (IOException ex) {
+ throw new ApplicationRuntimeException(
+ Tapestry.format("For.unable-to-convert-value", pk),
+ this,
+ null,
+ ex);
+ }
+ }
+
+ return data.iterator();
}
+ /**
+ * 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
+ * @return The primary key of the value, or null if such cannot be
extracted.
+ */
private Object getPrimaryKeyFromValue(Object value) {
Object primaryKey = null;
@@ -292,18 +302,20 @@
return primaryKey;
}
+ /**
+ * Returns a value that corresponds to the provided primary key.
+ * Uses either the 'keyExpression' if provided
+ *
+ * @param primaryKey
+ * @return
+ */
private Object getValueFromPrimaryKey(Object primaryKey) {
Object value = null;
- if (value == null && getKeyExpression() != null) {
- String keyExpression = getKeyExpression();
- if (keyExpression != null) {
- Map primaryKeys = getPrimaryKeyMap();
- if (primaryKeys == null)
- primaryKeys =
initializePrimaryKeysFromSource(keyExpression);
- value = primaryKeys.get(primaryKeys);
- }
- }
+ // if keyExpression is defined, try to get the object in that way
+ String keyExpression = getKeyExpression();
+ if (keyExpression != null)
+ value = getValueFromExpressionPrimaryKeys(keyExpression,
primaryKey);
if (value == null) {
IPrimaryKeyConverter converter = getConverter();
@@ -317,28 +329,79 @@
return value;
}
- private Map initializePrimaryKeysFromSource(String keyExpression)
+ private Object getValueFromExpressionPrimaryKeys(String keyExpression,
Object primaryKey)
{
- Map primaryKeys = new HashMap();
-
- Object fullSource = getFullSource();
- if (fullSource == null)
- fullSource = getSource();
- if (fullSource == null)
- return primaryKeys;
+ Object value = null;
- ExpressionEvaluator evaluator = getExpressionEvaluator();
+ Map primaryKeys = getPrimaryKeyMap();
+ if (primaryKeys == null)
+ primaryKeys = new HashMap();
+ else {
+ value = primaryKeys.get(primaryKey);
+ if (value != null)
+ return value;
+ }
+
+ ExpressionEvaluator evaluator = getExpressionEvaluator();
+
+ // Iterate over the elements in 'source' and 'fullSource' until
a primary key match is found
+ value = findPrimaryKeyMatch(primaryKey, PARAMETER_SOURCE);
+ if (value == null)
+ value = findPrimaryKeyMatch(primaryKey,
PARAMETER_FULL_SOURCE);
+
+ return value;
+ }
+
+ private Object findPrimaryKeyMatch(Object primaryKey, String parameter)
+ {
+ ExpressionEvaluator evaluator = getExpressionEvaluator();
+ String keyExpression = getKeyExpression();
+
+ Map primaryKeys = getPrimaryKeyMap();
+ if (primaryKeys == null)
+ primaryKeys = new HashMap();
- Iterator iteratorSource = (Iterator)
getValueConverter().coerceValue(fullSource, Iterator.class);
- while (iteratorSource.hasNext()) {
- Object value = iteratorSource.next();
- Object primaryKey = evaluator.read(value, keyExpression);
- if (primaryKey != null)
- primaryKeys.put(primaryKey, value);
- }
+ Map sourceIteratorMap = getSourceIteratorMap();
+ if (sourceIteratorMap == null)
+ sourceIteratorMap = new HashMap();
- setPrimaryKeyMap(primaryKeys);
- return primaryKeys;
+ Iterator it = (Iterator) sourceIteratorMap.get(parameter);
+ if (it == null) {
+ it = getSource(parameter);
+ if (it == null)
+ it = Collections.EMPTY_LIST.iterator();
+ }
+
+ try {
+ while (it.hasNext()) {
+ Object sourceValue = it.next();
+ Object sourcePrimaryKey = evaluator.read(sourceValue,
keyExpression);
+ if (sourcePrimaryKey != null)
+ primaryKeys.put(sourcePrimaryKey, sourceValue);
+
+ if (primaryKey.equals(sourcePrimaryKey)) {
+ return sourceValue;
+ }
+ }
+
+ return null;
+ }
+ finally {
+ sourceIteratorMap.put(parameter, it);
+ setSourceIteratorMap(sourceIteratorMap);
+ setPrimaryKeyMap(primaryKeys);
+ }
+ }
+
+ 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);
}
/**
1.3 +3 -3
jakarta-tapestry/framework/src/java/org/apache/tapestry/components/For.jwc
Index: For.jwc
===================================================================
RCS file:
/home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/components/For.jwc,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- For.jwc 28 Jul 2005 19:10:40 -0000 1.2
+++ For.jwc 2 Aug 2005 21:39:56 -0000 1.3
@@ -70,9 +70,9 @@
<parameter name="fullSource">
<description>
Only active in a form and in combination with the 'keyExpression'
parameter.
- The objects provided by this parameter will be used to determine the
values
- referred to by the primary keys stored in the form.
- If this parameter is not bound, 'source' is used instead.
+ If an object corresponding to a primary key stored in the form cannot be
+ found in the 'source' parameter, then the objects provided by this
parameter
+ are searched for a match next.
</description>
</parameter>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]