001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.component;
042:
043: import java.lang.reflect.Array;
044: import java.util.ArrayList;
045: import java.util.Iterator;
046: import javax.faces.FacesException;
047: import javax.faces.application.FacesMessage;
048: import javax.faces.component.UIComponent;
049: import javax.faces.context.FacesContext;
050: import javax.faces.convert.ConverterException;
051: import com.sun.rave.web.ui.component.Button;
052: import com.sun.rave.web.ui.component.Label;
053: import com.sun.rave.web.ui.component.StaticText;
054: import com.sun.rave.web.ui.model.list.ListItem;
055: import com.sun.rave.web.ui.theme.Theme;
056: import com.sun.rave.web.ui.util.ThemeUtilities;
057: import com.sun.rave.web.ui.util.ConversionUtilities;
058: import com.sun.rave.web.ui.util.ValueType;
059: import com.sun.rave.web.ui.util.ValueTypeEvaluator;
060:
061: /**
062: * <h4>About this tag.</h4>
063: *
064: * <p>This tag renders an OrderableList component. Use this component
065: * when web application users need to create and modify a list of
066: * strings. The application user can add new strings by typing them
067: * into the textfield and clicking the "moveUp" button, and remove them
068: * by selecting one or more items from the list and clicking the
069: * "Remove" button.</p>
070: *
071: * <h4>Configuring the listbox tag</h4>
072: *
073: * <p> Use the <code>list</code> attribute to bind the component
074: * to a model. The value must be an EL expression that corresponds to
075: * a managed bean or a property of a managed bean, and it must
076: * evaluate to an array of <code>java.lang.String</code>.
077: * </p>
078: *
079: * <p>To set the label of the textfield, use the
080: * <code>fieldLabel</code> attribute. To set the label of the
081: * textfield, use the <code>listLabel</code> attribute. To validate
082: * new items, use the <code>fieldValidator</code> attribute; to
083: * validate the contents of the list once the user has finished
084: * adding and removing items, specify a <code>labelValidator</code>.</p>
085: *
086: * <h4>Facets</h4>
087: *
088: * <ul>
089: * <li><code>fieldLabel</code>: use this facet to specify a custom
090: * component for the textfield label.</li>
091: * <li><code>listLabel</code>: use this facet to specify a custom
092: * component for the textfield label.</li>
093: * <li><code>field</code>: use this facet to specify a custom
094: * component for the textfield.</li>
095: * <li><code>moveUpButton</code>: use this facet to specify a custom
096: * component for the moveUp button.</li>
097: * <li><code>removeButton</code>: use this facet to specify a custom
098: * component for the remove button.</li>
099: * <li><code>search</code>: use this facet to specify a custom
100: * component for the search button. </li>
101: * <li><code>readOnly</code>: use this facet to specify a custom
102: * component for display a readonly version of the component.</li>
103: * <li><code>header</code>: use this facet to specify a header,
104: * rendered in a table row above the component.</li>
105: * <li><code>footer</code>: use this facet to specify a header,
106: * rendered in a table row below the component.</li>
107: * </ul>
108: *
109: * <h4>Client-side JavaScript functions</h4>
110: *
111: * <ul>
112: * <li>NONE yet</li>
113: * </ul>
114: * @author avk
115: */
116: public class OrderableList extends OrderableListBase implements
117: ListManager {
118:
119: /**
120: * The component id for the moveUp button.
121: */
122: public static final String MOVEUP_BUTTON_ID = "_moveUpButton"; //NOI18N
123: /**
124: * The facet name for the moveUp button.
125: */
126: public static final String MOVEUP_BUTTON_FACET = "moveUpButton"; //NOI18N
127:
128: /**
129: * The component id for the moveDown button.
130: */
131: public static final String MOVEDOWN_BUTTON_ID = "_moveDownButton"; //NOI18N
132: /**
133: * The facet name for the moveDown button.
134: */
135: public static final String MOVEDOWN_BUTTON_FACET = "moveDownButton"; //NOI18N
136:
137: /**
138: * The component id for the moveTop button.
139: */
140: public static final String MOVETOP_BUTTON_ID = "_moveTopButton"; //NOI18N
141: /**
142: * The facet name for the moveTop button.
143: */
144: public static final String MOVETOP_BUTTON_FACET = "moveTopButton"; //NOI18N
145:
146: /**
147: * The component id for the moveBottom button.
148: */
149: public static final String MOVEBOTTOM_BUTTON_ID = "_moveBottomButton"; //NOI18N
150: /**
151: * The facet name for the moveBottom button.
152: */
153: public static final String MOVEBOTTOM_BUTTON_FACET = "moveBottomButton"; //NOI18N
154:
155: /**
156: * The component ID for the remove button.
157: */
158: public static final String REMOVE_BUTTON_ID = "_removeButton"; //NOI18N
159: /**
160: * The facet name for the remove button.
161: */
162: public static final String REMOVE_BUTTON_FACET = "removeButton"; //NOI18N
163:
164: /**
165: * The component ID for the label.
166: */
167: public static final String LABEL_ID = "_label"; //NOI18N
168: /**
169: * The facet name for the label.
170: */
171: public static final String LABEL_FACET = "label"; //NOI18N
172:
173: /**
174: * The component ID for the read only text field.
175: */
176: public static final String READ_ONLY_ID = "_readOnly"; //NOI18N
177: /**
178: * The facet name for the readOnly text field.
179: */
180: public static final String READ_ONLY_FACET = "readOnly"; //NOI18N
181:
182: /**
183: * The name for the footer facet.
184: */
185: public static final String FOOTER_FACET = "footer"; //NOI18N
186:
187: /**
188: * The name of the component attribute that stores the name of the
189: * JavaScript object that organizes the JavsSCript functions for the
190: * component.
191: */
192: public static final String JSOBJECT = "com.sun.rave.web.ui.OrderableListJS";
193: /**
194: * Represents the "javascript:" printed at the start of javascript
195: * event handler code.
196: */
197: public static final String JAVASCRIPT_PREFIX = "javascript: ";
198: /**
199: * String representing "return false" printed at the end of the
200: * javascript event handlers.
201: */
202: public static final String RETURN = "return false;";
203:
204: /**
205: * Name of the JavaScript function which moves elements up.
206: */
207: public static final String MOVEUP_FUNCTION = ".moveUp(); ";
208: /**
209: * Name of the JavaScript function which moves elements down.
210: */
211: public static final String MOVEDOWN_FUNCTION = ".moveDown();";
212: /**
213: * Name of the JavaScript function which moves elements to the top.
214: */
215: public static final String MOVETOP_FUNCTION = ".moveTop(); ";
216: /**
217: * Name of the JavaScript function which moves elements to the bottom.
218: */
219: public static final String MOVEBOTTOM_FUNCTION = ".moveBottom();";
220: /**
221: * Name of the JavaScript function that updates the buttons.
222: */
223: public static final String UPDATEBUTTONS_FUNCTION = ".updateButtons(); ";
224: /**
225: * Name of the JavaScript function that handles changes on the
226: * available list.
227: */
228: public static final String ONCHANGE_FUNCTION = ".onChange(); ";
229:
230: /**
231: * Read only separator string
232: */
233: private static final String READ_ONLY_SEPARATOR = ", "; //NOI18N
234:
235: // Holds the ValueType of this component
236: private ValueTypeEvaluator valueTypeEvaluator = null;
237: private ArrayList listItems = null;
238: private transient Theme theme = null;
239:
240: private static final boolean DEBUG = false;
241:
242: public OrderableList() {
243: valueTypeEvaluator = new ValueTypeEvaluator(this );
244: }
245:
246: // Buttons
247: /**
248: * Get or create the MOVE UP button. Retrieves the component specified by the
249: * moveUpButton facet (if there is one) or creates a new Button component.
250: * @return A UI Component for the moveUp button
251: */
252: public UIComponent getMoveUpButtonComponent(FacesContext context) {
253:
254: if (DEBUG)
255: log("getMoveUpButtonComponent()");
256:
257: String id = getId();
258:
259: // Check if the page author has defined an moveUpbutton facet
260: UIComponent buttonComponent = getFacet(MOVEUP_BUTTON_FACET);
261: // If the page author has not defined a button facet,
262: // check if the page author specified a button.
263: if (buttonComponent == null) {
264:
265: buttonComponent = createButton(getTheme().getMessage(
266: "OrderableList.moveUp"), //NOI18N
267: MOVEUP_BUTTON_ID, MOVEUP_BUTTON_FACET);
268: StringBuffer jsBuffer = new StringBuffer(200);
269: jsBuffer.append(JAVASCRIPT_PREFIX);
270: jsBuffer.append(getAttributes().get(JSOBJECT));
271: jsBuffer.append(MOVEUP_FUNCTION);
272: jsBuffer.append(RETURN);
273: ((Button) buttonComponent).setOnClick(jsBuffer.toString());
274: } else if (DEBUG) {
275: log("\tFound facet."); //NOI18N
276: }
277: if (buttonComponent != null && isDisabled()) {
278: buttonComponent.getAttributes().put("disabled",
279: Boolean.TRUE); //NOI18N
280: }
281: return buttonComponent;
282: }
283:
284: /**
285: * Get or create the MOVEDOWN button. Retrieves the component specified by the
286: * moveDownButton facet (if there is one) or creates a new Button component.
287: * @return A UI Component for the MOVEDOWN button
288: */
289: public UIComponent getMoveDownButtonComponent(FacesContext context) {
290:
291: if (DEBUG)
292: log("getmoveDownButtonComponent()");
293:
294: String id = getId();
295:
296: // Check if the page author has defined an moveDownbutton facet
297: UIComponent buttonComponent = getFacet(MOVEDOWN_BUTTON_FACET);
298: // If the page author has not defined a button facet,
299: // check if the page author specified a button.
300: if (buttonComponent == null) {
301:
302: buttonComponent = createButton(getTheme().getMessage(
303: "OrderableList.moveDown"), //NOI18N
304: MOVEDOWN_BUTTON_ID, MOVEDOWN_BUTTON_FACET);
305: StringBuffer jsBuffer = new StringBuffer(200);
306: jsBuffer.append(JAVASCRIPT_PREFIX);
307: jsBuffer.append(getAttributes().get(JSOBJECT));
308: jsBuffer.append(MOVEDOWN_FUNCTION);
309: jsBuffer.append(RETURN);
310: ((Button) buttonComponent).setOnClick(jsBuffer.toString());
311: } else if (DEBUG) {
312: log("\tFound facet."); //NOI18N
313: }
314: if (buttonComponent != null && isDisabled()) {
315: buttonComponent.getAttributes().put("disabled",
316: Boolean.TRUE); //NOI18N
317: }
318: return buttonComponent;
319: }
320:
321: /**
322: * Get or create the MOVETOP button. Retrieves the component specified by the
323: * moveTopButton facet (if there is one) or creates a new Button component.
324: * @return A UI Component for the Add button
325: */
326: public UIComponent getMoveTopButtonComponent(FacesContext context) {
327:
328: if (DEBUG)
329: log("getAddButtonComponent()");
330:
331: String id = getId();
332:
333: // Check if the page author has defined an moveTopbutton facet
334: UIComponent buttonComponent = getFacet(MOVETOP_BUTTON_FACET);
335: // If the page author has not defined a button facet,
336: // check if the page author specified a button.
337: if (buttonComponent == null) {
338:
339: buttonComponent = createButton(getTheme().getMessage(
340: "OrderableList.moveTop"), //NOI18N
341: MOVETOP_BUTTON_ID, MOVETOP_BUTTON_FACET);
342: StringBuffer jsBuffer = new StringBuffer(200);
343: jsBuffer.append(JAVASCRIPT_PREFIX);
344: jsBuffer.append(getAttributes().get(JSOBJECT));
345: jsBuffer.append(MOVETOP_FUNCTION);
346: jsBuffer.append(RETURN);
347: ((Button) buttonComponent).setOnClick(jsBuffer.toString());
348: } else if (DEBUG) {
349: log("\tFound facet."); //NOI18N
350: }
351: if (buttonComponent != null && isDisabled()) {
352: buttonComponent.getAttributes().put("disabled",
353: Boolean.TRUE); //NOI18N
354: }
355: return buttonComponent;
356: }
357:
358: /**
359: * Get or create the MOVEBOTTOM button. Retrieves the component specified by the
360: * moveBottomButton facet (if there is one) or creates a new Button component.
361: * @return A UI Component for the Move Bottom button
362: */
363: public UIComponent getMoveBottomButtonComponent(FacesContext context) {
364:
365: if (DEBUG)
366: log("getMoveBottomButtonComponent()");
367:
368: String id = getId();
369:
370: // Check if the page author has defined an addbutton facet
371: UIComponent buttonComponent = getFacet(MOVEBOTTOM_BUTTON_FACET);
372: // If the page author has not defined a button facet,
373: // check if the page author specified a button.
374: if (buttonComponent == null) {
375:
376: buttonComponent = createButton(getTheme().getMessage(
377: "OrderableList.moveBottom"), //NOI18N
378: MOVEBOTTOM_BUTTON_ID, MOVEBOTTOM_BUTTON_FACET);
379: StringBuffer jsBuffer = new StringBuffer(200);
380: jsBuffer.append(JAVASCRIPT_PREFIX);
381: jsBuffer.append(getAttributes().get(JSOBJECT));
382: jsBuffer.append(MOVEBOTTOM_FUNCTION);
383: jsBuffer.append(RETURN);
384: ((Button) buttonComponent).setOnClick(jsBuffer.toString());
385: } else if (DEBUG) {
386: log("\tFound facet."); //NOI18N
387: }
388: if (buttonComponent != null && isDisabled()) {
389: buttonComponent.getAttributes().put("disabled",
390: Boolean.TRUE); //NOI18N
391: }
392: return buttonComponent;
393: }
394:
395: // Labels
396: /**
397: * Gets or creates a component for the list label. Retrieves the
398: * listLabel facet if one was specified, or creates a new label component.
399: * @return A UIComponent for the list label
400: */
401: public UIComponent getHeaderComponent() {
402:
403: if (DEBUG)
404: log("getListLabelComponent()");
405:
406: String id = getId();
407:
408: // Check if the page author has defined a label facet
409: UIComponent labelComponent = getFacet(LABEL_FACET);
410:
411: // If the page author has not defined a label facet,
412: // check if the page author specified a label.
413: if (labelComponent == null) {
414:
415: String labelString = getLabel();
416: if (labelString == null || labelString.length() == 0) {
417: labelString = getTheme().getMessage(
418: "OrderableList.defaultListLabel"); //NOI18N
419: }
420:
421: labelComponent = createLabel(labelString, LABEL_ID,
422: LABEL_FACET);
423:
424: ((Label) labelComponent).setLabeledComponent(this );
425: } else if (DEBUG) {
426: log("\tFound facet."); //NOI18N
427: }
428:
429: return labelComponent;
430: }
431:
432: public String getPrimaryElementID(FacesContext context) {
433: return this .getClientId(context).concat(ListSelector.LIST_ID);
434: }
435:
436: /**
437: * Retrieve an Iterator of ListSelector.ListItem, to be used by the
438: * renderer.
439: * @return an Iterator over {@link ListItem}.
440: * @throws javax.faces.FacesException
441: */
442: public Iterator getListItems(FacesContext context, boolean ruler)
443: throws FacesException {
444:
445: if (DEBUG)
446: log("getListItems()");
447:
448: listItems = new ArrayList();
449:
450: Object submittedValue = getSubmittedValue();
451: if (submittedValue != null
452: && submittedValue instanceof String[]) {
453: ListItem listItem = null;
454: String[] values = (String[]) submittedValue;
455: for (int counter = 0; counter < values.length; ++counter) {
456: if (DEBUG)
457: log("Adding listItem " + values[counter]);
458: listItem = new ListItem(values[counter],
459: values[counter]);
460: listItem.setValue(values[counter]);
461: listItems.add(listItem);
462: }
463: return listItems.iterator();
464: }
465:
466: Object listItemsObject = getList();
467: if (listItemsObject == null) {
468: if (DEBUG)
469: log("\tNo list items!");
470: // do nothing...
471: } else if (valueTypeEvaluator.getValueType() == ValueType.LIST) {
472: Iterator items = ((java.util.List) listItemsObject)
473: .iterator();
474: Object item;
475: ListItem listItem;
476: while (items.hasNext()) {
477: item = items.next();
478: listItems.add(createListItem(this , item));
479: }
480: }
481:
482: else if (valueTypeEvaluator.getValueType() == ValueType.ARRAY) {
483:
484: if (DEBUG)
485: log("\tFound array value");
486:
487: // The strings variable represents the strings entered by
488: // the user, as an array.
489:
490: Object[] listObjects = (Object[]) listItemsObject;
491:
492: for (int counter = 0; counter < listObjects.length; ++counter) {
493: listItems
494: .add(createListItem(this , listObjects[counter]));
495: }
496: }
497:
498: else {
499: String msg = getTheme().getMessage(
500: "OrderableList.invalidListType"); //NOI18N
501: throw new FacesException(msg);
502: }
503:
504: return listItems.iterator();
505: }
506:
507: /**
508: * Enforce non null values.
509: * This is ok, since Converter returns null on null input.
510: * And secondly this is equivalent to SelectItem and therefore
511: * Option which do not allow null values.
512: *
513: * However we have to be wary of values that are "".
514: * But if the null case is out of the way the this should
515: * work ok.
516: */
517: protected ListItem createListItem(UIComponent comp, Object value) {
518:
519: // Do not allow null values
520: //
521: if (value == null) {
522: throw new NullPointerException(
523: "OrderableList ListItems cannot have null values");
524: }
525: if (DEBUG)
526: log("createListItem()");
527: String label = ConversionUtilities.convertValueToString(comp,
528: value);
529: if (DEBUG)
530: log("\tLabel is " + label);
531: ListItem listItem = new ListItem(value, label);
532: if (DEBUG)
533: log("\tCreated ListItem");
534: listItem.setValue(label);
535: return listItem;
536: }
537:
538: public String[] getValueAsStringArray(FacesContext context) {
539:
540: if (DEBUG)
541: log("getValueAsStringArray)");
542:
543: Iterator iterator = getListItems(context, false);
544: int numItems = listItems.size();
545: String[] values = new String[numItems];
546:
547: int counter = 0;
548: while (counter < numItems) {
549: values[counter] = ((ListItem) (iterator.next())).getValue();
550: if (DEBUG)
551: log("List item value "
552: + String.valueOf(values[counter]));
553: ++counter;
554: }
555: return values;
556: }
557:
558: /**
559: * Retrieve the value of this component (the "selected" property) as an
560: * object. This method is invoked by the JSF engine during the validation
561: * phase. The JSF default behaviour is for components to defer the
562: * conversion and validation to the renderer, but for the Selector based
563: * components, the renderers do not share as much functionality as the
564: * components do, so it is more efficient to do it here.
565: * @param context The FacesContext of the request
566: * @param submittedValue The submitted value of the component
567: */
568: public Object getConvertedValue(FacesContext context,
569: Object submittedValue) throws ConverterException {
570:
571: if (DEBUG)
572: log("getConvertedValue()");
573:
574: if (!(submittedValue instanceof String[])) {
575: throw new ConverterException(
576: "Submitted value must be a String array");
577: }
578: String[] rawValues = (String[]) submittedValue;
579:
580: // If there are no elements in rawValues nothing was submitted.
581: // If null was rendered, return null
582: //
583: if (rawValues.length == 0) {
584: if (ConversionUtilities.renderedNull(this )) {
585: return null;
586: }
587: }
588:
589: Object cValue = null;
590: try {
591:
592: if (valueTypeEvaluator.getValueType() == ValueType.ARRAY) {
593: if (DEBUG)
594: log("\tComponent value is an array");
595: cValue = ConversionUtilities.convertValueToArray(this ,
596: rawValues, context);
597: }
598: // This case is not supported yet!
599: else if (valueTypeEvaluator.getValueType() == ValueType.LIST) {
600: if (DEBUG)
601: log("\tComponent value is a list");
602: cValue = ConversionUtilities.convertValueToList(this ,
603: rawValues, context);
604: }
605: } catch (Exception ex) {
606: if (DEBUG)
607: ex.printStackTrace();
608: }
609: return cValue;
610: }
611:
612: // Readonly value
613: /**
614: * Return a string suitable for displaying the value in read only mode.
615: * The default is to separate the list values with a comma.
616: *
617: * @param context The FacesContext
618: * @throws javax.faces.FacesException If the list items cannot be processed
619: */
620: protected String getValueAsReadOnly(FacesContext context)
621: throws FacesException {
622:
623: // The comma format READ_ONLY_SEPARATOR should be part of the theme
624: // and/or configurable by the application
625: //
626: StringBuffer valueBuffer = new StringBuffer(200);
627:
628: Iterator iterator = getListItems(context, false);
629:
630: while (iterator.hasNext()) {
631: String string = ((ListItem) (iterator.next())).getLabel();
632: // Do this with a boolean on getListItems instead
633: if (string.indexOf("nbsp") > -1) { //NOI18N
634: continue;
635: }
636: valueBuffer.append(string);
637: if (iterator.hasNext()) {
638: valueBuffer.append(READ_ONLY_SEPARATOR);
639: }
640: }
641: return valueBuffer.toString();
642: }
643:
644: /**
645: * Creates a component for the OrderableList in case the component
646: * is read-only.
647: * @return A UIComponent that displays the read-only value
648: */
649: public UIComponent getReadOnlyValueComponent() {
650:
651: if (DEBUG)
652: log("getListLabelComponent()");
653:
654: String id = getId();
655:
656: // Check if the page author has defined a label facet
657: UIComponent textComponent = getFacet(READ_ONLY_FACET); //NOI18N
658:
659: // If the page author has not defined a label facet,
660: // check if the page author specified a label.
661: if (textComponent == null) {
662: FacesContext context = FacesContext.getCurrentInstance();
663: textComponent = createText(getValueAsReadOnly(context), //NOI18N
664: READ_ONLY_ID, READ_ONLY_FACET);
665: } else if (DEBUG) {
666: log("\tFound facet."); //NOI18N
667: }
668: return textComponent;
669: }
670:
671: // TODO - these are reused by several list components, can we refactor these?
672: private Label createLabel(String labelString, String id,
673: String facetName) {
674:
675: if (DEBUG)
676: log("createLabel()");
677:
678: // If we find a label, define a component and add it to the
679: // children, unless it has been added in a previous cycle
680: // (the component is being redisplayed).
681:
682: if (labelString == null || labelString.length() < 1) {
683: // TODO - maybe print a default?
684: labelString = new String();
685: }
686:
687: Label label = new Label();
688: label.setText(labelString);
689: label.setLabelLevel(getLabelLevel());
690: this .getFacets().put(facetName, label);
691: label.setId(getId().concat(id));
692: return label;
693: }
694:
695: // TODO - these are reused by several list components, can we refactor these?
696: private StaticText createText(String text, String id,
697: String facetName) {
698:
699: if (DEBUG)
700: log("createText()");
701:
702: // If we find a label, define a component and add it to the
703: // children, unless it has been added in a previous cycle
704: // (the component is being redisplayed).
705:
706: if (text == null || text.length() < 1) {
707: // TODO - maybe print a default?
708: text = new String();
709: }
710:
711: StaticText field = new StaticText();
712: field.setValue(text);
713: field.setId(getId().concat(id));
714: getFacets().put(facetName, field);
715: return field;
716: }
717:
718: // TODO - these are reused by several list components, can we refactor these?
719: private Button createButton(String text, String id, String facetName) {
720:
721: if (DEBUG)
722: log("createButton()");
723:
724: Button button = new Button();
725: button.setId(getId().concat(id));
726: //button.setImmediate(true);
727: button.setText(text);
728: if (getTabIndex() > 0) {
729: button.setTabIndex(getTabIndex());
730: }
731: button.setPrimary(true);
732: getFacets().put(facetName, button);
733: return button;
734: }
735:
736: private void log(String s) {
737: System.out.println(this .getClass().getName() + "::" + s); //NOI18N
738: }
739:
740: private Theme getTheme() {
741: return ThemeUtilities.getTheme(FacesContext
742: .getCurrentInstance());
743: }
744:
745: public int getRows() {
746:
747: int rows = super .getRows();
748: if (rows < 1) {
749: rows = 12;
750: super .setRows(12);
751: }
752: return rows;
753: }
754:
755: public String getOnChange() {
756: return null;
757: }
758:
759: /**
760: * <p>Return <code>true</code> if the new value is different from the
761: * previous value.</p>
762: *
763: * This only implements a compareValues for value if it is an Array.
764: * If value is not an Array, defer to super.compareValues.
765: * The assumption is that the ordering of the elements
766: * between the previous value and the new value is determined
767: * in the same manner.
768: *
769: * Another assumption is that the two object arguments
770: * are of the same type, both arrays of both not arrays.
771: *
772: * @param previous old value of this component (if any)
773: * @param value new value of this component (if any)
774: */
775: protected boolean compareValues(Object previous, Object value) {
776:
777: // Let super take care of null cases
778: //
779: if (previous == null || value == null) {
780: return super .compareValues(previous, value);
781: }
782: if (value instanceof Object[]) {
783: // If the lengths aren't equal return true
784: //
785: int length = Array.getLength(value);
786: if (Array.getLength(previous) != length) {
787: return true;
788: }
789: // Each element at index "i" in previous must be equal to the
790: // elementa at index "i" in value.
791: //
792: for (int i = 0; i < length; ++i) {
793:
794: Object newValue = Array.get(value, i);
795: Object prevValue = Array.get(previous, i);
796:
797: // This is probably not necessary since
798: // an Option's value cannot be null
799: //
800: if (newValue == null) {
801: if (prevValue == null) {
802: continue;
803: } else {
804: return true;
805: }
806: }
807: if (prevValue == null) {
808: return true;
809: }
810:
811: if (!prevValue.equals(newValue)) {
812: return true;
813: }
814: }
815: return false;
816: }
817: return super .compareValues(previous, value);
818: }
819:
820: public boolean mainListSubmits() {
821: return false;
822: }
823: }
|