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.Map;
045:
046: import javax.faces.FacesException;
047: import javax.faces.component.EditableValueHolder;
048: import javax.faces.component.UIComponent;
049: import javax.faces.context.FacesContext;
050: import javax.faces.context.ResponseWriter;
051: import com.sun.rave.web.ui.component.EditableList;
052: import com.sun.rave.web.ui.component.ListSelector;
053: import com.sun.rave.web.ui.theme.Theme;
054: import com.sun.rave.web.ui.theme.ThemeJavascript;
055: import com.sun.rave.web.ui.theme.ThemeStyles;
056: import com.sun.rave.web.ui.util.RenderingUtilities;
057: import com.sun.rave.web.ui.util.ThemeUtilities;
058:
059: /**
060: *
061: * @author avk
062: */
063: public class EditableListRenderer extends ListRendererBase {
064:
065: private final static boolean DEBUG = false;
066:
067: /**
068: * <p>Render the editable list component
069: *
070: * @param context <code>FacesContext</code> for the current request
071: * @param component <code>UIComponent</code> to be rendered
072: * end should be rendered
073: *
074: * @exception IOException if an input/output error occurs
075: */
076: public void encodeEnd(FacesContext context, UIComponent component)
077: throws IOException {
078:
079: if (DEBUG)
080: log("encodeEnd()"); //NOI18N
081:
082: if (component instanceof EditableList) {
083:
084: renderListComponent((EditableList) component, context,
085: getStyles(component, context));
086: } else {
087: String message = "Component " + component.toString()
088: + //NOI18N
089: " has been associated with an EditableListRenderer. "
090: + //NOI18N
091: " This renderer can only be used by components " + //NOI18N
092: " that extend com.sun.rave.web.ui.component.Selector."; //NOI18N
093: throw new FacesException(message);
094: }
095: }
096:
097: /**
098: * Retrieve user input from the UI.
099: * @param context The FacesContext of this request
100: * @param component The component associated with the renderer
101: */
102: public void decode(FacesContext context, UIComponent component) {
103:
104: if (DEBUG)
105: log("decode()");
106:
107: EditableList list = (EditableList) component;
108:
109: if (list.isReadOnly()) {
110: if (DEBUG)
111: log("component is readonly...");
112: return;
113: }
114:
115: String id = list.getClientId(context);
116: Map params = context.getExternalContext()
117: .getRequestParameterValuesMap();
118:
119: // The id for the editable list content or "select" element.
120: // If there is a parameter matching listID then
121: // there have been selections made. These are only
122: // respected when the action is remove.
123: // Do it every time, since we cannot know which action
124: // has taken place, an add or a remove.
125: //
126: String listID = id.concat(ListSelector.LIST_ID);
127: String[] selections = null;
128: Object parameters = params.get(listID);
129: if (parameters == null) {
130: selections = new String[0];
131: } else if (parameters instanceof String[]) {
132: selections = (String[]) parameters;
133: } else {
134: selections = new String[1];
135: selections[0] = parameters.toString();
136: }
137: // These are the values to remove.
138: // The submitted value for the component will be the
139: // current contents of the select element.
140: // It is important to not reference the values to
141: // remove during an add action.
142: //
143: list.setValuesToRemove(selections);
144:
145: // Always decode the list contents.
146: // This is the only place where the current values of the
147: // list are kept. This is a result of having both the
148: // add and remove buttons set with immediate == true.
149: // With immediate true, update model values does not
150: // occur. In the listbox case, validation does not occur
151: // either therefore the local value of the EditableList
152: // which is the contents of the list, is not updated
153: // either. The current value is stored only in the
154: // submittedValue during the lifecycle of a request
155: // initiated by the add or remove button.
156: // The update model values will only occur when a submit
157: // of the page has occurred.
158: // The decoded field is the hidden field rendered
159: // by the ListRenderer containing the current list contents.
160:
161: String valueID = id.concat(ListSelector.VALUE_ID);
162: super .decode(context, component, valueID);
163: }
164:
165: /**
166: * <p>This method determines whether the component should be
167: * rendered as a standalone list, or laid out together with a
168: * label that was defined as part of the component.</p>
169: *
170: * <p>A label will be rendered if either of the following is
171: * true:</p>
172: * <ul>
173: * <li>The page author defined a label facet; or</li>
174: * <li>The page author specified text in the label attribute.</li>
175: * </ul>
176: * <p>If there is a label, the component will be laid out using a
177: * HTML table. If not, the component will be rendered as a
178: * standalone HTML <tt>select</tt> element.</p>
179: * @param component The component associated with the
180: * renderer. Must be a subclass of ListSelector.
181: * @param context The FacesContext of the request
182: * @param styles A String array of styles used to render the
183: * component. The first item of the array is the name of the
184: * JavaScript method that handles change event. The second item is
185: * the style used when the list is enabled. The third style is the
186: * one to use when the list is disabled. The fourth item is the
187: * style to use for an item that is enabled, the fifth to use for
188: * an item that is disabled, and the sixth to use when the item is
189: * selected.
190: * @throws java.io.IOException if the renderer fails to write to
191: * the response
192: */
193: void renderListComponent(EditableList component,
194: FacesContext context, String[] styles) throws IOException {
195:
196: if (DEBUG)
197: log("renderListComponent()");
198:
199: if (component.isReadOnly()) {
200: UIComponent label = component.getListLabelComponent();
201: super .renderReadOnlyList(component, label, context,
202: styles[8]);
203: return;
204: }
205:
206: UIComponent headerComponent = component
207: .getFacet(EditableList.HEADER_FACET);
208: UIComponent footerComponent = component
209: .getFacet(EditableList.FOOTER_FACET);
210:
211: boolean gotHeaderOrFooter = (headerComponent != null)
212: && (footerComponent != null);
213:
214: ResponseWriter writer = context.getResponseWriter();
215: renderJavaScriptReference(component, styles[16], writer);
216:
217: super .renderOpenEncloser(component, context, "div", styles[8]);
218:
219: if (DEBUG)
220: log("layout the component");
221:
222: if (gotHeaderOrFooter) {
223:
224: writer.startElement("table", component); //NOI18N
225: writer.writeText("\n", null); //NOI18N
226:
227: if (headerComponent != null) {
228: addComponentSingleRow(component, headerComponent,
229: context);
230: }
231:
232: // New table row for the list
233:
234: writer.startElement("tr", component); //NOI18N
235: writer.writeText("\n", null); //NOI18N
236: writer.startElement("td", component); //NOI18N
237: writer.writeText("\n", null); //NOI18N
238: }
239:
240: writer.startElement("table", component); //NOI18N
241: writer.writeAttribute("class", styles[9], null); //NOI18N
242: writer.writeText("\n", null); //NOI18N
243:
244: boolean listOnTop = component.isListOnTop();
245:
246: if (listOnTop) {
247: addListRow(component, context, styles);
248: addFieldRow(component, context, styles);
249: } else {
250: addFieldRow(component, context, styles);
251: addListRow(component, context, styles);
252: }
253: writer.endElement("table"); //NOI18N
254: writer.writeText("\n", null); //NOI18N
255:
256: if (gotHeaderOrFooter) {
257:
258: writer.endElement("td"); //NOI18N
259: writer.writeText("\n", null); //NOI18N
260: writer.endElement("tr"); //NOI18N
261: writer.writeText("\n", null); //NOI18N
262:
263: if (footerComponent != null) {
264: addComponentSingleRow(component, footerComponent,
265: context);
266: }
267:
268: writer.endElement("table"); //NOI18N
269: writer.writeText("\n", null); //NOI18N
270: }
271:
272: writer.endElement("div"); //NOI18N
273:
274: renderJavaScript(component, context, writer);
275:
276: }
277:
278: /**
279: * Overrides encodeChildren of Renderer to do nothing. This
280: * renderer renders its own children, but not through this
281: * method.
282: * @param context The FacesContext of the request
283: * @param component The component associated with the
284: * renderer. Must be a subclass of ListSelector.
285: */
286: public void encodeChildren(
287: javax.faces.context.FacesContext context,
288: javax.faces.component.UIComponent component)
289: throws java.io.IOException {
290: return;
291: }
292:
293: /**
294: * <p>Renders a component in a table row.</p>
295: * @param component The component
296: * @param context The FacesContext of the request
297: * @throws java.io.IOException if the renderer fails to write to
298: * the response
299: */
300: private void addComponentSingleRow(EditableList list,
301: UIComponent component, FacesContext context)
302: throws IOException {
303:
304: ResponseWriter writer = context.getResponseWriter();
305: writer.startElement("tr", list); //NOI18N
306: writer.writeText("\n", null); //NOI18N
307: writer.startElement("td", list); //NOI18N
308: RenderingUtilities.renderComponent(component, context);
309: writer.writeText("\n", null); //NOI18N
310: // Perhaps this should depend on the dir?
311: // writer.writeAttribute("align", "left", null);
312: writer.endElement("td"); //NOI18N
313: writer.writeText("\n", null); //NOI18N
314: writer.endElement("tr"); //NOI18N
315: writer.writeText("\n", null); //NOI18N
316: }
317:
318: /**
319: * <p>Renders the list row</p>
320: * @param component The component
321: * @param context The FacesContext of the request
322: * @throws java.io.IOException if the renderer fails to write to
323: * the response
324: */
325: private void addListRow(EditableList component,
326: FacesContext context, String[] styles) throws IOException {
327:
328: // Perhaps this should depend on the dir?
329: // writer.writeAttribute("align", "left", null);
330:
331: UIComponent listLabelComponent = component
332: .getListLabelComponent();
333: UIComponent removeButtonComponent = component
334: .getRemoveButtonComponent();
335:
336: ResponseWriter writer = context.getResponseWriter();
337:
338: // Begin new row for the list label, list & remove button
339: writer.startElement("tr", component); //NOI18N
340: writer.writeText("\n", null); //NOI18N
341:
342: // Render the label in a new table cell
343: writer.startElement("td", component); //NOI18N
344: writer.writeAttribute("class", styles[13], null); //NOI18N
345: writer.writeAttribute("valign", "top", null); //NOI18N
346: writer.writeText("\n", null); //NOI18N
347: RenderingUtilities.renderComponent(listLabelComponent, context);
348: writer.endElement("td"); //NOI18N
349: writer.writeText("\n", null); //NOI18N
350:
351: // Render the textfield in a new table cell
352: writer.startElement("td", component); //NOI18N
353: writer.writeAttribute("class", styles[14], null); //NOI18N
354: writer.writeAttribute("valign", "top", null); //NOI18N
355: writer.writeText("\n", null); //NOI18N
356: super .renderHiddenValue(component, context, writer, styles[8]);
357: writer.writeText("\n", null);
358: String id = component.getClientId(context).concat(
359: ListSelector.LIST_ID);
360: super .renderList(component, id, context, styles);
361: writer.endElement("td"); //NOI18N
362: writer.writeText("\n", null); //NOI18N
363:
364: // Render the button in a new table cell
365:
366: // Create another table cell for the list and the remove buttons...
367: writer.startElement("td", component); //NOI18N
368: writer.writeAttribute("class", styles[15], null); //NOI18N
369: writer.writeAttribute("valign", "top", null); //NOI18N
370: writer.writeText("\n", null); //NOI18N
371: RenderingUtilities.renderComponent(removeButtonComponent,
372: context);
373: writer.endElement("td"); //NOI18N
374: writer.writeText("\n", null); //NOI18N
375:
376: // End the row
377: writer.endElement("tr"); //NOI18N
378: writer.writeText("\n", null); //NOI18N
379: }
380:
381: /**
382: * <p>Renders the list row</p>
383: * @param component The component
384: * @param context The FacesContext of the request
385: * @throws java.io.IOException if the renderer fails to write to
386: * the response
387: */
388: private void addFieldRow(EditableList component,
389: FacesContext context, String[] styles) throws IOException {
390:
391: // Perhaps this should depend on the dir?
392: // writer.writeAttribute("align", "left", null);
393:
394: UIComponent textfieldLabelComponent = component
395: .getFieldLabelComponent();
396: UIComponent textfieldComponent = component.getFieldComponent();
397: UIComponent addButtonComponent = component
398: .getAddButtonComponent();
399: UIComponent searchButtonComponent = component
400: .getFacet(EditableList.SEARCH_FACET);
401:
402: ResponseWriter writer = context.getResponseWriter();
403:
404: // Begin new row
405: writer.startElement("tr", component); //NOI18N
406: writer.writeText("\n", null); //NOI18N
407:
408: // Render the label
409: writer.startElement("td", component); //NOI18N
410: writer.writeAttribute("class", styles[10], null); //NOI18N
411: writer.writeText("\n", null); //NOI18N
412: RenderingUtilities.renderComponent(textfieldLabelComponent,
413: context);
414: writer.writeText("\n", null); //NOI18N
415: writer.endElement("td"); //NOI18N
416: writer.writeText("\n", null); //NOI18N
417:
418: // Create another table cell for the text field, the add
419: // button and the search button...
420: writer.startElement("td", component); //NOI18N
421: writer.writeAttribute("class", styles[11], null); //NOI18N
422: writer.writeAttribute("valign", "top", null); //NOI18N
423: writer.writeText("\n", null); //NOI18N
424: RenderingUtilities.renderComponent(textfieldComponent, context);
425: writer.writeText("\n", null); //NOI18N
426: writer.endElement("td"); //NOI18N
427: writer.writeText("\n", null); //NOI18N
428:
429: // Create another table cell for the text field, the add
430: // button and the search button...
431: writer.startElement("td", component); //NOI18N
432: writer.writeAttribute("class", styles[12], null); //NOI18N
433: writer.writeAttribute("valign", "top", null); //NOI18N
434: writer.writeText("\n", null); //NOI18N
435: RenderingUtilities.renderComponent(addButtonComponent, context);
436: writer.writeText("\n", null); //NOI18N
437: writer.endElement("td"); //NOI18N
438: writer.writeText("\n", null); //NOI18N
439:
440: if (searchButtonComponent != null) {
441: writer.startElement("td", component); //NOI18N
442: writer.writeAttribute("class", styles[12], null); //NOI18N
443: writer.writeAttribute("valign", "top", null); //NOI18N
444: writer.writeText("\n", null); //NOI18N
445: RenderingUtilities.renderComponent(searchButtonComponent,
446: context);
447: writer.writeText("\n", null); //NOI18N
448: writer.endElement("td"); //NOI18N
449: writer.writeText("\n", null); //NOI18N
450: }
451:
452: // End the row
453: writer.endElement("tr"); //NOI18N
454: writer.writeText("\n", null); //NOI18N
455: }
456:
457: private void renderJavaScriptReference(EditableList component,
458: String jsRef, ResponseWriter writer) throws IOException {
459: // We should check if this one is already printed on the
460: // page...
461: writer.writeText("\n", null);
462: writer.startElement("script", component); // NOI18N
463: writer.writeAttribute("type", "text/javascript", null); // NOI18N
464: writer.writeURIAttribute("src", jsRef, null); // NOI18N
465: writer.endElement("script"); // NOI18N
466: writer.write("\n"); // NOI18N
467: }
468:
469: private void renderJavaScript(EditableList component,
470: FacesContext context, ResponseWriter writer)
471: throws IOException {
472:
473: String jsObjectName = (String) (component
474: .getJavaScriptObjectName());
475:
476: StringBuffer jsBuffer = new StringBuffer(200);
477: jsBuffer.append("var "); //NOI18N
478: jsBuffer.append(jsObjectName.toString());
479: jsBuffer.append(" = new EditableList(\'"); //NOI18N
480: jsBuffer.append(component.getClientId(context));
481: jsBuffer.append("\');\n"); //NOI18N
482: jsBuffer.append(jsObjectName.toString());
483: jsBuffer.append(EditableList.UPDATE_BUTTONS_FUNCTION);
484: writer.writeText("\n", null); //NOI18N
485: writer.startElement("script", component); // NOI18N
486: writer.writeAttribute("type", "text/javascript", null); // NOI18N
487: writer.writeText(jsBuffer.toString(), null);
488: writer.writeText("\n", null); //NOI18N
489: writer.endElement("script"); // NOI18N
490: writer.write("\n"); // NOI18N
491: }
492:
493: /**
494: * <p>Render the appropriate element end, depending on the value of the
495: * <code>type</code> property.</p>
496: *
497: * @param context <code>FacesContext</code> for the current request
498: * @param monospace <code>UIComponent</code> if true, use the monospace
499: * styles to render the list.
500: *
501: * @exception IOException if an input/output error occurs
502: */
503: private String[] getStyles(UIComponent component,
504: FacesContext context) {
505:
506: if (DEBUG)
507: log("getStyles()");
508:
509: Theme theme = ThemeUtilities.getTheme(context);
510:
511: StringBuffer onchangeBuffer = new StringBuffer(128);
512: onchangeBuffer.append(((EditableList) component).getOnChange());
513: onchangeBuffer.append("listbox_changed('");
514: onchangeBuffer.append(component.getClientId(context));
515: onchangeBuffer.append("'); return false;");
516:
517: String[] styles = new String[17];
518: styles[0] = onchangeBuffer.toString(); //NOI18N
519: styles[1] = theme.getStyleClass(ThemeStyles.LIST);
520: styles[2] = theme.getStyleClass(ThemeStyles.LIST_DISABLED);
521: styles[3] = theme.getStyleClass(ThemeStyles.LIST_OPTION);
522: styles[4] = theme
523: .getStyleClass(ThemeStyles.LIST_OPTION_DISABLED);
524: styles[5] = theme
525: .getStyleClass(ThemeStyles.LIST_OPTION_SELECTED);
526: styles[6] = theme.getStyleClass(ThemeStyles.LIST_OPTION_GROUP);
527: styles[7] = theme
528: .getStyleClass(ThemeStyles.LIST_OPTION_SEPARATOR);
529: styles[8] = theme.getStyleClass(ThemeStyles.HIDDEN);
530: styles[9] = theme.getStyleClass(ThemeStyles.EDITABLELIST_TABLE);
531: styles[10] = theme
532: .getStyleClass(ThemeStyles.EDITABLELIST_FIELD_LABEL);
533: styles[11] = theme
534: .getStyleClass(ThemeStyles.EDITABLELIST_FIELD);
535: styles[12] = theme
536: .getStyleClass(ThemeStyles.EDITABLELIST_ADD_BUTTON);
537: styles[13] = theme
538: .getStyleClass(ThemeStyles.EDITABLELIST_LIST_LABEL);
539: styles[14] = theme.getStyleClass(ThemeStyles.EDITABLELIST_LIST);
540: styles[15] = theme
541: .getStyleClass(ThemeStyles.EDITABLELIST_REMOVE_BUTTON);
542: styles[16] = theme
543: .getPathToJSFile(ThemeJavascript.EDITABLE_LIST);
544: return styles;
545: }
546: }
|