You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@click.apache.org by sa...@apache.org on 2010/04/04 10:33:51 UTC
svn commit: r930643 -
/click/trunk/click/framework/src/org/apache/click/control/Select.java
Author: sabob
Date: Sun Apr 4 08:33:50 2010
New Revision: 930643
URL: http://svn.apache.org/viewvc?rev=930643&view=rev
Log:
added DataProvider to Select
Modified:
click/trunk/click/framework/src/org/apache/click/control/Select.java
Modified: click/trunk/click/framework/src/org/apache/click/control/Select.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/control/Select.java?rev=930643&r1=930642&r2=930643&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/control/Select.java (original)
+++ click/trunk/click/framework/src/org/apache/click/control/Select.java Sun Apr 4 08:33:50 2010
@@ -26,6 +26,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import org.apache.click.util.ClickUtils;
+import org.apache.click.util.DataProvider;
import org.apache.click.util.HtmlStringBuffer;
import org.apache.click.util.PropertyUtils;
@@ -51,6 +53,7 @@ import org.apache.click.util.PropertyUti
*
* <h3>Select Examples</h3>
*
+ * <a name="single-select-example"></a>
* <h4>Single Item Select</h4>
* A single item Select, will only allow users to select one item from the list.
* By default the Select {@link #multiple} item property is false.
@@ -61,25 +64,25 @@ import org.apache.click.util.PropertyUti
* An example of a single item Select is provided below along with the
* rendered HTML.
*
- * <pre class="codeJava">
- * <span class="kw">public class</span> GenderPage <span class="kw">extends</span> Page {
+ * <pre class="prettyprint">
+ * public class GenderPage extends Page {
*
- * <span class="kw">public</span> Form form = <span class="kw">new</span> Form();
+ * public Form form = new Form();
*
- * <span class="kw">private</span> Select genderSelect = <span class="kw">new</span> Select(<span class="st">"Gender"</span>);
+ * private Select genderSelect = new Select("Gender");
*
- * <span class="kw">public</span> GenderPage() {
- * genderSelect.setRequired(<span class="kw">true</span>);
- * genderSelect.add(<span class="kw">new</span> Option(<span class="st">"U"</span>, <span class="st">""</span>);
- * genderSelect.add(<span class="kw">new</span> Option(<span class="st">"M"</span>, <span class="st">"Male"</span>));
- * genderSelect.add(<span class="kw">new</span> Option(<span class="st">"F"</span>, <span class="st">"Female"</span>));
+ * public GenderPage() {
+ * genderSelect.setRequired(true);
+ * genderSelect.add(new Option("U", "");
+ * genderSelect.add(new Option("M", "Male"));
+ * genderSelect.add(new Option("F", "Female"));
* form.add(genderSelect);
*
- * form.add(<span class="kw">new</span> Submit(<span class="st">"ok"</span>, <span class="st">" OK "</span>));
+ * form.add(new Submit("ok", " OK "));
* }
*
- * <span class="kw">public void</span> onPost() {
- * <span class="kw">if</span> (form.isValid()) {
+ * public void onPost() {
+ * if (form.isValid()) {
* String gender = genderSelect.getValue();
* ..
* }
@@ -104,6 +107,7 @@ import org.apache.click.util.PropertyUti
* example the "U" option will not be a valid selection, as it is the first
* item in the option list.
*
+ * <a name="multiple-select-example"></a>
* <h4>Multiple Item Select</h4>
* A multiple item Select, will allow users to select multiple items from the list.
* By default the Select {@link #multiple} item property is false, and must be
@@ -116,31 +120,31 @@ import org.apache.click.util.PropertyUti
* An example of a single item Select is provided below along with the
* rendered HTML.
*
- * <pre class="codeJava">
- * <span class="kw">public class</span> LocationPage <span class="kw">extends</span> Page {
+ * <pre class="prettyprint">
+ * public class LocationPage extends Page {
*
- * <span class="kw">public</span> Form form = <span class="kw">new</span> Form();
+ * public Form form = new Form();
*
- * <span class="kw">private</span> Select locationSelect = <span class="kw">new</span> Select(<span class="st">"location"</span>);
+ * private Select locationSelect = new Select("location");
*
- * <span class="kw">public</span> LocationPage() {
- * locationSelect.setMutliple(<span class="kw">true</span>);
- * locationSelect.setRequired(<span class="kw">true</span>);
+ * public LocationPage() {
+ * locationSelect.setMutliple(true);
+ * locationSelect.setRequired(true);
* locationSelect.setSize(7);
- * locationSelect.add(<span class="st">"QLD"</span>);
- * locationSelect.add(<span class="st">"NSW"</span>);
- * locationSelect.add(<span class="st">"NT"</span>);
- * locationSelect.add(<span class="st">"SA"</span>);
- * locationSelect.add(<span class="st">"TAS"</span>);
- * locationSelect.add(<span class="st">"VIC"</span>);
- * locationSelect.add(<span class="st">"WA"</span>);
+ * locationSelect.add("QLD");
+ * locationSelect.add("NSW");
+ * locationSelect.add("NT");
+ * locationSelect.add("SA");
+ * locationSelect.add("TAS");
+ * locationSelect.add("VIC");
+ * locationSelect.add("WA");
* form.add(locationSelect);
*
- * form.add(<span class="kw">new</span> Submit(<span class="st">"ok"</span>, <span class="st">" OK "</span>));
+ * form.add(new Submit("ok", " OK "));
* }
*
- * <span class="kw">public void</span> onPost() {
- * <span class="kw">if</span> (form.isValid()) {
+ * public void onPost() {
+ * if (form.isValid()) {
* String location = locationSelect.getValue();
* ..
* }
@@ -163,6 +167,7 @@ import org.apache.click.util.PropertyUti
* Note in this example the {@link #add(String)} method is used to add an Option
* item to the Select.
*
+ * <a name="required-behaviour"></a>
* <h3>Required Behaviour</h3>
*
* When a Select control's required property is set to true, then the user has
@@ -190,12 +195,13 @@ import org.apache.click.util.PropertyUti
* }
*
* ..
- * }
- * </pre>
+ * } </pre>
*
- * Remember always populate the Select option list before it is processed. Do
- * not populate the option list in a Page's onRender() methods.
+ * Unless you use a <a href="#dataprovider">DataProvider</a>, remember to always
+ * populate the Select option list before it is processed. Do not populate the
+ * option list in a Page's onRender() methods.
*
+ * <a name="readonly-behaviour"></a>
* <h3>Readonly Behaviour</h3>
*
* Note the <select> HTML element does not support the "readonly" attribute.
@@ -204,15 +210,62 @@ import org.apache.click.util.PropertyUti
* readonly field, and will render a hidden field of the same name so that its
* value will be submitted with the form.
*
+ * <a name="dataprovider"></a>
+ * <h3>DataProvider</h3>
+ * A common issue new Click users face is which page event (onInit or onRender)
+ * to populate the Select {@link #getOptionList() optionList} in. To alleviate
+ * this problem you can set a
+ * {@link #setDataProvider(org.apache.click.util.DataProvider) dataProvider}
+ * which allows the Select to fetch data when needed. This is
+ * particularly useful if retrieveing Select data is expensive e.g. loading
+ * from a database.
* <p/>
- * See also the W3C HTML reference:
- * <a class="external" target="_blank" title="W3C HTML 4.01 Specification"
- * href="http://www.w3.org/TR/html401/interact/forms.html#h-17.6">SELECT</a>
+ * Below is a simple example:
+ *
+ * <pre class="prettyprint">
+ * public class GenderPage extends Page {
+ *
+ * public Form form = new Form();
+ *
+ * private Select genderSelect = new Select("Gender");
+ *
+ * public GenderPage() {
+ *
+ * // Set the Select default "non-selection" option
+ * genderSelect.setDefaultOption(new Option("U", "");
+ *
+ * // Set a DataProvider which "getData" method will be called to populate the
+ * // optionList. The "getData" method is only called when the optionList
+ * // data is needed
+ * genderSelect.setDataProvider(new DataProvider() {
+ * public List getData() {
+ * List options = new ArrayList();
+ * options.add(new Option("M", "Male"));
+ * options.add(new Option("F", "Female"));
+ * return options;
+ * }
+ * });
+ *
+ * form.add(genderSelect);
+ *
+ * form.add(new Submit("ok", " OK "));
+ * }
*
- * <h3>Specify a default value</h3>
+ * public void onPost() {
+ * if (form.isValid()) {
+ * String gender = genderSelect.getValue();
+ * ..
+ * }
+ * }
+ * } </pre>
+ *
+ * <a name="default-value"></a>
+ * <h3>Specify the default selected value</h3>
+ *
+ * If you need to set the selected value to something other than the first
+ * option, set the Select {@link #setValue(java.lang.String) value} to the
+ * option value you want to select:
*
- * It is often necessary to set a default selected value. This is best done in
- * the {@link org.apache.click.Page#onRender()} method:
* <pre class="prettyprint">
* public MyPage extends Page {
* private Select mySelect;
@@ -221,16 +274,15 @@ import org.apache.click.util.PropertyUti
* mySelect = new Select("mySelect");
* mySelect.add("YES");
* mySelect.add("NO");
- * }
*
- * public void onRender() {
- * // Only specify a default value if the current value is null
- * if (mySelect.getValue() == null) {
- * mySelect.setValue("YES");
- * }
+ * // If you want NO to be selected by default, set the value to "NO"
+ * mySelect.setValue("NO");
* }
- * }
- * </pre>
+ * } </pre>
+ *
+ * See also the W3C HTML reference:
+ * <a class="external" target="_blank" title="W3C HTML 4.01 Specification"
+ * href="http://www.w3.org/TR/html401/interact/forms.html#h-17.6">SELECT</a>
*
* @see Option
* @see OptionGroup
@@ -275,6 +327,19 @@ public class Select extends Field {
*/
protected List selectedValues;
+ /** The select data provider. */
+ @SuppressWarnings("unchecked")
+ protected DataProvider dataProvider;
+
+ /**
+ * The default option will be the first option added to the Select.
+ * This property is often used when populating the Select from a
+ * {@link #setDataProvider(org.apache.click.util.DataProvider)}, where
+ * the DataProvider does not return a sensible default option e.g. an
+ * empty ("") option.
+ */
+ protected Option defaultOption;
+
// Constructors -----------------------------------------------------------
/**
@@ -392,6 +457,42 @@ public class Select extends Field {
}
/**
+ * Add the given Option/OptionGroup/String/Number/Boolean to the Select.
+ *
+ * @param option one of either Option/OptionGroup/String/Number/Boolean
+ * to add
+ * @throws IllegalArgumentException if option is null, or the option
+ * is an unsupported class
+ */
+ public void add(Object option) {
+ if (option instanceof Option) {
+ getOptionList().add(option);
+
+ } else if (option instanceof OptionGroup) {
+ getOptionList().add(option);
+
+ } else if (option instanceof String) {
+ getOptionList().add(new Option(option.toString()));
+
+ } else if (option instanceof Number) {
+ getOptionList().add(new Option(option.toString()));
+
+ } else if (option instanceof Boolean) {
+ getOptionList().add(new Option(option.toString()));
+
+ } else {
+ String message = "Unsupported options class "
+ + option.getClass().getName() + ". Please use method "
+ + "Select.addAll(Collection, String, String) instead.";
+ throw new IllegalArgumentException(message);
+ }
+
+ if (getOptionList().size() == 1) {
+ setInitialValue();
+ }
+ }
+
+ /**
* Add the given Option/OptionGroup/String/Number/Boolean collection to the
* Select.
*
@@ -400,38 +501,16 @@ public class Select extends Field {
* @throws IllegalArgumentException if options is null, or the collection
* contains an unsupported class
*/
- public void addAll(Collection options) {
+ public void addAll(Collection<? extends Object> options) {
if (options == null) {
String msg = "options parameter cannot be null";
throw new IllegalArgumentException(msg);
}
if (!options.isEmpty()) {
- for (Iterator i = options.iterator(); i.hasNext();) {
- Object option = i.next();
- if (option instanceof Option) {
- getOptionList().add(option);
-
- } else if (option instanceof OptionGroup) {
- getOptionList().add(option);
-
- } else if (option instanceof String) {
- getOptionList().add(new Option(option.toString()));
-
- } else if (option instanceof Number) {
- getOptionList().add(new Option(option.toString()));
-
- } else if (option instanceof Boolean) {
- getOptionList().add(new Option(option.toString()));
-
- } else {
- String message = "Unsupported options class "
- + option.getClass().getName() + ". Please use method "
- + "Select.addAll(Collection, String, String) instead.";
- throw new IllegalArgumentException(message);
- }
+ for (Object option : options) {
+ add(option);
}
- setInitialValue();
}
}
@@ -483,45 +562,67 @@ public class Select extends Field {
/**
* Add the given collection of objects to the Select, creating new Option
- * instances based on the object properties specified by value and label.
+ * instances based on the object properties specified by optionValueProperty
+ * and optionLabelProperty. If the optionLabelProperty is null, the
+ * optionValueProperty will be used as both the value and label of the
+ * options.
+ * <p/>
+ * The collection objects can either be POJOs (plain old java objects) or
+ * {@link java.util.Map} instances.
+ * <p/>
+ * Example usage:
*
* <pre class="prettyprint">
* Select select = new Select("type", "Type:");
* select.addAll(getCustomerService().getCustomerTypes(), "id", "name");
* form.add(select); </pre>
*
+ * This method will iterate over all customerTypes and for each customerType
+ * create a new Option, setting the option value to the customerType
+ * <tt>"id"</tt>, and the option label to the customerType <tt>"name"</tt>.
+ *
* @param objects the collection of objects to render as options
- * @param value the name of the object property to render as the Option value
- * @param label the name of the object property to render as the Option label
- * @throws IllegalArgumentException if options, value or label parameter is null
+ * @param optionValueProperty the name of the object property to render as
+ * the Option value
+ * @param optionLabelProperty the name of the object property to render as
+ * the Option label
+ * @throws IllegalArgumentException if objects or valueProperty parameter is
+ * null
*/
- public void addAll(Collection objects, String value, String label) {
+ public void addAll(Collection objects, String optionValueProperty,
+ String optionLabelProperty) {
+
if (objects == null) {
String msg = "objects parameter cannot be null";
throw new IllegalArgumentException(msg);
}
- if (value == null) {
- String msg = "value parameter cannot be null";
- throw new IllegalArgumentException(msg);
- }
- if (label == null) {
- String msg = "label parameter cannot be null";
+ if (optionValueProperty == null) {
+ String msg = "optionValueProperty parameter cannot be null";
throw new IllegalArgumentException(msg);
}
if (objects.isEmpty()) {
return;
}
-
+
Map methodCache = new HashMap();
for (Iterator i = objects.iterator(); i.hasNext();) {
Object object = i.next();
try {
- Object valueResult = PropertyUtils.getValue(object, value, methodCache);
+ Object valueResult = PropertyUtils.getValue(object,
+ optionValueProperty, methodCache);
+
+ // Default labelResult to valueResult
+ Object labelResult = valueResult;
- Object labelResult = PropertyUtils.getValue(object, label, methodCache);
+ // If optionLabelProperty is specified, lookup the labelResult
+ // from the object
+ if (optionLabelProperty != null) {
+ labelResult = PropertyUtils.getValue(object,
+ optionLabelProperty, methodCache);
+ }
Option option = null;
@@ -542,6 +643,51 @@ public class Select extends Field {
}
/**
+ * Return the select row list DataProvider.
+ *
+ * @return the select row list DataProvider
+ */
+ @SuppressWarnings("unchecked")
+ public DataProvider getDataProvider() {
+ return dataProvider;
+ }
+
+ /**
+ * Set the select row list DataProvider. The dataProvider can return any
+ * mixture of Option/OptionGroup/String/Number/Boolean values.
+ * <p/>
+ * Example usage:
+ *
+ * <pre class="prettyprint">
+ * Select select = new Select("name", "Name");
+ *
+ * // Set the Select default "non-selection" option
+ * select.setDefaultOption(new Option("U", ""));
+ *
+ * select.setDataProvider(new DataProvider() {
+ * public List getData() {
+ * List options = new ArrayList();
+ * options.add(new Option("M", "Male"));
+ * options.add(new Option("F", "Female"));
+ * return options;
+ * }
+ * }); </pre>
+ *
+ * @param dataProvider the select row list DataProvider
+ */
+ @SuppressWarnings("unchecked")
+ public void setDataProvider(DataProvider dataProvider) {
+ this.dataProvider = dataProvider;
+ if (dataProvider != null) {
+ if (optionList != null) {
+ ClickUtils.getLogService().warn("please note that setting a"
+ + " dataProvider will nullify the optionList");
+ }
+ setOptionList(null);
+ }
+ }
+
+ /**
* Return the number of Select display rows.
*
* @return the number of Select display rows
@@ -625,13 +771,86 @@ public class Select extends Field {
}
/**
+ * Set the Select default option. The default option will be the first option
+ * added to the Select {@link #getOptionList() optionList}.
+ * <p/>
+ * <b>Please note</b>: this property is used in conjunction with the Select
+ * {@link #setDataProvider(org.apache.click.util.DataProvider) dataProvider},
+ * where the DataProvider does not return a sensible default, non-selecting
+ * option. For example if the DataProvider returns a list of Authors from the
+ * database, the list won't include a default empty ("") option to choose
+ * from. By setting the defaultOption property, the Select will add this
+ * Option as the first option of the Select {@link #getOptionList() optionList}.
+ * <p/>
+ * In addition, if the Select is {@link #setRequired(boolean) required},
+ * the defaultOption is used to check whether the Select is valid or not.
+ * In other words, if the user's selected value equals the defaultOption value,
+ * the Select won't be valid since no selection was made by the user.
+ * <p/>
+ * Example usage:
+ * <pre class="prettyprint">
+ * public void onInit() {
+ * authorSelect.setDefaultOption(Option.EMPTY_OPTION);
+ *
+ * authorSelect.setDataProvider(new DataProvider() {
+ * public List<Author> getData() {
+ * return getAuthorDao().getAuthors();
+ * }
+ * });
+ * form.add(authorSelect);
+ * } </pre>
+ *
+ * @param option the Select default option
+ */
+ public void setDefaultOption(Option option) {
+ this.defaultOption = option;
+ }
+
+ /**
+ * Return the Select default option or null if no default option is set
+ *
+ * @see #setDefaultOption(org.apache.click.control.Option)
+ *
+ * @return the Select default option or null if no default option is set
+ */
+ public Option getDefaultOption() {
+ return defaultOption;
+ }
+
+ /**
* Return the Option list.
*
* @return the Option list
*/
public List getOptionList() {
if (optionList == null) {
+
optionList = new ArrayList();
+
+ Option defaultOption = getDefaultOption();
+ if (defaultOption != null) {
+ optionList.add(defaultOption);
+ }
+
+ DataProvider dp = getDataProvider();
+
+ if (dp != null) {
+ Iterable iterableData = dp.getData();
+
+ // Create and populate the optionList from the Iterable data
+ if (iterableData instanceof Collection) {
+ // Popuplate optionList from options
+ addAll((Collection) iterableData);
+
+ } else {
+ if (iterableData != null) {
+ // Popuplate optionList from options
+ for (Object option : iterableData) {
+ add(option);
+ }
+ }
+ }
+ }
}
return optionList;
}
@@ -818,20 +1037,33 @@ public class Select extends Field {
setErrorMessage("select-error");
} else {
- if (getOptionList().isEmpty()) {
- String msg = "Mandatory Select field " + getName()
- + " has no options to validate the request against";
- throw new RuntimeException(msg);
- }
+ String defaultValue = getDefaultOptionValue();
+
+ // if no defaultValue is present, lookup value from OptionList
+ if (defaultValue == null) {
+ if (getOptionList().isEmpty()) {
+ String msg =
+ "Mandatory Select field " + getName()
+ + " has no options to validate the request"
+ + " against. Solutions are to either set the"
+ + " Select defaultOption(), use a DataProvider"
+ + " or set the optionList in the Page onInit()"
+ + " page event";
+ throw new RuntimeException(msg);
+ }
- String firstValue = "";
- Object firstEntry = getOptionList().get(0);
- if (firstEntry instanceof Option) {
- Option option = (Option) firstEntry;
- firstValue = option.getValue();
+ Object firstEntry = getOptionList().get(0);
+ if (firstEntry instanceof Option) {
+ Option option = (Option) firstEntry;
+ defaultValue = option.getValue();
+ }
+ }
+ if (defaultValue == null) {
+ defaultValue = "";
}
- if (firstValue.equals(getValue())) {
+
+ if (defaultValue.equals(getValue())) {
setErrorMessage("select-error");
}
}
@@ -842,6 +1074,23 @@ public class Select extends Field {
// Protected Methods ------------------------------------------------------
/**
+ * Return the Select {@link #getDefaultOption() defaultOption} value, or
+ * null if no defaultOption is set.
+ *
+ * @see #getDefaultOption()
+ * @see #setDefaultOption(org.apache.click.control.Option)
+ *
+ * @return the Select defaultOption value, or null if no defaultOption is set
+ */
+ protected String getDefaultOptionValue() {
+ Option defaultOption = getDefaultOption();
+ if (defaultOption != null) {
+ return defaultOption.getValue();
+ }
+ return null;
+ }
+
+ /**
* Set the initial select option value.
*/
protected void setInitialValue() {