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.Iterator;
045: import java.util.Map;
046: import java.util.Collection;
047: import java.util.Properties;
048: import java.util.StringTokenizer;
049:
050: import javax.faces.FactoryFinder;
051: import javax.faces.application.Application;
052: import javax.faces.application.ApplicationFactory;
053: import javax.faces.component.UIInput;
054: import javax.faces.component.UIComponent;
055: import javax.faces.component.ValueHolder;
056: import javax.faces.context.FacesContext;
057: import javax.faces.context.ResponseWriter;
058: import javax.faces.convert.Converter;
059: import javax.faces.convert.ConverterException;
060:
061: import com.sun.rave.web.ui.component.Checkbox;
062: import com.sun.rave.web.ui.component.Label;
063: import com.sun.rave.web.ui.component.ImageComponent;
064: import com.sun.rave.web.ui.component.RadioButton;
065: import com.sun.rave.web.ui.component.Selector;
066: import com.sun.rave.web.ui.component.RbCbSelector;
067: import com.sun.rave.web.ui.component.util.Util;
068: import com.sun.rave.web.ui.model.Option;
069: import com.sun.rave.web.ui.theme.Theme;
070: import com.sun.rave.web.ui.theme.ThemeStyles;
071: import com.sun.rave.web.ui.renderer.RadioButtonGroupRenderer; // for javadoc
072: import com.sun.rave.web.ui.renderer.CheckboxGroupRenderer; // for javadoc
073: import com.sun.rave.web.ui.renderer.RowColumnRenderer; // for javadoc
074: import com.sun.rave.web.ui.util.ConversionUtilities;
075: import com.sun.rave.web.ui.util.RenderingUtilities;
076:
077: /**
078: * The <code>SelectorGroupRenderer</code> is the abstract base class for
079: * {@link RadioButtonGroupRenderer} and
080: * {@link CheckboxGroupRenderer}. It provides
081: * common behavior for driving the
082: * {@link RowColumnRenderer} superclass.
083: * Its <code>decode</code> method provides decoding for both
084: * <code>CheckboxGroup</code>
085: * and <code>RadioButtonGroup</code> components. It decodes the value as a
086: * <code>String[]</code> for both components. For <code>CheckboxGroup</code>
087: * components this an array of possibly 0 or more elements. For
088: * the <code>RadioButtonGroup</code> component the value is decoded as a
089: * <code>String[]</code> of at least one element.
090: */
091: abstract class SelectorGroupRenderer extends RowColumnRenderer {
092:
093: private static final String LABELFACTORY = "com.sun.rave.web.ui.component.util.factories.LabelFactory"; //NOI18N
094:
095: /**
096: * The define constant indicating the style class
097: * for the top level TABLE element.
098: */
099: protected final static int GRP = 0;
100: /**
101: * The define constant indicating the style class
102: * for the CSS table CAPTION (a CELL element).
103: */
104: protected final static int GRP_CAPTION = 1;
105: /**
106: * The define constant indicating the style class
107: * for a disabled CSS table CAPTION (a LABEL) element.
108: */
109: protected final static int GRP_LABEL = 2;
110: /**
111: * The define constant indicating the style class
112: * for a disabled CSS table CAPTION (a LABEL) element.
113: */
114: protected final static int GRP_LABEL_DIS = 3;
115: /**
116: * The define constant indicating the style class
117: * for the even rows.
118: */
119: protected final static int GRP_ROW_EVEN = 4;
120: /**
121: * The define constant indicating the style class
122: * for the odd rows.
123: */
124: protected final static int GRP_ROW_ODD = 5;
125: /**
126: * The define constant indicating the style class
127: * for the even cells.
128: */
129: protected final static int GRP_CELL_EVEN = 6;
130: /**
131: * The define constant indicating the style class
132: * for the odd cells.
133: */
134: protected final static int GRP_CELL_ODD = 7;
135: /**
136: * The define constant indicating the style class
137: * for an INPUT element.
138: */
139: protected final static int INPUT = 8;
140: /**
141: * The define constant indicating the style class
142: * for a disabled INPUT element.
143: */
144: protected final static int INPUT_DIS = 9;
145: /**
146: * The define constant indicating the style class
147: * for the LABEL element.
148: */
149: protected final static int LABEL = 10;
150: /**
151: * The define constant indicating the style class
152: * for a disabled LABEL element.
153: */
154: protected final static int LABEL_DIS = 11;
155: /**
156: * The define constant indicating the style class
157: * for the IMG element.
158: */
159: protected final static int IMAGE = 12;
160: /**
161: * The define constant indicating the style class
162: * for a disabled IMG element.
163: */
164: protected final static int IMAGE_DIS = 13;
165: /**
166: * The define constant indicating the label level style class
167: * for a LABEL element.
168: */
169: protected final static int LABEL_LVL1 = 14;
170: /**
171: * The define constant indicating the label level style class
172: * for a LABEL element.
173: */
174: protected final static int LABEL_LVL2 = 15;
175: /**
176: * The define constant indicating the label level style class
177: * for a LABEL element.
178: */
179: protected final static int LABEL_LVL3 = 16;
180: /**
181: * The define constant indicating the default label level style class
182: * for a LABEL element.
183: */
184: protected final static int LABEL_LVL_DEF = 0;
185:
186: private final static int LABEL_LVL = IMAGE_DIS;
187:
188: /**
189: * Creates a new instance of SelectorGroupRenderer
190: */
191: public SelectorGroupRenderer() {
192: super ();
193: }
194:
195: /**
196: * Return style constants for the controls in the group.
197: * The getStyles method is implemented by subclasses
198: * to return a <code>String[]</code> of style constants as
199: * defined in <code>ThemeStyles</code>
200: * in an order defined by the constants in this class.
201: */
202: protected abstract String[] getStyles();
203:
204: /**
205: * Implemented in the subclass to return the UIComponent for a
206: * control in the group.
207: *
208: * @param context FacesContext for the request we are processing.
209: * @param component The RadioButtonGroup component to be decoded.
210: * @param theme Theme for the request we are processing.
211: * @param id the new component's id.
212: * @param option the <code>Option</code> being rendered.
213: */
214: protected abstract UIComponent getSelectorComponent(
215: FacesContext context, UIComponent component, Theme theme,
216: String id, Option option);
217:
218: /**
219: * Decode the <code>RadioButtonGroup</code> or
220: * <code>CheckboxGroup</code> selection.
221: * If the component clientId is found as a request parameter, which is
222: * rendered as the value of the <code>name</code> attribute of
223: * the INPUT elements of type radio or checkbox, the <code>String[]</code>
224: * value is assigned as the submitted value on the component.
225: * <p>
226: * In the case of a <code>CheckboxGroup</code> component the array may
227: * have zero or more elements. In the case of <code>RadioButtonGroup</code>
228: * there is always only one element.
229: * </p>
230: * <p>
231: * If the component clientId is not found as a request parameter a
232: * <code>String[0]</code> is assigned as the submitted value,
233: * meaning that this is a <code>CheckboxGroup</code> component with no
234: * check boxes selected.
235: *
236: * @param context FacesContext for the request we are processing.
237: * @param component The RadioButtonGroup component to be decoded.
238: */
239: public void decode(FacesContext context, UIComponent component) {
240:
241: if (context == null || component == null) {
242: throw new NullPointerException();
243: }
244:
245: if (isDisabled(component) || isReadOnly(component)) {
246: return;
247: }
248:
249: setSubmittedValues(context, component);
250: }
251:
252: private void setSubmittedValues(FacesContext context,
253: UIComponent component) {
254:
255: String clientId = component.getClientId(context);
256:
257: Map requestParameterValuesMap = context.getExternalContext()
258: .getRequestParameterValuesMap();
259:
260: // If the clientId is found some controls are checked
261: //
262: if (requestParameterValuesMap.containsKey(clientId)) {
263: String[] newValues = (String[]) requestParameterValuesMap
264: .get(clientId);
265:
266: ((UIInput) component).setSubmittedValue(newValues);
267: return;
268: }
269: // Return if there are no disabledCheckedValues and there
270: // were no controls checked
271: //
272: ((UIInput) component).setSubmittedValue(new String[0]);
273: return;
274: }
275:
276: /**
277: * Render the child components of this UIComponent, following the rules
278: * described for encodeBegin() to acquire the appropriate value to be
279: * rendered. This method will only be called if the rendersChildren property
280: * of this component is true.
281: *
282: * @param context FacesContext for the request we are processing.
283: * @param component UIComponent to be decoded.
284: */
285: public void encodeChildren(FacesContext context,
286: UIComponent component) throws IOException {
287: }
288:
289: /**
290: * Called from the renderEnd method of the subclass to begin
291: * rendering the component.
292: *
293: * @param context FacesContext for the request we are processing.
294: * @param component UIComponent to be decoded.
295: * @param writer <code>ResponseWriter</code> to which the HTML will
296: * be output
297: * @param columns the number of columns to use when rendering the controls
298: */
299: protected void renderSelectorGroup(FacesContext context,
300: UIComponent component, Theme theme, ResponseWriter writer,
301: int columns) throws IOException {
302:
303: // If there are more items than columns, render additional rows.
304: //
305: Selector selector = (Selector) component;
306:
307: // See if we are rendering null for nothing selected
308: //
309: Object selected = selector.getSelected();
310:
311: // If the submittedValue is null record the rendered value
312: // If the submittedValue is not null then it contains the
313: // String values of the selected controls.
314: // If the submittedValue is not null but zero in length
315: // then nothing is selected. Assume that the component still
316: // has the appropriate rendered state.
317: //
318: if (selector.getSubmittedValue() == null) {
319: ConversionUtilities.setRenderedValue(component, selected);
320: }
321:
322: // If there aren't any items don't render anything
323: //
324: Option[] items = getItems((Selector) component);
325: if (items == null) {
326: return;
327: }
328: int length = items.length;
329: if (length == 0) {
330: return;
331: }
332:
333: columns = columns <= 0 ? 1 : (columns > length ? length
334: : columns);
335: int rows = (length + (columns - 1)) / columns;
336:
337: // Render the table layout
338: renderRowColumnLayout(context, component, theme, writer, rows,
339: columns);
340: }
341:
342: // Should be in component
343: //
344: protected Option[] getItems(Selector selector) {
345: Object items = selector.getItems();
346: if (items == null) {
347: return null;
348: } else if (items instanceof Option[]) {
349: return (Option[]) items;
350: } else if (items instanceof Map) {
351: int size = ((Map) items).size();
352: return (Option[]) ((Map) items).values().toArray(
353: new Option[size]);
354: } else if (items instanceof Collection) {
355: int size = ((Collection) items).size();
356: return (Option[]) ((Collection) items)
357: .toArray(new Option[size]);
358: } else {
359: throw new IllegalArgumentException(
360: "Selector.items is not Option[], Map, or Collection");
361: }
362: }
363:
364: protected void renderCellContent(FacesContext context,
365: UIComponent component, Theme theme, ResponseWriter writer,
366: int itemN) throws IOException {
367:
368: Option[] items = getItems((Selector) component);
369: if (itemN >= items.length) {
370: renderEmptyCell(context, component, theme, writer);
371: return;
372: }
373:
374: String id = component.getId().concat("_") + itemN; //NOI18N
375: UIComponent content = getSelectorComponent(context, component,
376: theme, id, items[itemN]);
377: RenderingUtilities.renderComponent(content, context);
378: }
379:
380: /**
381: * Called by the RowColumnRenderer superclass when the
382: * group label should be rendered.
383: *
384: * @param context FacesContext for the request we are processing.
385: * @param component UIComponent to be decoded.
386: * @param theme Theme for the request we are processing.
387: * @param writer <code>ResponseWriter</code> to which the HTML will
388: * be output
389: */
390: protected void renderCaption(FacesContext context,
391: UIComponent component, Theme theme, ResponseWriter writer)
392: throws IOException {
393:
394: UIComponent captionComponent = getCaptionComponent(context,
395: component, theme, component.getId().concat("_caption")); //NOI18N
396: if (captionComponent != null) {
397: RenderingUtilities.renderComponent(captionComponent,
398: context);
399: }
400: }
401:
402: private UIComponent getCaptionComponent(FacesContext context,
403: UIComponent component, Theme theme, String captionId)
404: throws IOException {
405:
406: // Check if the page author has defined a label facet
407: //
408: // What if the component is readonly ? Do we need to modify
409: // the facet to be readonly, disabled, or required ?
410: // What about styles, etc.
411: //
412: UIComponent labelComponent = component.getFacet("label"); //NOI18N
413: if (labelComponent != null) {
414: return labelComponent;
415: }
416:
417: // If we find a label, define a label component
418: //
419: String attrvalue = (String) component.getAttributes().get(
420: "label"); //NOI18N
421: if (attrvalue == null || attrvalue.length() <= 0) {
422: return null;
423: }
424:
425: Properties props = new Properties();
426: labelComponent = Util.getChild(component, captionId,
427: LABELFACTORY, props);
428:
429: Map labelAttrs = labelComponent.getAttributes();
430:
431: labelAttrs.put("text", attrvalue); //NOI18N
432: // Set the for attribute for the first control ?
433: //
434: labelAttrs.put("for", //NOI18N
435: component.getClientId(context).concat("_0_input")); //NOI18N
436: // This causes a back and forth conversion
437: // from boolean to String to boolean
438: //
439: Boolean required = (Boolean) component.getAttributes().get(
440: "required"); //NOI18N
441: if (attrvalue != null) {
442: labelAttrs.put("required", required); //NOI18N
443: }
444:
445: attrvalue = (String) component.getAttributes().get("accesskey"); //NOI18N
446: if (attrvalue != null) {
447: labelAttrs.put("accesskey", attrvalue); //NOI18N
448: }
449:
450: // Give the group's tooltip to the group label
451: //
452: attrvalue = (String) component.getAttributes().get("toolTip"); //NOI18N
453: if (attrvalue != null) {
454: labelAttrs.put("toolTip", attrvalue); //NOI18N
455: }
456:
457: Integer lblLvl = (Integer) component.getAttributes().get(
458: "labelLevel"); //NOI18N
459:
460: // Need to synch up defaults
461: //
462: if (lblLvl == null) {
463: lblLvl = new Integer(2);
464: }
465:
466: // This does not work because the component interface for
467: // {get,set}LabelLevel is int primitive and the Factories
468: // convert to Integer.
469: //
470: //props.setProperty("labelLevel", String.valueOf(styleLevel)); //NOI18N
471: labelAttrs.put("labelLevel", lblLvl); //NOI18N
472:
473: int styleCode = GRP_LABEL;
474: Boolean disabled = (Boolean) component.getAttributes().get(
475: "disabled"); //NOI18N
476: if (disabled != null && disabled.booleanValue() == true) {
477: labelAttrs.put("disabled", disabled); //NOI18N
478: styleCode = GRP_LABEL_DIS;
479: }
480:
481: String captionStyle = getStyle(theme, styleCode);
482: if (captionStyle != null) {
483: labelAttrs.put("styleClass", captionStyle); //NOI18N
484: }
485:
486: return labelComponent;
487: }
488:
489: /**
490: * Called from the <code>renderCellContent</code> method implemented
491: * in the sublclass when there are no more controls to render.
492: *
493: * @param context FacesContext for the request we are processing.
494: * @param component UIComponent to be decoded.
495: * @param theme Theme for the request we are processing.
496: * @param writer <code>ResponseWriter</code> to which the HTML will
497: * be output
498: */
499: protected void renderEmptyCell(FacesContext context,
500: UIComponent component, Theme theme, ResponseWriter writer)
501: throws IOException {
502:
503: /*
504: writer.writeText(" ", null); //NOI18N
505: */
506: }
507:
508: // Structural styles
509: //
510: private int[] rowcolstyle = { GRP, GRP_CAPTION, GRP_ROW_EVEN,
511: GRP_ROW_ODD, GRP_CELL_EVEN, GRP_CELL_ODD };
512:
513: /**
514: * Pass on the style request from the
515: * {@link com.sun.rave.web.ui.renderer.RowColumnRenderer} to
516: * the <code>SelectorGroupRenderer</code> subclass.
517: *
518: * @param theme Theme for the request we are processing.
519: * @param styleCode the desired style class constant
520: */
521: protected final String getRowColumnStyle(Theme theme, int styleCode) {
522: return getStyle(theme, rowcolstyle[styleCode]);
523: }
524:
525: /**
526: * Return the style class name for the structural element indicated
527: * by <code>styleCode</code>
528: *
529: * @param styleCode identifies the style class for the element about
530: * to be rendered.
531: */
532: private String getStyle(Theme theme, int styleCode) {
533: String style = null;
534: try {
535: style = theme.getStyleClass(getStyles()[styleCode]);
536: } catch (Exception e) {
537: // Don't care
538: }
539: return style;
540: }
541:
542: /**
543: * Return the style class name and level for the structural element
544: * indicated by <code>styleCode</code>
545: *
546: * @param styleCode identifies the style class for the element about
547: * to be rendered.
548: * @param styleLevelCode identifies the style class level for the
549: * element about to be rendered.
550: */
551: protected String getStyle(Theme theme, int styleCode,
552: int styleLevelCode) {
553:
554: String style = getStyle(theme, styleCode);
555: if (style == null) {
556: return null;
557: }
558:
559: StringBuffer styleBuf = new StringBuffer(style);
560:
561: String styleLevel = null;
562: if (styleLevelCode != LABEL_LVL_DEF) {
563: styleLevel = getStyle(theme, styleLevelCode);
564: }
565:
566: // No style code for the desired one, get the default
567: //
568: if (styleLevel != null) {
569: if (styleBuf.length() != 0) {
570: styleBuf.append(" "); //NOI18N
571: }
572: styleBuf.append(styleLevel);
573: } else {
574: style = null;
575: switch (styleCode) {
576: case GRP_CAPTION:
577: style = getStyle(theme, LABEL_LVL2);
578: break;
579: case LABEL:
580: style = getStyle(theme, LABEL_LVL3);
581: break;
582: }
583: if (style != null) {
584: if (styleBuf.length() != 0) {
585: styleBuf.append(" "); //NOI18N
586: }
587: styleBuf.append(style);
588: }
589: }
590: return styleBuf.toString();
591: }
592:
593: //<RAVE>
594: //mbohm 6300361,6300362
595: //transfer event attributes from a radiobuttongroup/checkboxgroup to a
596: //radiobutton/checkbox
597: protected void transferEventAttributes(Selector group,
598: RbCbSelector rbcb) {
599: Map groupAttributes = group.getAttributes();
600: Map rbcbAttributes = rbcb.getAttributes();
601: final String[] eventAttributeNames = AbstractRenderer.EVENTS_ATTRIBUTES;
602: for (int i = 0; i < eventAttributeNames.length; i++) {
603: Object eventAttributeValue = groupAttributes
604: .get(eventAttributeNames[i]);
605: if (eventAttributeValue != null) {
606: rbcbAttributes.put(eventAttributeNames[i],
607: eventAttributeValue);
608: }
609: }
610: }
611: //</RAVE>
612: }
|