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 com.sun.data.provider.SortCriteria;
044: import com.sun.rave.web.ui.component.Alarm;
045: import com.sun.rave.web.ui.component.TableColumn;
046: import com.sun.rave.web.ui.component.TableRowGroup;
047: import com.sun.rave.web.ui.theme.Theme;
048: import com.sun.rave.web.ui.theme.ThemeStyles;
049: import com.sun.rave.web.ui.util.LogUtil;
050: import com.sun.rave.web.ui.util.RenderingUtilities;
051: import com.sun.rave.web.ui.util.ThemeUtilities;
052:
053: import java.io.IOException;
054: import java.util.Iterator;
055:
056: import javax.faces.component.UIComponent;
057: import javax.faces.context.FacesContext;
058: import javax.faces.context.ResponseWriter;
059: import javax.faces.render.Renderer;
060:
061: /**
062: * This class renders TableColumn components.
063: * <p>
064: * The tableColumn component provides a layout mechanism for displaying columns
065: * of data. UI guidelines describe specific behavior that can applied to the
066: * rows and columns of data such as sorting, filtering, pagination, selection,
067: * and custom user actions. In addition, UI guidelines also define sections of
068: * the table that can be used for titles, row group headers, and placement of
069: * pre-defined and user defined actions.
070: * </p><p>
071: * Note: Column headers and footers are rendered by TableRowGroupRenderer. Table
072: * column footers are rendered by TableRenderer.
073: * </p><p>
074: * Note: To see the messages logged by this class, set the following global
075: * defaults in your JDK's "jre/lib/logging.properties" file.
076: * </p><p><pre>
077: * java.util.logging.ConsoleHandler.level = FINE
078: * com.sun.rave.web.ui.renderer.TableColumnRenderer.level = FINE
079: * </pre></p><p>
080: * See TLD docs for more information.
081: * </p>
082: */
083: public class TableColumnRenderer extends Renderer {
084: /**
085: * The set of String pass-through attributes to be rendered.
086: * <p>
087: * Note: The WIDTH, HEIGHT, and BGCOLOR attributes are all deprecated (in
088: * the HTML 4.0 spec) in favor of style sheets. In addition, the DIR and
089: * LANG attributes are not cuurently supported.
090: * </p>
091: */
092: private static final String stringAttributes[] = { "abbr", //NOI18N
093: "axis", //NOI18N
094: "bgColor", //NOI18N
095: "char", //NOI18N
096: "charOff", //NOI18N
097: "dir", //NOI18N
098: "headers", //NOI18N
099: "height", //NOI18N
100: "lang", //NOI18N
101: "onClick", //NOI18N
102: "onDblClick", //NOI18N
103: "onKeyDown", //NOI18N
104: "onKeyPress", //NOI18N
105: "onKeyUp", //NOI18N
106: "onMouseDown", //NOI18N
107: "onMouseUp", //NOI18N
108: "onMouseMove", //NOI18N
109: "onMouseOut", //NOI18N
110: "onMouseOver", //NOI18N
111: "style", //NOI18N
112: "valign", //NOI18N
113: "width" }; //NOI18N
114:
115: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
116: // Renderer methods
117: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118:
119: /**
120: * Render the beginning of the specified UIComponent to the output stream or
121: * writer associated with the response we are creating.
122: *
123: * @param context FacesContext for the current request.
124: * @param component UIComponent to be rendered.
125: *
126: * @exception IOException if an input/output error occurs.
127: * @exception NullPointerException if context or component is null.
128: */
129: public void encodeBegin(FacesContext context, UIComponent component)
130: throws IOException {
131: if (context == null || component == null) {
132: log("encodeBegin", //NOI18N
133: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
134: throw new NullPointerException();
135: }
136: if (!component.isRendered()) {
137: log("encodeBegin",
138: "Component not rendered, nothing to display"); //NOI18N
139: return;
140: }
141:
142: // Don't render TableColumn parents.
143: TableColumn col = (TableColumn) component;
144: Iterator kids = col.getTableColumnChildren();
145: if (!kids.hasNext()) {
146: ResponseWriter writer = context.getResponseWriter();
147: renderEnclosingTagStart(context, col, writer);
148: }
149: }
150:
151: /**
152: * Render the children of the specified UIComponent to the output stream or
153: * writer associated with the response we are creating.
154: *
155: * @param context FacesContext for the current request.
156: * @param component UIComponent to be decoded.
157: *
158: * @exception IOException if an input/output error occurs.
159: * @exception NullPointerException if context or component is null.
160: */
161: public void encodeChildren(FacesContext context,
162: UIComponent component) throws IOException {
163: if (context == null || component == null) {
164: log("encodeChildren", //NOI18N
165: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
166: throw new NullPointerException();
167: }
168: if (!component.isRendered()) {
169: log("encodeChildren",
170: "Component not rendered, nothing to display"); //NOI18N
171: return;
172: }
173:
174: // Don't render TableColumn parents.
175: TableColumn col = (TableColumn) component;
176: Iterator kids = col.getTableColumnChildren();
177: if (kids.hasNext()) {
178: while (kids.hasNext()) {
179: TableColumn kid = (TableColumn) kids.next();
180: if (!kid.isRendered()) {
181: log("encodeChildren", //NOI18N
182: "TableColumn not rendered, nothing to display"); //NOI18N
183: continue;
184: }
185: RenderingUtilities.renderComponent(kid, context);
186: }
187: } else {
188: // Render empty cell image. Don't do this automatically because we
189: // cannot reconize truly null values, such as an empty alarm cell or a
190: // comment field which is blank, neither of which should have the dash
191: // image. Further, the dash image is not used for cells that contain
192: // user interface elements such as checkboxes or drop-down lists when
193: // these elements are not applicable. Instead, the elements are simply
194: // not displayed.
195: if (col.isEmptyCell()) {
196: RenderingUtilities.renderComponent(col
197: .getEmptyCellIcon(), context);
198: } else {
199: // Render children.
200: kids = col.getChildren().iterator();
201: while (kids.hasNext()) {
202: UIComponent kid = (UIComponent) kids.next();
203: if (!kid.isRendered()) {
204: log("encodeChildren", //NOI18N
205: "TableColumn child not rendered, nothing to display"); //NOI18N
206: continue;
207: }
208: RenderingUtilities.renderComponent(kid, context);
209:
210: // Render a separator bar between each child component.
211: if (kids.hasNext() && col.isEmbeddedActions()) {
212: RenderingUtilities.renderComponent(col
213: .getEmbeddedActionSeparatorIcon(),
214: context);
215: ResponseWriter writer = context
216: .getResponseWriter();
217: writer.writeText(" ", null); //NOI18N
218: }
219: }
220: }
221: }
222: }
223:
224: /**
225: * Render the ending of the specified UIComponent to the output stream or
226: * writer associated with the response we are creating.
227: *
228: * @param context FacesContext for the current request.
229: * @param component UIComponent to be rendered.
230: *
231: * @exception IOException if an input/output error occurs.
232: * @exception NullPointerException if context or component is null.
233: */
234: public void encodeEnd(FacesContext context, UIComponent component)
235: throws IOException {
236: if (context == null || component == null) {
237: log("encodeEnd", //NOI18N
238: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
239: throw new NullPointerException();
240: }
241: if (!component.isRendered()) {
242: log("encodeEnd",
243: "Component not rendered, nothing to display"); //NOI18N
244: return;
245: }
246:
247: // Don't render TableColumn parents.
248: TableColumn col = (TableColumn) component;
249: Iterator kids = col.getTableColumnChildren();
250: if (!kids.hasNext()) {
251: ResponseWriter writer = context.getResponseWriter();
252: renderEnclosingTagEnd(context, col, writer);
253: }
254: }
255:
256: /**
257: * Return a flag indicating whether this Renderer is responsible
258: * for rendering the children the component it is asked to render.
259: * The default implementation returns false.
260: */
261: public boolean getRendersChildren() {
262: return true;
263: }
264:
265: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
266: // Enclosing tag methods
267: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
268:
269: /**
270: * Render enclosing tag for TableColumn components.
271: *
272: * @param context FacesContext for the current request.
273: * @param component TableColumn to be rendered.
274: * @param writer ResponseWriter to which the component should be rendered.
275: *
276: * @exception IOException if an input/output error occurs.
277: */
278: protected void renderEnclosingTagStart(FacesContext context,
279: TableColumn component, ResponseWriter writer)
280: throws IOException {
281: if (component == null) {
282: log("renderEnclosingTagStart", //NOI18N
283: "Cannot render enclosing tag, UIComponent is null"); //NOI18N
284: return;
285: }
286:
287: // Note: Row header is not valid for select column.
288: boolean isRowHeader = component.isRowHeader()
289: && component.getSelectId() == null;
290:
291: writer.writeText("\n", null); //NOI18N
292: writer.startElement(isRowHeader ? "th" : "td", component); //NOI18N
293:
294: // Render client id.
295: writer.writeAttribute("id", component.getClientId(context),
296: null); //NOI18N
297:
298: // Render style class.
299: RenderingUtilities.renderStyleClass(context, writer, component,
300: getStyleClass(component));
301:
302: // Render align.
303: if (component.getAlign() != null) {
304: writer.writeAttribute("align", component.getAlign(), null); //NOI18N
305: }
306:
307: // Render scope.
308: if (isRowHeader) {
309: writer.writeAttribute("scope", "row", null); //NOI18N
310: } else if (component.getScope() != null) {
311: writer.writeAttribute("scope", component.getScope(), null); //NOI18N
312: }
313:
314: // Render colspan.
315: if (component.getColSpan() > -1) {
316: writer.writeAttribute("colspan", //NOI18N
317: Integer.toString(component.getColSpan()), null); //NOI18N
318: }
319:
320: // Render rowspan.
321: if (component.getRowSpan() > -1) {
322: writer.writeAttribute("rowspan", //NOI18N
323: Integer.toString(component.getRowSpan()), null); //NOI18N
324: }
325:
326: // Render nowrap.
327: if (component.isNoWrap()) {
328: writer.writeAttribute("nowrap", "nowrap", null); //NOI18N
329: }
330:
331: // Render tooltip.
332: if (component.getToolTip() != null) {
333: writer.writeAttribute("title", component.getToolTip(),
334: "toolTip"); //NOI18N
335: }
336:
337: // Render pass through attributes.
338: RenderingUtilities.writeStringAttributes(component, writer,
339: stringAttributes);
340: }
341:
342: /**
343: * Render enclosing tag for TableColumn components.
344: *
345: * @param context FacesContext for the current request.
346: * @param component TableColumn to be rendered.
347: * @param writer ResponseWriter to which the component should be rendered.
348: *
349: * @exception IOException if an input/output error occurs.
350: */
351: protected void renderEnclosingTagEnd(FacesContext context,
352: TableColumn component, ResponseWriter writer)
353: throws IOException {
354: if (component == null) {
355: log("renderEnclosingTagEnd", //NOI18N
356: "Cannot render enclosing tag, UIComponent is null"); //NOI18N
357: return;
358: }
359:
360: // Note: Row header is not valid for select column.
361: boolean isRowHeader = component.isRowHeader()
362: && component.getSelectId() == null;
363:
364: writer.endElement(isRowHeader ? "th" : "td"); //NOI18N
365: }
366:
367: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
368: // Private methods
369: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
370:
371: /**
372: * Helper method to get column style class for TableColumn components.
373: *
374: * @param component TableColumn to be rendered
375: *
376: * @return The style class.
377: */
378: private String getStyleClass(TableColumn component) {
379: String styleClass = null;
380: if (component == null) {
381: log("getStyleClass", //NOI18N
382: "Cannot obtain style class, TableColumn is null"); //NOI18N
383: return styleClass;
384: }
385:
386: // Get sort level.
387: TableRowGroup group = component.getTableRowGroupAncestor();
388: SortCriteria criteria = component.getSortCriteria();
389: int level = (group != null) ? group.getSortLevel(criteria) : -1;
390:
391: // Get style class.
392: if (component.isSpacerColumn()) {
393: styleClass = ThemeStyles.TABLE_TD_SPACER;
394: } else if (component.getSeverity() != null
395: && !component.getSeverity().equals(Alarm.SEVERITY_OK)) {
396: styleClass = ThemeStyles.TABLE_TD_ALARM;
397: } else if (level == 1) {
398: if (component.getSelectId() != null) {
399: styleClass = ThemeStyles.TABLE_TD_SELECTCOL_SORT;
400: } else {
401: styleClass = ThemeStyles.TABLE_TD_SORT;
402: }
403: } else {
404: if (component.getSelectId() != null) {
405: styleClass = ThemeStyles.TABLE_TD_SELECTCOL;
406: } else {
407: styleClass = ThemeStyles.TABLE_TD_LAYOUT;
408: }
409: }
410: return getTheme().getStyleClass(styleClass);
411: }
412:
413: /** Helper method to get Theme objects. */
414: private Theme getTheme() {
415: return ThemeUtilities.getTheme(FacesContext
416: .getCurrentInstance());
417: }
418:
419: /**
420: * Log fine messages.
421: */
422: private void log(String method, String message) {
423: // Get class.
424: Class clazz = this .getClass();
425: if (LogUtil.fineEnabled(clazz)) {
426: // Log method name and message.
427: LogUtil.fine(clazz, clazz.getName() + "." + method + ": "
428: + message); //NOI18N
429: }
430: }
431: }
|