001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.markup.html.form;
018:
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.StringTokenizer;
024:
025: import org.apache.wicket.WicketRuntimeException;
026: import org.apache.wicket.markup.ComponentTag;
027: import org.apache.wicket.model.IModel;
028: import org.apache.wicket.model.Model;
029: import org.apache.wicket.util.convert.ConversionException;
030: import org.apache.wicket.util.string.AppendingStringBuffer;
031: import org.apache.wicket.util.string.Strings;
032:
033: /**
034: * A multiple choice list component.
035: *
036: * @author Jonathan Locke
037: * @author Johan Compagner
038: * @author Martijn Dashorst
039: */
040: public class ListMultipleChoice extends AbstractChoice {
041: private static final long serialVersionUID = 1L;
042:
043: /** The default maximum number of rows to display. */
044: private static int defaultMaxRows = 8;
045:
046: /** The maximum number of rows to display. */
047: private int maxRows = defaultMaxRows;
048:
049: /**
050: * Gets the default maximum number of rows to display.
051: *
052: * @return Returns the defaultMaxRows.
053: */
054: protected static int getDefaultMaxRows() {
055: return defaultMaxRows;
056: }
057:
058: /**
059: * Sets the default maximum number of rows to display.
060: *
061: * @param defaultMaxRows
062: * The defaultMaxRows to set.
063: */
064: protected static void setDefaultMaxRows(final int defaultMaxRows) {
065: ListMultipleChoice.defaultMaxRows = defaultMaxRows;
066: }
067:
068: /**
069: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String)
070: */
071: public ListMultipleChoice(final String id) {
072: super (id);
073: }
074:
075: /**
076: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
077: * List)
078: */
079: public ListMultipleChoice(final String id, final List choices) {
080: super (id, choices);
081: }
082:
083: /**
084: * Creates a multiple choice list with a maximum number of visible rows.
085: *
086: * @param id
087: * component id
088: * @param choices
089: * list of choices
090: * @param maxRows
091: * the maximum number of visible rows.
092: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
093: * List)
094: */
095: public ListMultipleChoice(final String id, final List choices,
096: final int maxRows) {
097: super (id, choices);
098: this .maxRows = maxRows;
099: }
100:
101: /**
102: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
103: * List,IChoiceRenderer)
104: */
105: public ListMultipleChoice(final String id, final List choices,
106: final IChoiceRenderer renderer) {
107: super (id, choices, renderer);
108: }
109:
110: /**
111: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
112: * IModel, List)
113: */
114: public ListMultipleChoice(final String id, IModel object,
115: final List choices) {
116: super (id, object, choices);
117: }
118:
119: /**
120: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
121: * IModel, List,IChoiceRenderer)
122: */
123: public ListMultipleChoice(final String id, IModel object,
124: final List choices, final IChoiceRenderer renderer) {
125: super (id, object, choices, renderer);
126: }
127:
128: /**
129: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
130: * IModel)
131: */
132: public ListMultipleChoice(String id, IModel choices) {
133: super (id, choices);
134: }
135:
136: /**
137: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
138: * IModel,IModel)
139: */
140: public ListMultipleChoice(String id, IModel model, IModel choices) {
141: super (id, model, choices);
142: }
143:
144: /**
145: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
146: * IModel,IChoiceRenderer)
147: */
148: public ListMultipleChoice(String id, IModel choices,
149: IChoiceRenderer renderer) {
150: super (id, choices, renderer);
151: }
152:
153: /**
154: * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
155: * IModel, IModel,IChoiceRenderer)
156: */
157: public ListMultipleChoice(String id, IModel model, IModel choices,
158: IChoiceRenderer renderer) {
159: super (id, model, choices, renderer);
160: }
161:
162: /**
163: * Sets the number of visible rows in the listbox.
164: *
165: * @param maxRows
166: * the number of visible rows
167: * @return this
168: */
169: public final ListMultipleChoice setMaxRows(final int maxRows) {
170: this .maxRows = maxRows;
171: return this ;
172: }
173:
174: /**
175: * @see FormComponent#getModelValue()
176: */
177: public final String getModelValue() {
178: // Get the list of selected values
179: Object modelObject = getModelObject();
180: if (modelObject != null && !(modelObject instanceof Collection)) {
181: throw new WicketRuntimeException(
182: "Model object for a ListMultipleChoice must be a Collection (found "
183: + modelObject.getClass() + ")");
184: }
185: final Collection selectedValues = (Collection) modelObject;
186: final AppendingStringBuffer buffer = new AppendingStringBuffer();
187: if (selectedValues != null) {
188: final List choices = getChoices();
189: for (final Iterator iterator = selectedValues.iterator(); iterator
190: .hasNext();) {
191: final Object object = iterator.next();
192:
193: int index = choices.indexOf(object);
194: buffer.append(getChoiceRenderer().getIdValue(object,
195: index));
196: buffer.append(VALUE_SEPARATOR);
197: }
198: }
199: return buffer.toString();
200: }
201:
202: /**
203: * @see org.apache.wicket.markup.html.form.AbstractChoice#isSelected(Object,int,
204: * String)
205: */
206: protected final boolean isSelected(Object choice, int index,
207: String selected) {
208: // Have a value at all?
209: if (selected != null) {
210: // Loop through ids
211: for (final StringTokenizer tokenizer = new StringTokenizer(
212: selected, VALUE_SEPARATOR); tokenizer
213: .hasMoreTokens();) {
214: final String id = tokenizer.nextToken();
215: if (id.equals(getChoiceRenderer().getIdValue(choice,
216: index))) {
217: return true;
218: }
219: }
220: }
221: return false;
222: }
223:
224: /**
225: * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
226: */
227: protected void onComponentTag(final ComponentTag tag) {
228: super .onComponentTag(tag);
229: tag.put("multiple", "multiple");
230:
231: if (!tag.getAttributes().containsKey("size")) {
232: tag.put("size", Math.min(maxRows, getChoices().size()));
233: }
234: }
235:
236: /**
237: * @see org.apache.wicket.markup.html.form.FormComponent#convertValue(String[])
238: */
239: protected Object convertValue(String[] ids)
240: throws ConversionException {
241: if (ids != null && ids.length > 0 && !Strings.isEmpty(ids[0])) {
242: return convertChoiceIdsToChoices(ids);
243: } else {
244: // TODO 1.3: check if its safe to return Collections.EMPTY_LIST here
245: return new ArrayList();
246: }
247: }
248:
249: /**
250: * Converts submitted choice ids to choice objects.
251: *
252: * @param ids
253: * choice ids. this array is nonnull and always contains at least
254: * one id.
255: * @return list of choices.
256: */
257: protected List convertChoiceIdsToChoices(String[] ids) {
258: ArrayList selectedValues = new ArrayList();
259:
260: // If one or more ids is selected
261: if (ids != null && ids.length > 0 && !Strings.isEmpty(ids[0])) {
262: // Get values that could be selected
263: final List choices = getChoices();
264:
265: // Loop through selected indices
266: for (int i = 0; i < ids.length; i++) {
267: for (int index = 0; index < choices.size(); index++) {
268: // Get next choice
269: final Object choice = choices.get(index);
270: if (getChoiceRenderer().getIdValue(choice, index)
271: .equals(ids[i])) {
272: selectedValues.add(choice);
273: break;
274: }
275: }
276: }
277: }
278: return selectedValues;
279:
280: }
281:
282: /**
283: * If the model object exists, it is assumed to be a Collection, and it is
284: * modified in-place. Then {@link Model#setObject(Object)} is called with
285: * the same instance: it allows the Model to be notified of changes even
286: * when {@link Model#getObject()} returns a different {@link Collection} at
287: * every invocation.
288: *
289: * @see FormComponent#updateModel()
290: * @throws UnsupportedOperationException
291: * if the model object Collection cannot be modified
292: */
293: public void updateModel() {
294: Collection selectedValues = (Collection) getModelObject();
295: if (selectedValues != null) {
296: modelChanging();
297: selectedValues.clear();
298: selectedValues.addAll((Collection) getConvertedInput());
299: modelChanged();
300: getModel().setObject(selectedValues);
301: } else {
302: selectedValues = (Collection) getConvertedInput();
303: modelChanging();
304: getModel().setObject(selectedValues);
305: modelChanged();
306: }
307: }
308: }
|