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.renderer;
042:
043: import java.io.IOException;
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049: import java.util.Properties;
050: import java.util.StringTokenizer;
051:
052: import javax.faces.component.UIComponent;
053: import javax.faces.component.UISelectMany;
054: import javax.faces.component.ValueHolder;
055: import javax.faces.component.EditableValueHolder;
056: import javax.faces.context.FacesContext;
057: import javax.faces.context.ResponseWriter;
058: import javax.faces.render.Renderer;
059:
060: import com.sun.rave.web.ui.component.ListManager;
061: import com.sun.rave.web.ui.component.ListSelector;
062: import com.sun.rave.web.ui.component.Label;
063: import com.sun.rave.web.ui.component.util.Util;
064: import com.sun.rave.web.ui.model.Separator;
065: import com.sun.rave.web.ui.theme.Theme;
066: import com.sun.rave.web.ui.model.OptionTitle;
067: import com.sun.rave.web.ui.model.list.ListItem;
068: import com.sun.rave.web.ui.model.list.StartGroup;
069: import com.sun.rave.web.ui.model.list.EndGroup;
070: import com.sun.rave.web.ui.util.ConversionUtilities;
071: import com.sun.rave.web.ui.util.RenderingUtilities;
072: import com.sun.rave.web.ui.util.ThemeUtilities;
073:
074: /**
075: * The ListRendererBase is the base class for the listbox renderers
076: * (Drop-down Menu and Selectable List). These are both rendered using
077: * the same HTML tag (select) so a lot of the renderering functionality
078: * is shared.
079: */
080: abstract public class ListRendererBase extends Renderer {
081:
082: /**
083: *
084: */
085: private final static boolean DEBUG = false;
086:
087: /** <p>The list of attribute names in the HTML 4.01 Specification that
088: * correspond to the entity type <em>%events;</em>.</p>
089: */
090: public static final String[] STRING_ATTRIBUTES = { "onBlur",
091: "onClick", "onDblClick", "onFocus", //NOI18N
092: "onMouseDown", "onMouseUp", "onMouseOver", //NOI18N
093: "onMouseMove", "onMouseOut", "onKeyPress", //NOI18N
094: "onKeyDown", "onKeyUp", "onSelect" //NOI18N
095: };
096:
097: protected static final String SEPARATOR = "|"; //NOI18N
098:
099: /**
100: * <p>This method determines whether the component should be
101: * rendered as a standalone list, or laid out together with a
102: * label that was defined as part of the component.</p>
103: *
104: * <p>A label will be rendered if either of the following is
105: * true:</p>
106: * <ul>
107: * <li>The page author defined a label facet; or</li>
108: * <li>The page author specified text in the label attribute.</li>
109: * </ul>
110: * <p>If there is a label, the component will be laid out using a
111: * HTML table. If not, the component will be rendered as a
112: * standalone HTML <tt>select</tt> element.</p>
113: * @param component The component associated with the
114: * renderer. Must be a subclass of ListSelector.
115: * @param context The FacesContext of the request
116: * @param styles A String array of styles used to render the
117: * component. The first item of the array is the name of the
118: * JavaScript method that handles change event. The second item is
119: * the style used when the list is enabled. The third style is the
120: * one to use when the list is disabled. The fourth item is the
121: * style to use for an item that is enabled, the fifth to use for
122: * an item that is disabled, and the sixth to use when the item is
123: * selected.
124: * @throws java.io.IOException if the renderer fails to write to
125: * the response
126: */
127: void renderListComponent(ListSelector component,
128: FacesContext context, String[] styles) throws IOException {
129:
130: if (DEBUG)
131: log("renderListComponent()");
132:
133: boolean readonly = component.isReadOnly();
134: if (readonly) {
135: if (DEBUG)
136: log("\t component is readonly");
137: // We don't want to accidentally mark any label as
138: // required in this case...
139: component.setRequired(false);
140: }
141:
142: UIComponent label = component.getLabelComponent();
143: ResponseWriter writer = context.getResponseWriter();
144: String id = component.getClientId(context);
145: boolean spanRendered = false;
146:
147: if (label != null) {
148: renderOpenEncloser(component, context, "span", styles[8]); //NOI18N
149: spanRendered = true;
150: writer.writeText("\n", null);
151: if (!component.isLabelOnTop() && component.getRows() > 1) {
152: Map attributes = label.getAttributes();
153: Object styleClass = attributes.get("styleClass");
154: if (styleClass == null) {
155: attributes.put("styleClass", styles[9]);
156: } else if (styleClass.toString().indexOf(styles[9]) == -1) {
157: attributes.put("styleClass", styleClass + " "
158: + styles[9]);
159: }
160: }
161:
162: RenderingUtilities.renderComponent(label, context);
163: writer.writeText("\n", null);
164: if (component.isLabelOnTop()) {
165:
166: writer.startElement("br", component); //NOI18N
167: writer.endElement("br"); //NOI18N
168: writer.writeText("\n", null);
169: }
170:
171: writer.writeText("\n", null);
172: id = id.concat(ListSelector.LIST_ID);
173: }
174:
175: if (readonly) {
176: UIComponent value = component.getReadOnlyValueComponent();
177: if (label == null) {
178: value.getAttributes()
179: .put("style", component.getStyle());
180: value.getAttributes().put("styleClass",
181: component.getStyleClass());
182: }
183: RenderingUtilities.renderComponent(value, context);
184: } else {
185: //renderHiddenValue(component, context, writer, styles[8]);
186: //writer.writeText("\n", null);
187:
188: // Because renderHiddenValue is commented out this needs
189: // to be called for supporting DB NULL values.
190: // If it becomes uncommented remove this call.
191: //
192: recordRenderedValue(component);
193: renderList(component, id, context, styles, label == null);
194: }
195: if (label != null) {
196: context.getResponseWriter().endElement("span"); //NOI18N
197: }
198: }
199:
200: /**
201: * Overrides encodeChildren of Renderer to do nothing. This
202: * renderer renders its own children, but not through this
203: * method.
204: * @param context The FacesContext of the request
205: * @param component The component associated with the
206: * renderer. Must be a subclass of ListSelector.
207: */
208: public void encodeChildren(
209: javax.faces.context.FacesContext context,
210: javax.faces.component.UIComponent component)
211: throws java.io.IOException {
212: return;
213: }
214:
215: /**
216: * <p>Renders the opening div tag.</p>
217: * @param component The component associated with the
218: * renderer. Must implement ListManager
219: * @param context The FacesContext of the request
220: * @param element One of "span" or "div"
221: * @throws java.io.IOException if the renderer fails to write to
222: * the response
223: */
224: protected void renderOpenEncloser(ListManager component,
225: FacesContext context, String element, String hiddenStyle)
226: throws IOException {
227:
228: String id = component.getClientId(context);
229:
230: ResponseWriter writer = context.getResponseWriter();
231: writer.writeText("\n", null);
232: writer.startElement(element, (UIComponent) component);
233: writer.writeAttribute("id", id, "id"); //NOI18N
234:
235: String style = component.getStyle();
236: if (style != null && style.length() > 0) {
237: writer.writeAttribute("style", style, "style"); //NOI18N
238: }
239: style = component.getStyleClass();
240: if (component.isVisible()) {
241: if (style != null && style.length() > 0) {
242: writer.writeAttribute("class", style, "class"); //NOI18N
243: }
244: } else {
245: if (style == null) {
246: style = hiddenStyle;
247: } else {
248: style = style + " " + hiddenStyle; //NOI18N
249: }
250: writer.writeAttribute("class", style, "class"); //NOI18N
251: }
252:
253: writer.writeText("\n", null); //NOI18N
254: }
255:
256: protected void renderHiddenValue(UIComponent component,
257: FacesContext context, ResponseWriter writer,
258: String hiddenStyle) throws IOException {
259:
260: ListManager listManager = (ListManager) component;
261:
262: recordRenderedValue(component);
263:
264: String hiddenID = component.getClientId(context).concat(
265: ListSelector.VALUE_ID);
266:
267: String[] values = listManager.getValueAsStringArray(context);
268: if (DEBUG) {
269: log("Values are: ");
270: for (int i = 0; i < values.length; ++i) {
271: log(String.valueOf(values[i]));
272: }
273: }
274:
275: writer.startElement("select", component); //NOI18N
276: writer.writeAttribute("id", hiddenID, null); //NOI18N
277: writer.writeAttribute("name", hiddenID, null); //NOI18N
278: writer.writeAttribute("multiple", "true", null); //NOI18N
279: writer.writeAttribute("class", hiddenStyle, null); //NOI18N
280: writer.writeText("\n", null); //NOI18N
281: for (int counter = 0; counter < values.length; ++counter) {
282: writer.startElement("option", component); //NOI18N
283: writer.writeAttribute("selected", "selected", null); //NOI18N
284: writer.writeAttribute("value", values[counter], null); //NOI18N
285: writer.writeText(values[counter], null); //NOI18N
286: writer.endElement("option"); //NOI18N
287: writer.writeText("\n", null); //NOI18N
288: }
289: writer.endElement("select"); //NOI18N
290: }
291:
292: /**
293: * This is the base method for rendering a HTML select
294: * element. This method is based on the functionality of the RI
295: * version, so it invokes a method renderSelectItems which in term
296: * invokes renderSelectItem. Currently, this renderer requires for
297: * the options to be specified using the JSF SelectItem construct,
298: * but this should be replaced with a Lockhart version, because
299: * the JSF version lacks the ability to associate an id with the
300: * list item. I'm not sure whether it should be possible to use
301: * SelectItem as well yet.
302: * @param component The UI Component associated with the
303: * renderer.
304: * @param context The FacesContext of the request
305: * @param styles A String array of styles used to render the
306: * component. The first item of the array is the name of the
307: * JavaScript method that handles change event. The second item is
308: * the style used when the list is enabled. The third style is the
309: * one to use when the list is disabled. The fourth item is the
310: * style to use for an item that is enabled, the fifth to use for
311: * an item that is disabled, and the sixth to use when the item is
312: * selected.
313: * @throws java.io.IOException if the renderer fails to write to
314: * the response
315: */
316: protected void renderList(ListManager component, String id,
317: FacesContext context, String[] styles) throws IOException {
318: renderList(component, id, context, styles, false);
319: }
320:
321: private void renderList(ListManager listManager, String id,
322: FacesContext context, String[] styles,
323: boolean renderUserStyles) throws IOException {
324:
325: // Set the style class
326: String styleClass = styles[1];
327: if (listManager.isDisabled()) {
328: styleClass = styles[2];
329: }
330:
331: ResponseWriter writer = context.getResponseWriter();
332:
333: writer.startElement("select", (UIComponent) listManager); //NOI18N
334:
335: if (renderUserStyles) {
336: String style = listManager.getStyle();
337: if (style != null && style.length() > 0) {
338: writer.writeAttribute("style", style, null); //NOI18N
339: }
340: String compStyleClass = getStyleClass(listManager,
341: styles[8]);
342:
343: if (compStyleClass != null && compStyleClass.length() > 0) {
344: styleClass = compStyleClass + " " + styleClass;
345: }
346: }
347:
348: writer.writeAttribute("class", styleClass, null); //NOI18N
349: writer.writeAttribute("id", id, null); //NOI18N
350: if (listManager.mainListSubmits()) {
351: writer.writeAttribute("name", id, null); //NOI18N
352: }
353: int size = listManager.getRows();
354: if (size < 1) {
355: size = 12;
356: }
357: writer.writeAttribute("size", String.valueOf(size), null); //NOI18N
358:
359: if (listManager.isMultiple()) {
360: writer.writeAttribute("multiple", "multiple", null); //NOI18N
361: }
362:
363: if (listManager.isDisabled()) {
364: writer.writeAttribute("disabled", //NOI18N
365: "disabled", //NOI18N
366: "disabled"); //NOI18N
367: }
368:
369: String tooltip = listManager.getToolTip();
370: if (tooltip != null) {
371: writer.writeAttribute("title", tooltip, null); //NOI18N
372: }
373:
374: if (DEBUG)
375: log("Setting onchange event handler");
376: writer.writeAttribute("onchange", styles[0], null); //NOI18N
377:
378: int tabindex = listManager.getTabIndex();
379: if (tabindex > 0 && tabindex < 32767) {
380: writer.writeAttribute("tabindex", //NOI18N
381: String.valueOf(tabindex), "tabindex"); //NOI18N
382: }
383:
384: RenderingUtilities.writeStringAttributes(
385: (UIComponent) listManager, writer, STRING_ATTRIBUTES);
386:
387: writer.writeText("\n", null); //NOI18N
388:
389: renderListOptions((UIComponent) listManager, listManager
390: .getListItems(context, true), writer, styles);
391:
392: writer.endElement("select"); //NOI18N
393: writer.writeText("\n", null); //NOI18N
394: }
395:
396: /**
397: * Append the webui component's own JavaScript function at the end
398: * of any component-specific event handling code.
399: * @param component The ListManager for which we create the function call
400: * @param functionName The name of the function
401: * @param context The FacesContext of this request
402: */
403: protected String getOnChangeJavaScript(ListManager component,
404: String functionName, FacesContext context) {
405:
406: String script = component.getOnChange();
407: String id = component.getClientId(context);
408: StringBuffer onchangeBuffer = new StringBuffer(200);
409: if (script != null) {
410: onchangeBuffer.append(script).append(";");
411: }
412: onchangeBuffer.append(functionName);
413: onchangeBuffer.append("(\'"); //NOI18N
414: onchangeBuffer.append(id);
415: onchangeBuffer.append("\'); "); //NOI18N
416: onchangeBuffer.append(" return false;"); //NOI18N
417: return onchangeBuffer.toString();
418: }
419:
420: /**
421: * This is the method responsible for rendering the options of a
422: * HTML select element. This method is based on the corresponding
423: * method from the JSF RI, so the options to be specified using
424: * the JSF SelectItem construct. This will have to be replaced -
425: * see the renderList method for details.
426: * <p/><i>Note - option groups are not yet implemented w.r.t. any
427: * styles specified by the HCI guidelines.</i>
428: * @param component The UI Component associated with the renderer
429: * @param styles A String array of styles used to render the
430: * component. The first item of the array is the name of the
431: * JavaScript method that handles change event. The second item is
432: * the style used when the list is enabled. The third style is the
433: * one to use when the list is disabled. The fourth item is the
434: * style to use for an item that is enabled, the fifth to use for
435: * an item that is disabled, and the sixth to use when the item is
436: * selected.
437: * @throws java.io.IOException if the renderer fails to write to
438: * the response
439: */
440: void renderListOptions(UIComponent component,
441: Iterator optionsIterator, ResponseWriter writer,
442: String[] styles) throws IOException {
443:
444: if (DEBUG)
445: log("renderListOptions() START");
446:
447: Object option = null;
448: boolean noSeparator = true;
449:
450: while (optionsIterator.hasNext()) {
451:
452: option = optionsIterator.next();
453:
454: if (option instanceof Separator) {
455: if (DEBUG)
456: log("\tFound separator"); //NOII18N
457: renderSeparator(component, writer, styles[7]);
458: }
459:
460: else if (option instanceof StartGroup) {
461:
462: if (DEBUG)
463: log("\tFound option group"); //NOII18N
464:
465: StartGroup group = (StartGroup) option;
466: if (DEBUG) {
467: log("\tThis is the start of a group"); //NOI18N
468: log("\tLabel is" + group.getLabel()); //NOI18N
469: }
470:
471: if (!noSeparator) {
472: renderSeparator(component, writer, styles[7]);
473: }
474: writer.startElement("optgroup", component); //NOI18N
475: // <RAVE>
476: // writer.writeAttribute("label",
477: // group.getLabel(),
478: // null);
479: if (group.getLabel() != null)
480: writer.writeAttribute("label", group.getLabel(),
481: null); //NOI18N
482: // </RAVE>
483: writer.writeAttribute("class", //NOI18N
484: styles[6], null);
485: writer.write("\n"); //NOI18N
486: noSeparator = true;
487: } else if (option instanceof EndGroup) {
488: if (DEBUG)
489: log("\tThis is the end of a group");
490: writer.endElement("optgroup"); //NOI18N
491: writer.write("\n"); //NOI18N
492: if (optionsIterator.hasNext()) {
493: renderSeparator(component, writer, styles[7]);
494: }
495: noSeparator = true;
496: } else {
497: renderListOption(component, (ListItem) option, writer,
498: styles);
499: noSeparator = false;
500: }
501: }
502: if (DEBUG)
503: log("renderListOptions() END");
504: }
505:
506: /**
507: *
508: * This is the method responsible for rendering an individual
509: * option for a HTML select element.
510: *
511: * @param option The ListItem to render
512: * @param writer The ResponseWriter used to render the option
513: * @param styles A String array of styles used to render the
514: * component. The first item of the array is the name of the
515: * JavaScript method that handles change event. The second item is
516: * the style used when the list is enabled. The third style is the
517: * one to use when the list is disabled. The fourth item is the
518: * style to use for an item that is enabled, the fifth to use for
519: * an item that is disabled, and the sixth to use when the item is
520: * selected.
521: * @throws java.io.IOException if the renderer fails to write to
522: * the response
523: */
524: void renderListOption(UIComponent list, ListItem listItem,
525: ResponseWriter writer, String[] styles) throws IOException {
526:
527: if (DEBUG)
528: log("renderListOption() - START");
529:
530: // By default, we use the basic option style
531: String styleClass = styles[3];
532:
533: if (listItem.isSelected()) {
534: if (DEBUG)
535: log("\tItem is selected");
536: // Use "selected" option style
537: styleClass = styles[5];
538: if (DEBUG)
539: log("\tStyleclass is " + styleClass);
540: } else if (listItem.isDisabled()) {
541: if (DEBUG)
542: log("\tItem is disabled");
543: // Use "disabled" option style
544: styleClass = styles[4];
545: } else if (DEBUG) {
546: if (DEBUG)
547: log("\tNormal item");
548: }
549:
550: writer.writeText("\t", null); //NOI18N
551: writer.startElement("option", list); //NOI18N
552: writer.writeAttribute("class", styleClass, null); //NOI18N
553: String itemValue = listItem.getValue();
554:
555: // Note that there is no distinction made between an
556: // itemValue that is null or an empty string.
557: // This is important since the results may be indistinguishable
558: // on the client and therefore indistinguishable in the response.
559: //
560: // However Option which inherits from SelectItem does not
561: // allow null item values.
562: //
563: if (itemValue != null) {
564: if (DEBUG)
565: log("Item value is not null");
566: writer.writeAttribute("value", itemValue, null); //NOI18N
567: }
568: if (listItem.isDisabled()) {
569: writer.writeAttribute("disabled", "disabled", null); //NOI18N
570: }
571: if (listItem.isSelected()) {
572: if (DEBUG)
573: log("\tWriting selected attribute");
574: writer.writeAttribute("selected", "selected", null); //NOI18N
575: }
576:
577: //<RAVE>
578: // Write List Item tooltip (bug Fix: 6274989)
579: String description = listItem.getDescription();
580: if (description != null) {
581: if (DEBUG)
582: log("Item description is not null - " + description);
583: writer.writeAttribute("title", description, null); //NOI18N
584: }
585: //</RAVE>
586:
587: boolean title = listItem.isTitle();
588: if (title) {
589: writer.write("— "); //NOI18N
590: }
591: writer.write(listItem.getLabel());
592: if (title) {
593: writer.write("—"); //NOI18N
594: }
595: writer.endElement("option"); //NOI18N
596: writer.writeText("\n", null); //NOI18N
597: if (DEBUG)
598: log("\trenderListOption() - END");
599: return;
600: }
601:
602: /**
603: *
604: * This method is responsible for rendering a separator for an
605: * option group.
606: *
607: * @param option The component for which we render the separator
608: * @param writer The ResponseWriter used to render the separator
609: * @param style The style to use when rendering the option.
610: * @throws java.io.IOException if the renderer fails to write to
611: * the response
612: */
613: void renderSeparator(UIComponent component, ResponseWriter writer,
614: String style) throws IOException {
615:
616: if (!(component instanceof ListSelector)) {
617: return;
618: }
619:
620: ListSelector selector = (ListSelector) component;
621: if (!selector.isSeparators()) {
622: return;
623: }
624:
625: writer.writeText("\t", null); //NOI18N
626: writer.startElement("option", component); //NOI18N
627: writer.writeAttribute("class", style, null); //NOI18N
628: writer.writeAttribute("disabled", "disabled", null); //NOI18N
629:
630: int numEms = selector.getSeparatorLength();
631: StringBuffer labelBuffer = new StringBuffer();
632: for (int em = 0; em < numEms; ++em) {
633: labelBuffer.append("-"); //NOI18N
634: }
635:
636: writer.writeText(labelBuffer.toString(), null);
637: writer.endElement("option"); //NOI18N
638: writer.writeText("\n", null); //NOI18N
639: }
640:
641: /** This method is used by some of the renderers that extend
642: ListRenderBase (not ListBox and DropDown). I should
643: probably refactor things so that we always know what
644: the hidden style is instead - then renderListComponent
645: would do.
646: TODO.
647: */
648: void renderReadOnlyList(ListManager component, UIComponent label,
649: FacesContext context, String hiddenStyle)
650: throws IOException {
651:
652: UIComponent value = component.getReadOnlyValueComponent();
653:
654: renderOpenEncloser(component, context, "span", hiddenStyle);
655: if (label != null) {
656: RenderingUtilities.renderComponent(label, context);
657: }
658: RenderingUtilities.renderComponent(value, context);
659: context.getResponseWriter().endElement("span"); //NOI18N
660: }
661:
662: // Prepend the hidden style to the user's added style if necessary
663: private String getStyleClass(ListManager component,
664: String hiddenStyle) {
665:
666: String style = component.getStyleClass();
667: if (style != null && style.length() == 0) {
668: style = null;
669: }
670:
671: if (!component.isVisible()) {
672: if (style == null) {
673: style = hiddenStyle;
674: } else {
675: style = style + " " + hiddenStyle;
676: }
677: }
678: return style;
679: }
680:
681: /**
682: * Retrieve user input from the UI.
683: * @param context The FacesContext of this request
684: * @param component The component associated with the renderer
685: */
686: public void decode(FacesContext context, UIComponent component) {
687:
688: if (DEBUG)
689: log("decode()");
690: String id = component.getClientId(context);
691: if (((ListSelector) component).getLabelComponent() != null) {
692: id = id.concat(ListSelector.LIST_ID);
693: }
694: decode(context, component, id);
695: }
696:
697: /**
698: * Retrieve user input from the UI.
699: * The expected format of the request parameter of interest is
700: * <separator>value<separator>value<separator> ...
701: * If a value is an empty string the format is
702: * <separator><separator>
703: * If there is no value there is a single separator.
704: * @param context The FacesContext of this request
705: * @param component The component associated with the renderer
706: * @param id The DOM id of the select element which represents the
707: * value of the list
708: */
709: protected void decode(FacesContext context, UIComponent component,
710: String id) {
711:
712: if (DEBUG)
713: log("decode(context, component, id)");
714: if (DEBUG)
715: log("id is " + id);
716: ListManager lmComponent = (ListManager) component;
717:
718: if (lmComponent.isReadOnly()) {
719: if (DEBUG)
720: log("component is readonly...");
721: return;
722: }
723:
724: Map params = context.getExternalContext()
725: .getRequestParameterValuesMap();
726:
727: String[] values = null;
728: Object p = params.get(id);
729: if (p == null) {
730: values = new String[0];
731: } else {
732: if (DEBUG)
733: log("\tValue is string array");
734: values = (String[]) p;
735: }
736:
737: // If we find the OptionTitle.NONESELECTED
738: // value among a multiple selection list, remove it.
739: // If there is only one value and it matches
740: // OptionTitle.NONESELECTED then act like the
741: // submit did not happen at all and leave the submitted
742: // value as is. It should be null, thereby effectively
743: // taking this component out of further lifecycle processing.
744: //
745: if (values.length > 1) {
746: // Need to remove any OptionTitle submitted values
747: //
748: ArrayList newParams = new ArrayList();
749: for (int i = 0; i < values.length; ++i) {
750: if (OptionTitle.NONESELECTED.equals(values[i])) {
751: continue;
752: }
753: newParams.add(values[i]);
754: }
755: values = (String[]) newParams.toArray(new String[newParams
756: .size()]);
757: } else if (values.length == 1
758: && OptionTitle.NONESELECTED.equals(values[0])) {
759: return;
760: }
761:
762: if (DEBUG) {
763: log("\tNumber of Selected values " + //NOI18N
764: String.valueOf(values.length));
765: for (int counter = 0; counter < values.length; ++counter)
766: log("\tvalue: " + values[counter]); //NOI18N
767: }
768:
769: //CR 6447372
770: //if component is disabled, don't set empty array as submitted value
771: //so only set if values.length > 0, or if it's == 0 and the component
772: //is not disabled
773: if (values.length > 0 || !lmComponent.isDisabled()) {
774: lmComponent.setSubmittedValue(values);
775: }
776:
777: }
778:
779: /**
780: * The list is not responsible for rendering any child components,
781: * so this method returns false. (This is unintuitive, but it
782: * causes the right behaviour). I need to understand this better.
783: */
784: public boolean getRendersChildren() {
785: return true;
786: }
787:
788: /**
789: * Retrieves the selected values as Strings.
790: * @param context The FacesContext of this request
791: * @param component The component associated with the renderer
792: */
793: String[] getUserInput(FacesContext context, UIComponent component) {
794:
795: if (DEBUG)
796: log("::getUserSelectedValues()");
797: String id = component.getClientId(context);
798: // Util.doAssert(id != null);
799: Map params = context.getExternalContext()
800: .getRequestParameterValuesMap();
801:
802: String[] values = null;
803: if (params.containsKey(id)) {
804: values = (String[]) params.get(id);
805: }
806: if (values == null) {
807: values = new String[0];
808: }
809: if (DEBUG) {
810: log("\tNumber of Selected values " + // NOI18N
811: String.valueOf(values.length));
812: for (int counter = 0; counter < values.length; ++counter)
813: log("\t" + values[counter]); //NOI18N
814: }
815: return values;
816: }
817:
818: /**
819: * Log an error - only used during development time.
820: */
821: void log(String s) {
822: System.out.println(this .getClass().getName() + "::" + s); //NOI18N
823: }
824:
825: /**
826: * This must be called where the value is about to be rendered
827: * for DB Null value support
828: */
829: private void recordRenderedValue(UIComponent component) {
830:
831: if (component instanceof EditableValueHolder
832: && ((EditableValueHolder) component)
833: .getSubmittedValue() == null) {
834: ConversionUtilities.setRenderedValue(component,
835: ((EditableValueHolder) component).getValue());
836: }
837: }
838: }
|