001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.renderkit.dom_html_basic;
035:
036: import com.icesoft.faces.context.DOMContext;
037: import com.icesoft.faces.context.effects.JavascriptContext;
038: import org.w3c.dom.Document;
039: import org.w3c.dom.Element;
040: import org.w3c.dom.Text;
041: import org.apache.commons.logging.LogFactory;
042:
043: import javax.faces.component.*;
044: import javax.faces.component.html.HtmlSelectManyCheckbox;
045: import javax.faces.context.FacesContext;
046: import javax.faces.convert.Converter;
047: import javax.faces.convert.ConverterException;
048: import javax.faces.el.ValueBinding;
049: import javax.faces.model.SelectItem;
050: import javax.faces.model.SelectItemGroup;
051: import java.io.IOException;
052: import java.lang.reflect.Array;
053: import java.util.*;
054: import java.util.logging.Logger;
055: import java.util.logging.Level;
056:
057: public class MenuRenderer extends DomBasicInputRenderer {
058:
059: private static Logger log = Logger.getLogger(MenuRenderer.class
060: .getName());
061:
062: public void decode(FacesContext facesContext,
063: UIComponent uiComponent) {
064: validateParameters(facesContext, uiComponent, null);
065: if (isStatic(uiComponent)) {
066: return;
067: }
068: String clientId = uiComponent.getClientId(facesContext);
069: if (uiComponent instanceof UISelectMany) {
070: Map requestParameterValuesMap = facesContext
071: .getExternalContext()
072: .getRequestParameterValuesMap();
073: if (requestParameterValuesMap.containsKey(clientId)) {
074: String[] decodedValue = (String[]) requestParameterValuesMap
075: .get(clientId);
076: setSubmittedValue(uiComponent, decodedValue);
077: } else {
078: // This represents a deselected control
079: setSubmittedValue(uiComponent, new String[0]);
080: }
081: } else if (uiComponent instanceof UISelectOne) {
082: Map requestParameterValuesMap = facesContext
083: .getExternalContext()
084: .getRequestParameterValuesMap();
085: String decodedValue = null;
086: if ((requestParameterValuesMap != null)
087: && (requestParameterValuesMap.containsKey(clientId))) {
088: decodedValue = ((String[]) requestParameterValuesMap
089: .get(clientId))[0].trim();
090: } else {
091: //none of the option has been selected
092: //set it to a blank string, not to null
093: decodedValue = "";
094: }
095: ((UISelectOne) uiComponent).setSubmittedValue(decodedValue);
096:
097: }
098: return;
099: }
100:
101: public void encodeBegin(FacesContext facesContext,
102: UIComponent uiComponent) throws IOException {
103: validateParameters(facesContext, uiComponent, null);
104: }
105:
106: public void encodeChildren(FacesContext facesContext,
107: UIComponent uiComponent) {
108: validateParameters(facesContext, uiComponent, null);
109: }
110:
111: public void encodeEnd(FacesContext facesContext,
112: UIComponent uiComponent) throws IOException {
113: validateParameters(facesContext, uiComponent, null);
114:
115: renderSelect(facesContext, uiComponent);
116: JavascriptContext.fireEffect(uiComponent, facesContext);
117: }
118:
119: public Object getConvertedValue(FacesContext facesContext,
120: UIComponent uiComponent, Object newSubmittedValue)
121: throws ConverterException {
122: if (uiComponent instanceof UISelectOne) {
123: if (newSubmittedValue == null
124: || "".equals(newSubmittedValue)) {
125: return null;
126: } else {
127: return super .getConvertedValue(facesContext,
128: (UISelectOne) uiComponent, newSubmittedValue);
129: }
130: } else {
131: return convertSelectValue(facesContext,
132: ((UISelectMany) uiComponent),
133: (String[]) newSubmittedValue);
134: }
135: }
136:
137: public Object convertSelectValue(FacesContext facesContext,
138: UIComponent uiComponent, Object newSubmittedValue)
139: throws ConverterException {
140: if (uiComponent instanceof UISelectOne) {
141: if (newSubmittedValue == null
142: || "".equals(newSubmittedValue)) {
143: return null;
144: } else {
145: return super .getConvertedValue(facesContext,
146: (UISelectOne) uiComponent, newSubmittedValue);
147: }
148: } else {
149: return convertSelectValue(facesContext,
150: ((UISelectMany) uiComponent),
151: (String[]) newSubmittedValue);
152: }
153: }
154:
155: public Object convertSelectValue(FacesContext facesContext,
156: UISelectMany uiSelectMany, String[] newSubmittedValues)
157: throws ConverterException {
158:
159: ValueBinding valueBinding = uiSelectMany
160: .getValueBinding("value");
161: if (valueBinding == null) {
162: Class componentType = (new Object[1]).getClass()
163: .getComponentType();
164: return convertArray(facesContext, uiSelectMany,
165: componentType, newSubmittedValues);
166: }
167: Class valueBindingClass = valueBinding.getType(facesContext);
168: if (valueBindingClass == null) {
169: throw new ConverterException(
170: "Inconvertible type in value binding");
171: }
172: if (List.class.isAssignableFrom(valueBindingClass)) {
173: Converter converter = uiSelectMany.getConverter();
174: if (converter == null) {
175: // Determine if there is a default converter for the class
176: converter = getConverterForClass(valueBindingClass);
177: }
178:
179: ArrayList submittedValuesAsList = new ArrayList(
180: newSubmittedValues.length);
181: for (int index = 0; index < newSubmittedValues.length; index++) {
182: Object convertedValue = newSubmittedValues[index];
183: if (converter != null) {
184: convertedValue = converter.getAsObject(
185: facesContext, uiSelectMany,
186: newSubmittedValues[index]);
187: }
188: submittedValuesAsList.add(convertedValue);
189: }
190: return submittedValuesAsList;
191: }
192: if (valueBindingClass.isArray()) {
193: Class componentType = valueBindingClass.getComponentType();
194: return convertArray(facesContext, uiSelectMany,
195: componentType, newSubmittedValues);
196: }
197: throw new ConverterException(
198: "Non-list and Non-array values are inconvertible");
199: }
200:
201: protected Object convertArray(FacesContext facesContext,
202: UISelectMany uiSelectMany, Class componentType,
203: String[] newSubmittedValues) throws ConverterException {
204:
205: // component type of String means no conversion is necessary
206: if (componentType.equals(String.class)) {
207: return newSubmittedValues;
208: }
209:
210: // if newSubmittedValue is null return zero-length array
211: if (newSubmittedValues == null) {
212: return Array.newInstance(componentType, 0);
213: }
214:
215: // create the array with specified component length
216: int numberOfValues = newSubmittedValues.length;
217: Object convertedValues = Array.newInstance(componentType,
218: numberOfValues);
219:
220: // Determine if a converter is explicitly registered with the component
221: Converter converter = uiSelectMany.getConverter();
222: if (converter == null) {
223: // Determine if there is a default converter for the class
224: converter = getConverterForClass(componentType);
225: }
226: if (converter == null) {
227: // we don't need to convert base Object types
228: if (componentType.equals(Object.class)) {
229: return newSubmittedValues;
230: } else {
231: throw new ConverterException("Converter is null");
232: }
233: }
234:
235: for (int index = 0; index < numberOfValues; index++) {
236:
237: // convert the next element
238: Object nextConvertedElement = converter.getAsObject(
239: facesContext, uiSelectMany,
240: newSubmittedValues[index]);
241:
242: if (!componentType.isPrimitive()) {
243: Array.set(convertedValues, index, nextConvertedElement);
244: } else if (componentType.equals(Boolean.TYPE)) {
245:
246: Array
247: .setBoolean(convertedValues, index,
248: ((Boolean) nextConvertedElement)
249: .booleanValue());
250:
251: } else if (componentType.equals(Integer.TYPE)) {
252:
253: Array.setInt(convertedValues, index,
254: ((Integer) nextConvertedElement).intValue());
255:
256: } else if (componentType.equals(Long.TYPE)) {
257:
258: Array.setLong(convertedValues, index,
259: ((Long) nextConvertedElement).longValue());
260:
261: } else if (componentType.equals(Short.TYPE)) {
262:
263: Array.setShort(convertedValues, index,
264: ((Short) nextConvertedElement).shortValue());
265:
266: } else if (componentType.equals(Byte.TYPE)) {
267:
268: Array.setByte(convertedValues, index,
269: ((Byte) nextConvertedElement).byteValue());
270:
271: } else if (componentType.equals(Float.TYPE)) {
272:
273: Array.setFloat(convertedValues, index,
274: ((Float) nextConvertedElement).floatValue());
275:
276: } else if (componentType.equals(Double.TYPE)) {
277:
278: Array.setDouble(convertedValues, index,
279: ((Double) nextConvertedElement).doubleValue());
280:
281: } else if (componentType.equals(Character.TYPE)) {
282:
283: Array.setChar(convertedValues, index,
284: ((Character) nextConvertedElement).charValue());
285:
286: }
287: }
288: return convertedValues;
289: }
290:
291: protected void renderOption(FacesContext facesContext,
292: UIComponent uiComponent, SelectItem selectItem,
293: Element optionGroup) throws IOException {
294:
295: DOMContext domContext = DOMContext.attachDOMContext(
296: facesContext, uiComponent);
297:
298: Element select = (Element) domContext.getRootNode();
299: Element option = domContext.createElement("option");
300:
301: if (optionGroup == null) {
302: select.appendChild(option);
303: } else {
304: optionGroup.appendChild(option);
305: }
306:
307: String valueString = formatComponentValue(facesContext,
308: uiComponent, selectItem.getValue());
309: option.setAttribute("value", valueString);
310:
311: Object submittedValues[] = getSubmittedSelectedValues(uiComponent);
312: boolean isSelected;
313: if (submittedValues != null) {
314: isSelected = isSelected(valueString, submittedValues);
315: } else {
316: Object selectedValues = getCurrentSelectedValues(uiComponent);
317: isSelected = isSelected(selectItem.getValue(),
318: selectedValues);
319: }
320:
321: if (isSelected) {
322: option.setAttribute("selected", "selected");
323: }
324: if (selectItem.isDisabled()) {
325: option.setAttribute("disabled", "disabled");
326: }
327:
328: Document doc = domContext.getDocument();
329: Text labelNode = doc.createTextNode(selectItem.getLabel());
330: option.appendChild(labelNode);
331: }
332:
333: void renderSelect(FacesContext facesContext, UIComponent uiComponent)
334: throws IOException {
335: HashSet excludes = new HashSet();
336: // setup
337: DOMContext domContext = DOMContext.attachDOMContext(
338: facesContext, uiComponent);
339: if (!domContext.isInitialized()) {
340: Element root = domContext.createElement("select");
341: domContext.setRootNode(root);
342: setRootElementId(facesContext, root, uiComponent);
343: root.setAttribute("name", uiComponent
344: .getClientId(facesContext));
345: // render styleClass attribute if present.
346: String styleClass = null;
347: if (null != (styleClass = (String) uiComponent
348: .getAttributes().get("styleClass"))) {
349: root.setAttribute("class", styleClass);
350: }
351: if (!getMultipleText(uiComponent).equals("")) {
352: root.setAttribute("multiple", Boolean.TRUE.toString());
353: }
354: }
355: Element root = (Element) domContext.getRootNode();
356:
357: // Determine how many option(s) we need to render, and update
358: // the component's "size" attribute accordingly; The "size"
359: // attribute will be rendered as one of the "pass thru" attributes
360: int itemCount = countSelectOptionsRecursive(facesContext,
361: uiComponent);
362: // If "size" is *not* set explicitly, we have to default it correctly
363: Object size = uiComponent.getAttributes().get("size");
364: if ((null == size)
365: || ((size instanceof Integer) && (((Integer) size)
366: .intValue() == Integer.MIN_VALUE || ((Integer) size)
367: .intValue() == 0))) {
368: renderSizeAttribute(root, itemCount);
369: excludes.add("size");
370: }
371:
372: Object currentValue = null;
373: if (null == (currentValue = ((UIInput) uiComponent)
374: .getSubmittedValue())) {
375: currentValue = "";
376: }
377:
378: addJavaScript(facesContext, uiComponent, root, currentValue
379: .toString(), excludes);
380:
381: PassThruAttributeRenderer.renderAttributes(facesContext,
382: uiComponent, getExcludesArray(excludes));
383: excludes.clear();
384:
385: domContext.stepInto(uiComponent);
386: renderOptions(facesContext, uiComponent);
387: domContext.stepOver();
388: domContext.streamWrite(facesContext, uiComponent);
389:
390: }
391:
392: public String getEventType(UIComponent uiComponent) {
393: if (uiComponent instanceof javax.faces.component.html.HtmlSelectOneListbox) {
394: return "onchange";
395: } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectOneMenu) {
396: return "onchange";
397: } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectManyListbox) {
398: return "onchange";
399: } else if (uiComponent instanceof javax.faces.component.html.HtmlSelectManyMenu) {
400: return "onchange";
401: } else if (uiComponent instanceof HtmlSelectManyCheckbox) {
402: return "onclick";
403: }
404: return "";
405: }
406:
407: int countSelectOptionsRecursive(FacesContext facesContext,
408: UIComponent uiComponent) {
409: int counter = 0;
410: Iterator selectItems = getSelectItems(uiComponent);
411: while (selectItems.hasNext()) {
412: counter++;
413: SelectItem nextSelectItem = (SelectItem) selectItems.next();
414: if (nextSelectItem instanceof SelectItemGroup) {
415: counter += ((SelectItemGroup) nextSelectItem)
416: .getSelectItems().length;
417: }
418: }
419: return counter;
420: }
421:
422: void renderOptions(FacesContext facesContext,
423: UIComponent uiComponent) throws IOException {
424:
425: DOMContext domContext = DOMContext.attachDOMContext(
426: facesContext, uiComponent);
427:
428: Element rootSelectElement = (Element) domContext.getRootNode();
429: DOMContext.removeChildrenByTagName(rootSelectElement, "option");
430: DOMContext.removeChildrenByTagName(rootSelectElement,
431: "optgroup");
432:
433: Iterator selectItems = getSelectItems(uiComponent);
434: while (selectItems.hasNext()) {
435: SelectItem nextSelectItem = (SelectItem) selectItems.next();
436: if (nextSelectItem instanceof SelectItemGroup) {
437: Element optGroup = domContext.createElement("optgroup");
438: rootSelectElement.appendChild(optGroup);
439: optGroup.setAttribute("label", nextSelectItem
440: .getLabel());
441: domContext.setCursorParent(optGroup);
442: SelectItem[] selectItemsArray = ((SelectItemGroup) nextSelectItem)
443: .getSelectItems();
444: for (int i = 0; i < selectItemsArray.length; ++i) {
445: renderOption(facesContext, uiComponent,
446: selectItemsArray[i], optGroup);
447: }
448: } else {
449: renderOption(facesContext, uiComponent, nextSelectItem,
450: null);
451: }
452: }
453: }
454:
455: boolean isSelected(Object sentinel, Object selectedValues) {
456: boolean isSelected = false;
457: if (selectedValues == null || sentinel == null) {
458: return isSelected;
459: }
460: int length = Array.getLength(selectedValues);
461: for (int index = 0; index < length; index++) {
462: Object nextSelectedValue = Array.get(selectedValues, index);
463: if (nextSelectedValue == null && sentinel == null) {
464: isSelected = true;
465: break;
466: } else if (sentinel.equals(nextSelectedValue)) {
467: isSelected = true;
468: break;
469: } else if (sentinel instanceof String) {
470: if (isConversionMatched(sentinel.toString(),
471: nextSelectedValue)) {
472: isSelected = true;
473: break;
474: }
475: }
476: }
477: return isSelected;
478: }
479:
480: boolean isSelected(Object sentinelValue, Object[] selectedValues) {
481: boolean valueIsSelected = false;
482: if (selectedValues != null) {
483: Iterator selectedValuesIterator = Arrays.asList(
484: selectedValues).iterator();
485: while (selectedValuesIterator.hasNext()) {
486: if (selectedValuesIterator.next().equals(sentinelValue)) {
487: valueIsSelected = true;
488: break;
489: }
490: }
491: }
492: return valueIsSelected;
493: }
494:
495: /**
496: * Render "1" as the value of the size attribute
497: *
498: * @param targetElement
499: * @param size
500: * @throws IOException
501: */
502: protected void renderSizeAttribute(Element targetElement, int size)
503: throws IOException {
504: targetElement.setAttribute("size", "1");
505: }
506:
507: String getSelectedTextString() {
508: return " selected";
509: }
510:
511: // To derive a selectOne type component from this, override
512: // these methods.
513: public String getMultipleText(UIComponent component) {
514: if (component instanceof UISelectMany) {
515: return " multiple ";
516: }
517: return "";
518: }
519:
520: Object[] getSubmittedSelectedValues(UIComponent uiComponent) {
521: if (uiComponent instanceof UISelectMany) {
522: UISelectMany uiSelectMany = (UISelectMany) uiComponent;
523: return (Object[]) uiSelectMany.getSubmittedValue();
524: }
525: if (uiComponent instanceof UISelectOne) {
526: UISelectOne uiSelectOne = (UISelectOne) uiComponent;
527: Object submittedValue = uiSelectOne.getSubmittedValue();
528: if (submittedValue != null) {
529: return new Object[] { submittedValue };
530: }
531: }
532: return null;
533: }
534:
535: Object getCurrentSelectedValues(UIComponent uiComponent) {
536: Object currentSelectedValues = null;
537: if (uiComponent instanceof UISelectMany) {
538: UISelectMany uiSelectMany = (UISelectMany) uiComponent;
539: currentSelectedValues = uiSelectMany.getValue();
540: if (currentSelectedValues instanceof List) {
541: return ((List) currentSelectedValues).toArray();
542: }
543: return currentSelectedValues;
544: }
545: if (uiComponent instanceof UISelectOne) {
546: UISelectOne uiSelectOne = (UISelectOne) uiComponent;
547: currentSelectedValues = uiSelectOne.getValue();
548: if (currentSelectedValues != null) {
549: return new Object[] { currentSelectedValues };
550: }
551: }
552: return null;
553: }
554:
555: protected Iterator getSelectItems(UIComponent uiComponent) {
556:
557: List selectItems = new ArrayList();
558: Iterator children = uiComponent.getChildren().iterator();
559:
560: while (children.hasNext()) {
561: UIComponent nextSelectItemChild = (UIComponent) children
562: .next();
563: if (nextSelectItemChild instanceof UISelectItem) {
564: Object selectItemValue = ((UISelectItem) nextSelectItemChild)
565: .getValue();
566: if (selectItemValue != null
567: && selectItemValue instanceof SelectItem) {
568: selectItems.add(selectItemValue);
569: } else {
570: //If user defines only one member, either itemValue or itemLabel
571: //The default implementation throws a null pointer exception.
572: //So here we are identifying, if either itemValue or itemLabel is found,
573: //Assigned its value to the other member
574: assignDataIfNull(nextSelectItemChild);
575: selectItems.add(new SelectItem(
576: ((UISelectItem) nextSelectItemChild)
577: .getItemValue(),
578: ((UISelectItem) nextSelectItemChild)
579: .getItemLabel(),
580: ((UISelectItem) nextSelectItemChild)
581: .getItemDescription(),
582: ((UISelectItem) nextSelectItemChild)
583: .isItemDisabled()));
584: }
585: } else if (nextSelectItemChild instanceof UISelectItems) {
586: Object selectItemsValue = ((UISelectItems) nextSelectItemChild)
587: .getValue();
588:
589: if (selectItemsValue != null) {
590: if (selectItemsValue instanceof SelectItem) {
591: selectItems.add(selectItemsValue);
592: } else if (selectItemsValue instanceof Collection) {
593: Iterator selectItemsIterator = ((Collection) selectItemsValue)
594: .iterator();
595: while (selectItemsIterator.hasNext()) {
596: selectItems.add(selectItemsIterator.next());
597: }
598: } else if (selectItemsValue instanceof SelectItem[]) {
599: SelectItem selectItemArray[] = (SelectItem[]) selectItemsValue;
600: for (int i = 0; i < selectItemArray.length; i++) {
601: selectItems.add(selectItemArray[i]);
602: }
603: } else if (selectItemsValue instanceof Map) {
604: Iterator selectItemIterator = ((Map) selectItemsValue)
605: .keySet().iterator();
606: while (selectItemIterator.hasNext()) {
607: Object nextKey = selectItemIterator.next();
608: if (nextKey != null) {
609: Object nextValue = ((Map) selectItemsValue)
610: .get(nextKey);
611: if (nextValue != null) {
612: selectItems.add(new SelectItem(
613: nextValue.toString(),
614: nextKey.toString()));
615: }
616: }
617: }
618: } else if (selectItemsValue instanceof String[]) {
619: String stringItemArray[] = (String[]) selectItemsValue;
620: for (int i = 0; i < stringItemArray.length; i++) {
621: selectItems.add(new SelectItem(
622: stringItemArray[i]));
623: }
624: }
625: }
626: }
627: }
628: return selectItems.iterator();
629: }
630:
631: private void assignDataIfNull(Object selectItem) {
632: UISelectItem uiSelectItem = (UISelectItem) selectItem;
633: if (uiSelectItem.getItemValue() == null) {
634: if (uiSelectItem.getItemLabel() != null) {
635: uiSelectItem.setItemValue(uiSelectItem.getItemLabel());
636: }
637: }
638: if (uiSelectItem.getItemLabel() == null) {
639: if (uiSelectItem.getItemValue() != null) {
640: uiSelectItem.setItemLabel(uiSelectItem.getItemValue()
641: .toString());
642: }
643: }
644: }
645:
646: protected void addJavaScript(FacesContext facesContext,
647: UIComponent uiComponent, Element root, String currentValue,
648: Set excludes) {
649: }
650:
651: private boolean isConversionMatched(String sentinel,
652: Object selectedValue) {
653: boolean match = false;
654: if (selectedValue instanceof Long) {
655: if (selectedValue.equals(Long.valueOf(sentinel))) {
656: match = true;
657: }
658: } else if (selectedValue instanceof Byte) {
659: if (selectedValue.equals(Byte.valueOf(sentinel))) {
660: match = true;
661: }
662: } else if (selectedValue instanceof Integer) {
663: if (selectedValue.equals(Integer.valueOf(sentinel))) {
664: match = true;
665: }
666: } else if (selectedValue instanceof Short) {
667: if (selectedValue.equals(Short.valueOf(sentinel))) {
668: match = true;
669: }
670: } else if (selectedValue instanceof Double) {
671: if (selectedValue.equals(Double.valueOf(sentinel))) {
672: match = true;
673: }
674: } else if (selectedValue instanceof Float) {
675: if (selectedValue.equals(Float.valueOf(sentinel))) {
676: match = true;
677: }
678: } else if (selectedValue instanceof Boolean) {
679: if (selectedValue.equals(Boolean.valueOf(sentinel))) {
680: match = true;
681: }
682: }
683: return match;
684: }
685: }
|