0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package com.sun.rave.web.ui.renderer;
0042:
0043: import com.sun.data.provider.RowKey;
0044: import com.sun.rave.web.ui.component.Table;
0045: import com.sun.rave.web.ui.component.TableColumn;
0046: import com.sun.rave.web.ui.component.TableHeader;
0047: import com.sun.rave.web.ui.component.TableRowGroup;
0048: import com.sun.rave.web.ui.theme.Theme;
0049: import com.sun.rave.web.ui.theme.ThemeStyles;
0050: import com.sun.rave.web.ui.util.LogUtil;
0051: import com.sun.rave.web.ui.util.RenderingUtilities;
0052: import com.sun.rave.web.ui.util.ThemeUtilities;
0053:
0054: import java.io.IOException;
0055: import java.util.ArrayList;
0056: import java.util.HashMap;
0057: import java.util.Iterator;
0058: import java.util.List;
0059: import java.util.Map;
0060:
0061: import javax.faces.component.NamingContainer;
0062: import javax.faces.component.UIComponent;
0063: import javax.faces.context.FacesContext;
0064: import javax.faces.context.ResponseWriter;
0065: import javax.faces.render.Renderer;
0066:
0067: /**
0068: * This class renders TableRowGroup components.
0069: * <p>
0070: * The TableRowGroup component provides a layout mechanism for displaying rows
0071: * of data. UI guidelines describe specific behavior that can applied to the
0072: * rows and columns of data such as sorting, filtering, pagination, selection,
0073: * and custom user actions. In addition, UI guidelines also define sections of
0074: * the table that can be used for titles, row group headers, and placement of
0075: * pre-defined and user defined actions.
0076: * </p><p>
0077: * Note: Column headers and footers are rendered by TableRowGroupRenderer. Table
0078: * column footers are rendered by TableRenderer.
0079: * </p><p>
0080: * Note: To see the messages logged by this class, set the following global
0081: * defaults in your JDK's "jre/lib/logging.properties" file.
0082: * </p><p><pre>
0083: * java.util.logging.ConsoleHandler.level = FINE
0084: * com.sun.rave.web.ui.renderer.TableRowGroupRenderer.level = FINE
0085: * </pre></p><p>
0086: * See TLD docs for more information.
0087: * </p>
0088: */
0089: public class TableRowGroupRenderer extends Renderer {
0090: /**
0091: * The set of String pass-through attributes to be rendered.
0092: * <p>
0093: * Note: The BGCOLOR attribute is deprecated (in the HTML 4.0 spec) in favor
0094: * of style sheets. In addition, the DIR and LANG attributes are not
0095: * cuurently supported.
0096: * </p>
0097: */
0098: private static final String stringAttributes[] = { "align", //NOI18N
0099: "bgColor", //NOI18N
0100: "char", //NOI18N
0101: "charOff", //NOI18N
0102: "dir", //NOI18N
0103: "lang", //NOI18N
0104: "onClick", //NOI18N
0105: "onDblClick", //NOI18N
0106: "onKeyDown", //NOI18N
0107: "onKeyPress", //NOI18N
0108: "onKeyUp", //NOI18N
0109: "onMouseDown", //NOI18N
0110: "onMouseUp", //NOI18N
0111: "onMouseMove", //NOI18N
0112: "onMouseOut", //NOI18N
0113: "onMouseOver", //NOI18N
0114: "style", //NOI18N
0115: "valign" }; //NOI18N
0116:
0117: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0118: // Renderer methods
0119: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0120:
0121: /**
0122: * Render the beginning of the specified UIComponent to the output stream or
0123: * writer associated with the response we are creating.
0124: *
0125: * @param context FacesContext for the current request.
0126: * @param component UIComponent to be rendered.
0127: *
0128: * @exception IOException if an input/output error occurs.
0129: * @exception NullPointerException if context or component is null.
0130: */
0131: public void encodeBegin(FacesContext context, UIComponent component)
0132: throws IOException {
0133: if (context == null || component == null) {
0134: log("encodeBegin", //NOI18N
0135: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
0136: throw new NullPointerException();
0137: }
0138: if (!component.isRendered()) {
0139: log("encodeBegin",
0140: "Component not rendered, nothing to display"); //NOI18N
0141: return;
0142: }
0143:
0144: TableRowGroup group = (TableRowGroup) component;
0145: ResponseWriter writer = context.getResponseWriter();
0146:
0147: // Render group and column headers.
0148: if (group.isAboveColumnHeader()) {
0149: renderGroupHeader(context, group, writer);
0150: renderColumnHeaders(context, group, writer);
0151: } else {
0152: renderColumnHeaders(context, group, writer);
0153: renderGroupHeader(context, group, writer);
0154: }
0155: group.setRowKey(null); // Clean up.
0156: }
0157:
0158: /**
0159: * Render the children of the specified UIComponent to the output stream or
0160: * writer associated with the response we are creating.
0161: *
0162: * @param context FacesContext for the current request.
0163: * @param component UIComponent to be decoded.
0164: *
0165: * @exception IOException if an input/output error occurs.
0166: * @exception NullPointerException if context or component is null.
0167: */
0168: public void encodeChildren(FacesContext context,
0169: UIComponent component) throws IOException {
0170: if (context == null || component == null) {
0171: log("encodeChildren", //NOI18N
0172: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
0173: throw new NullPointerException();
0174: }
0175: if (!component.isRendered()) {
0176: log("encodeChildren",
0177: "Component not rendered, nothing to display"); //NOI18N
0178: return;
0179: }
0180:
0181: TableRowGroup group = (TableRowGroup) component;
0182: ResponseWriter writer = context.getResponseWriter();
0183:
0184: // Render empty data message.
0185: if (group.getRowCount() == 0) {
0186: log("encodeChildren",
0187: "Cannot render data, row count is zero"); //NOI18N
0188: renderEmptyDataColumn(context, group, writer);
0189: return;
0190: }
0191:
0192: // Get rendered row keys.
0193: RowKey[] rowKeys = group.getRenderedRowKeys();
0194: if (rowKeys == null) {
0195: log("encodeChildren",
0196: "Cannot render data, RowKey array is null"); //NOI18N
0197: return;
0198: }
0199:
0200: // Iterate over the rendered RowKey objects.
0201: for (int i = 0; i < rowKeys.length; i++) {
0202: group.setRowKey(rowKeys[i]);
0203: if (!group.isRowAvailable()) {
0204: log("encodeChildren",
0205: "Cannot render data, row not available"); //NOI18N
0206: break;
0207: }
0208:
0209: // Render row.
0210: renderEnclosingTagStart(context, group, writer, i);
0211:
0212: // Render children.
0213: Iterator kids = group.getTableColumnChildren();
0214: while (kids.hasNext()) {
0215: TableColumn col = (TableColumn) kids.next();
0216: if (!col.isRendered()) {
0217: log("encodeChildren", //NOI18N
0218: "TableColumn not rendered, nothing to display"); //NOI18N
0219: continue;
0220: }
0221: // Render column.
0222: RenderingUtilities.renderComponent(col, context);
0223: }
0224: renderEnclosingTagEnd(writer);
0225: }
0226: group.setRowKey(null); // Clean up.
0227: }
0228:
0229: /**
0230: * Render the ending of the specified UIComponent to the output stream or
0231: * writer associated with the response we are creating.
0232: *
0233: * @param context FacesContext for the current request.
0234: * @param component UIComponent to be rendered.
0235: *
0236: * @exception IOException if an input/output error occurs.
0237: * @exception NullPointerException if context or component is null.
0238: */
0239: public void encodeEnd(FacesContext context, UIComponent component)
0240: throws IOException {
0241: if (context == null || component == null) {
0242: log("encodeEnd", //NOI18N
0243: "Cannot render, FacesContext or UIComponent is null"); //NOI18N
0244: throw new NullPointerException();
0245: }
0246: if (!component.isRendered()) {
0247: log("encodeEnd",
0248: "Component not rendered, nothing to display"); //NOI18N
0249: return;
0250: }
0251:
0252: TableRowGroup group = (TableRowGroup) component;
0253: ResponseWriter writer = context.getResponseWriter();
0254:
0255: // Do not render footers for an empty table.
0256: if (group.getRowCount() == 0) {
0257: log("encodeEnd", //NOI18N
0258: "Column, group, and table footers not rendered, row count is zero"); //NOI18N
0259: return;
0260: }
0261:
0262: // Render group and column footers.
0263: if (group.isAboveColumnFooter()) {
0264: renderGroupFooter(context, group, writer);
0265: renderColumnFooters(context, group, writer);
0266: } else {
0267: renderColumnFooters(context, group, writer);
0268: renderGroupFooter(context, group, writer);
0269: }
0270:
0271: // Do not render table footers for an empty table.
0272: Table table = group.getTableAncestor();
0273: if (table.getRowCount() > 0) {
0274: renderTableColumnFooters(context, group, writer);
0275: } else {
0276: log("encodeEnd", //NOI18N
0277: "Table column footers not rendered, row count is zero"); //NOI18N
0278: }
0279:
0280: group.setRowKey(null); // Clean up.
0281: }
0282:
0283: /**
0284: * Return a flag indicating whether this Renderer is responsible
0285: * for rendering the children the component it is asked to render.
0286: * The default implementation returns false.
0287: */
0288: public boolean getRendersChildren() {
0289: return true;
0290: }
0291:
0292: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0293: // Empty data methods
0294: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0295:
0296: /**
0297: * Render empty data message for TableRowGroup components.
0298: *
0299: * @param context FacesContext for the current request.
0300: * @param component TableRowGroup to be rendered.
0301: * @param writer ResponseWriter to which the component should be rendered.
0302: *
0303: * @exception IOException if an input/output error occurs.
0304: */
0305: protected void renderEmptyDataColumn(FacesContext context,
0306: TableRowGroup component, ResponseWriter writer)
0307: throws IOException {
0308: if (component == null) {
0309: log("renderEmptyDataColumn", //NOI18N
0310: "Cannot render empty data column, TableRowGroup is null"); //NOI18N
0311: return;
0312: }
0313: // Render row start.
0314: renderEnclosingTagStart(context, component, writer, -1);
0315:
0316: // Render empty data column.
0317: writer.writeText("\n", null); //NOI18N
0318: RenderingUtilities.renderComponent(component
0319: .getEmptyDataColumn(), context);
0320: renderEnclosingTagEnd(writer);
0321: }
0322:
0323: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0324: // Column methods
0325: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0326:
0327: /**
0328: * Render column footers for TableRowGroup components.
0329: * <p>
0330: * Note: Although not currently a requirement, nested TableColumn children
0331: * could render column footers that look like:
0332: * </p><pre>
0333: *
0334: * | | 1 | 2 | | 3 | 4 | 5 | 6 | |
0335: * | A | B | C | D | E |
0336: *
0337: * </pre><p>
0338: * In this case, components would be rendered on separate rows. For example,
0339: * the HTML would look like:
0340: * </p><pre>
0341: *
0342: * <table border="1">
0343: * <tr>
0344: * <th rowspan="2">A</th>
0345: * <th>1</th>
0346: * <th>2</th>
0347: * <th rowspan="2">C</th>
0348: * <th>3</th>
0349: * <th>4</th>
0350: * <th>5</th>
0351: * <th>6</th>
0352: * <th rowspan="2">E</th>
0353: * </tr>
0354: * <tr>
0355: * <th colspan="2">B</th>
0356: * <th colspan="2">D</th>
0357: * </tr>
0358: * </table>
0359: *
0360: * </pre><p>
0361: * However, the current implementation will render only the first row, which
0362: * would look like:
0363: * </p><pre>
0364: *
0365: * | A | 1 | 2 | C | 3 | 4 | 5 | 6 | E |
0366: *
0367: * </pre>
0368: * @param context FacesContext for the current request.
0369: * @param component TableRowGroup to be rendered.
0370: * @param writer ResponseWriter to which the component should be rendered.
0371: *
0372: * @exception IOException if an input/output error occurs.
0373: */
0374: protected void renderColumnFooters(FacesContext context,
0375: TableRowGroup component, ResponseWriter writer)
0376: throws IOException {
0377: if (component == null) {
0378: log("renderColumnFooters", //NOI18N
0379: "Cannot render column footers, TableRowGroup is null"); //NOI18N
0380: return;
0381: }
0382:
0383: // Get Map of List objects containing nested TableColumn children.
0384: Map map = getColumnFooterMap(component);
0385:
0386: // Render nested TableColumn children on separate rows.
0387: Theme theme = getTheme();
0388: Table table = component.getTableAncestor();
0389: for (int c = 0; c < map.size(); c++) {
0390: // The default is to show one level only.
0391: if (c > 0 && !component.isMultipleColumnFooters()) {
0392: log("renderColumnFooters", //NOI18N
0393: "Multiple column footers not rendered, nothing to display"); //NOI18N
0394: break;
0395: }
0396:
0397: // Flag to keep from rendering empty tag when no headers are displayed.
0398: boolean renderStartElement = true;
0399:
0400: // Get List of nested TableColumn children.
0401: List list = (List) map.get(new Integer(c));
0402: for (int i = 0; i < list.size(); i++) {
0403: TableColumn col = (TableColumn) list.get(i);
0404: if (!col.isRendered()) {
0405: log("renderColumnFooters", //NOI18N
0406: "TableColumn not rendered, nothing to display"); //NOI18N
0407: continue;
0408: }
0409:
0410: // Get group footer.
0411: UIComponent footer = col.getColumnFooter();
0412: if (!(footer != null && footer.isRendered())) {
0413: log("renderColumnFooters", //NOI18N
0414: "Column footer not rendered, nothing to display"); //NOI18N
0415: continue;
0416: }
0417:
0418: // Render start element.
0419: if (renderStartElement) {
0420: renderStartElement = false;
0421: writer.writeText("\n", null); //NOI18N
0422: writer.startElement("tr", component); //NOI18N
0423: writer.writeAttribute("id", getId(component, //NOI18N
0424: TableRowGroup.COLUMN_FOOTER_BAR_ID
0425: + NamingContainer.SEPARATOR_CHAR
0426: + c), null);
0427:
0428: // Render style class.
0429: if (component.isCollapsed()) {
0430: writer
0431: .writeAttribute(
0432: "class", //NOI18N
0433: theme
0434: .getStyleClass(ThemeStyles.HIDDEN),
0435: null); //NOI18N
0436: }
0437: }
0438: // Render footer.
0439: RenderingUtilities.renderComponent(footer, context);
0440: }
0441:
0442: // If start element was rendered, this value will be false.
0443: if (!renderStartElement) {
0444: writer.endElement("tr"); //NOI18N
0445: }
0446: }
0447: }
0448:
0449: /**
0450: * Render column headers for TableRowGroup components.
0451: * <p>
0452: * Note: Although not typical, nested TableColumn children may render column
0453: * headers that look like:
0454: * </p><pre>
0455: *
0456: * | A | B | C | D | E |
0457: * | | 1 | 2 | | 3 | 4 | 5 | 6 | |
0458: *
0459: * </pre><p>
0460: * In this case, components would be rendered on separate rows. For example,
0461: * the HTML would look like:
0462: * </p><pre>
0463: *
0464: * <table border="1">
0465: * <tr>
0466: * <th rowspan="2">A</th>
0467: * <th colspan="2">B</th>
0468: * <th rowspan="2">C</th>
0469: * <th colspan="4">D</th>
0470: * <th rowspan="2">E</th>
0471: * </tr>
0472: * <tr>
0473: * <th>1</th>
0474: * <th>2</th>
0475: * <th>3</th>
0476: * <th>4</th>
0477: * <th>5</th>
0478: * <th>6</th>
0479: * </tr>
0480: * </table>
0481: *
0482: * </pre>
0483: * @param context FacesContext for the current request.
0484: * @param component TableRowGroup to be rendered.
0485: * @param writer ResponseWriter to which the component should be rendered.
0486: *
0487: * @exception IOException if an input/output error occurs.
0488: */
0489: protected void renderColumnHeaders(FacesContext context,
0490: TableRowGroup component, ResponseWriter writer)
0491: throws IOException {
0492: if (component == null) {
0493: log("renderColumnHeaders", //NOI18N
0494: "Cannot render column headers, TableRowGroup is null"); //NOI18N
0495: return;
0496: }
0497:
0498: // Get Map of List objects containing nested TableColumn children.
0499: Map map = getColumnHeaderMap(component);
0500:
0501: // Render nested TableColumn children on separate rows.
0502: Theme theme = getTheme();
0503: Table table = component.getTableAncestor();
0504: for (int c = 0; c < map.size(); c++) {
0505: // Flag to keep from rendering empty tag when no headers are displayed.
0506: boolean renderStartElement = true;
0507:
0508: // Get List of nested TableColumn children.
0509: List list = (List) map.get(new Integer(c));
0510: for (int i = 0; i < list.size(); i++) {
0511: TableColumn col = (TableColumn) list.get(i);
0512: if (!col.isRendered()) {
0513: log("renderColumnHeaders", //NOI18N
0514: "TableColumn not rendered, nothing to display"); //NOI18N
0515: continue;
0516: }
0517:
0518: // Get group header.
0519: UIComponent header = col.getColumnHeader();
0520: if (!(header != null && header.isRendered())) {
0521: log("renderColumnHeaders", //NOI18N
0522: "Column header not rendered, nothing to display"); //NOI18N
0523: continue;
0524: }
0525:
0526: // Render start element.
0527: if (renderStartElement) {
0528: renderStartElement = false;
0529: writer.writeText("\n", null); //NOI18N
0530: writer.startElement("tr", component); //NOI18N
0531: writer.writeAttribute("id", getId(component, //NOI18N
0532: TableRowGroup.COLUMN_HEADER_BAR_ID
0533: + NamingContainer.SEPARATOR_CHAR
0534: + c), null);
0535:
0536: // Render style class.
0537: //
0538: // Note: We must determine if column headers are available for
0539: // all or individual TableRowGroup components. That is, there
0540: // could be a single column header for all row groups or one for
0541: // each group. Thus, headers may only be hidden when there is
0542: // more than one column header.
0543: if (component.isCollapsed() && table != null
0544: && table.getColumnHeadersCount() > 1) {
0545: writer
0546: .writeAttribute(
0547: "class", //NOI18N
0548: theme
0549: .getStyleClass(ThemeStyles.HIDDEN),
0550: null); //NOI18N
0551: }
0552: }
0553: //<RAVE>
0554: // Set the visiblilty of the column header to that of the column
0555: ((TableHeader) header).setVisible(col.isVisible());
0556: //</RAVE>
0557:
0558: // Render header.
0559: RenderingUtilities.renderComponent(header, context);
0560: }
0561:
0562: // If start element was rendered, this value will be false.
0563: if (!renderStartElement) {
0564: writer.endElement("tr"); //NOI18N
0565: }
0566: }
0567: }
0568:
0569: /**
0570: * Render table column footers for TableRowGroup components.
0571: * <p>
0572: * Note: Although not currently a requirement, nested TableColumn children
0573: * could render column footers that look like:
0574: * </p><pre>
0575: *
0576: * | | 1 | 2 | | 3 | 4 | 5 | 6 | |
0577: * | A | B | C | D | E |
0578: *
0579: * </pre><p>
0580: * In this case, components would be rendered on separate rows. For example,
0581: * the HTML would look like:
0582: * </p><pre>
0583: *
0584: * <table border="1">
0585: * <tr>
0586: * <th rowspan="2">A</th>
0587: * <th>1</th>
0588: * <th>2</th>
0589: * <th rowspan="2">C</th>
0590: * <th>3</th>
0591: * <th>4</th>
0592: * <th>5</th>
0593: * <th>6</th>
0594: * <th rowspan="2">E</th>
0595: * </tr>
0596: * <tr>
0597: * <th colspan="2">B</th>
0598: * <th colspan="2">D</th>
0599: * </tr>
0600: * </table>
0601: *
0602: * </pre><p>
0603: * However, the current implementation will render only the first row, which
0604: * would look like:
0605: * </p><pre>
0606: *
0607: * | A | 1 | 2 | C | 3 | 4 | 5 | 6 | E |
0608: *
0609: * </pre>
0610: * @param context FacesContext for the current request.
0611: * @param component TableRowGroup to be rendered.
0612: * @param writer ResponseWriter to which the component should be rendered.
0613: *
0614: * @exception IOException if an input/output error occurs.
0615: */
0616: protected void renderTableColumnFooters(FacesContext context,
0617: TableRowGroup component, ResponseWriter writer)
0618: throws IOException {
0619: if (component == null) {
0620: log("renderTableColumnFooters", //NOI18N
0621: "Cannot render table column footers, TableRowGroup is null"); //NOI18N
0622: return;
0623: }
0624:
0625: // Get Map of List objects containing nested TableColumn children.
0626: Map map = getColumnFooterMap(component);
0627:
0628: // Render nested TableColumn children on separate rows.
0629: Theme theme = getTheme();
0630: Table table = component.getTableAncestor();
0631: for (int c = 0; c < map.size(); c++) {
0632: // The default is to show one level only.
0633: if (c > 0 && table != null
0634: && !component.isMultipleTableColumnFooters()) {
0635: log("renderTableColumnFooters", //NOI18N
0636: "Multiple table column footers not rendered, nothing to display"); //NOI18N
0637: break;
0638: }
0639:
0640: // Flag to keep from rendering empty tag when no headers are displayed.
0641: boolean renderStartElement = true;
0642:
0643: // Get List of nested TableColumn children.
0644: List list = (List) map.get(new Integer(c));
0645: for (int i = 0; i < list.size(); i++) {
0646: TableColumn col = (TableColumn) list.get(i);
0647: if (!col.isRendered()) {
0648: log("renderTableColumnFooters", //NOI18N
0649: "TableColumn not rendered, nothing to display"); //NOI18N
0650: continue;
0651: }
0652:
0653: // Get group footer.
0654: UIComponent footer = col.getTableColumnFooter();
0655: if (!(footer != null && footer.isRendered())) {
0656: log("renderTableColumnFooters", //NOI18N
0657: "Table column footer not rendered, nothing to display"); //NOI18N
0658: continue;
0659: }
0660:
0661: // Render start element.
0662: if (renderStartElement) {
0663: renderStartElement = false;
0664: writer.writeText("\n", null); //NOI18N
0665: writer.startElement("tr", component); //NOI18N
0666: writer.writeAttribute("id", getId(component, //NOI18N
0667: TableRowGroup.TABLE_COLUMN_FOOTER_BAR_ID
0668: + NamingContainer.SEPARATOR_CHAR
0669: + c), null);
0670:
0671: // Render style class.
0672: //
0673: // Note: We must determine if column footers are available
0674: // for all or individual TableRowGroup components. That is,
0675: // there could be a single column footer for all row groups
0676: // or one for each group. Thus, footers may only be hidden
0677: // when there is more than one column footer.
0678: if (component.isCollapsed()
0679: && table.getColumnHeadersCount() > 1) {
0680: writer
0681: .writeAttribute(
0682: "class", //NOI18N
0683: theme
0684: .getStyleClass(ThemeStyles.HIDDEN),
0685: null); //NOI18N
0686: }
0687: }
0688: // Render header.
0689: RenderingUtilities.renderComponent(footer, context);
0690: }
0691:
0692: // If start element was rendered, this value will be false.
0693: if (!renderStartElement) {
0694: writer.endElement("tr"); //NOI18N
0695: }
0696: }
0697: }
0698:
0699: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0700: // Group methods
0701: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0702:
0703: /**
0704: * Render group footer for TableRowGroup components.
0705: *
0706: * @param context FacesContext for the current request.
0707: * @param component TableRowGroup to be rendered.
0708: * @param writer ResponseWriter to which the component should be rendered.
0709: *
0710: * @exception IOException if an input/output error occurs.
0711: */
0712: protected void renderGroupFooter(FacesContext context,
0713: TableRowGroup component, ResponseWriter writer)
0714: throws IOException {
0715: if (component == null) {
0716: log("renderGroupFooter", //NOI18N
0717: "Cannot render group footer, TableRowGroup is null"); //NOI18N
0718: return;
0719: }
0720:
0721: // Get group footer.
0722: UIComponent footer = component.getGroupFooter();
0723: if (!(footer != null && footer.isRendered())) {
0724: log("renderGroupFooter", //NOI18N
0725: "Group footer not rendered, nothing to display"); //NOI18N
0726: return;
0727: }
0728:
0729: Theme theme = getTheme();
0730: writer.writeText("\n", null); //NOI18N
0731: writer.startElement("tr", component); //NOI18N
0732: writer.writeAttribute("id", getId(component, //NOI18N
0733: TableRowGroup.GROUP_FOOTER_BAR_ID), null);
0734:
0735: // Render style class.
0736: if (component.isCollapsed()) {
0737: writer.writeAttribute("class", //NOI18N
0738: theme.getStyleClass(ThemeStyles.HIDDEN), null); //NOI18N
0739: }
0740:
0741: // Render footer.
0742: RenderingUtilities.renderComponent(footer, context);
0743: writer.endElement("tr"); //NOI18N
0744: }
0745:
0746: /**
0747: * Render group header for TableRowGroup components.
0748: *
0749: * @param context FacesContext for the current request.
0750: * @param component TableRowGroup to be rendered.
0751: * @param writer ResponseWriter to which the component should be rendered.
0752: *
0753: * @exception IOException if an input/output error occurs.
0754: */
0755: protected void renderGroupHeader(FacesContext context,
0756: TableRowGroup component, ResponseWriter writer)
0757: throws IOException {
0758: if (component == null) {
0759: log("renderGroupHeader", //NOI18N
0760: "Cannot render group header, TableRowGroup is null"); //NOI18N
0761: return;
0762: }
0763:
0764: // Get group header.
0765: UIComponent header = component.getGroupHeader();
0766: if (!(header != null && header.isRendered())) {
0767: log("renderGroupHeader", //NOI18N
0768: "Group header not rendered, nothing to display"); //NOI18N
0769: return;
0770: }
0771:
0772: Theme theme = getTheme();
0773: writer.writeText("\n", null); //NOI18N
0774: writer.startElement("tr", component); //NOI18N
0775: writer.writeAttribute("id", getId(component, //NOI18N
0776: TableRowGroup.GROUP_HEADER_BAR_ID), null);
0777:
0778: // Render header.
0779: RenderingUtilities.renderComponent(header, context);
0780: writer.endElement("tr"); //NOI18N
0781: }
0782:
0783: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0784: // Enclosing tag methods
0785: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0786:
0787: /**
0788: * Render enclosing tag for TableRowGroup components.
0789: *
0790: * @param context FacesContext for the current request.
0791: * @param component TableRowGroup to be rendered.
0792: * @param writer ResponseWriter to which the component should be rendered.
0793: * @param index The current row index.
0794: *
0795: * @exception IOException if an input/output error occurs.
0796: */
0797: protected void renderEnclosingTagStart(FacesContext context,
0798: TableRowGroup component, ResponseWriter writer, int index)
0799: throws IOException {
0800: if (component == null) {
0801: log("renderEnclosingTagStart", //NOI18N
0802: "Cannot render enclosing tag, TableRowGroup is null"); //NOI18N
0803: return;
0804: }
0805:
0806: Theme theme = getTheme();
0807: writer.writeText("\n", null); //NOI18N
0808: writer.startElement("tr", component); //NOI18N
0809: writer.writeAttribute("id", component.getClientId(context),
0810: null); //NOI18N
0811:
0812: // Get style class for nonempty table.
0813: String styleClasses[] = getRowStyleClasses(component);
0814: String styleClass = (index > -1 && styleClasses.length > 0) ? styleClasses[index
0815: % styleClasses.length]
0816: : null;
0817:
0818: // Get selected style class.
0819: if (component.isSelected()) {
0820: String s = theme
0821: .getStyleClass(ThemeStyles.TABLE_SELECT_ROW);
0822: styleClass = (styleClass != null) ? styleClass + " " + s
0823: : s; //NOI18N
0824: }
0825:
0826: // Get collapsed style class.
0827: if (component.isCollapsed()) {
0828: String s = theme.getStyleClass(ThemeStyles.HIDDEN);
0829: styleClass = (styleClass != null) ? styleClass + " " + s
0830: : s; //NOI18N
0831: }
0832:
0833: // Render style class.
0834: RenderingUtilities.renderStyleClass(context, writer, component,
0835: styleClass);
0836:
0837: // Render tooltip.
0838: if (component.getToolTip() != null) {
0839: writer.writeAttribute("title", component.getToolTip(),
0840: "toolTip"); //NOI18N
0841: }
0842:
0843: // Render pass through attributes.
0844: RenderingUtilities.writeStringAttributes(component, writer,
0845: stringAttributes);
0846: }
0847:
0848: /**
0849: * Render enclosing tag for TableRowGroup components.
0850: *
0851: * @param writer ResponseWriter to which the component should be rendered.
0852: *
0853: * @exception IOException if an input/output error occurs.
0854: */
0855: protected void renderEnclosingTagEnd(ResponseWriter writer)
0856: throws IOException {
0857: writer.endElement("tr"); //NOI18N
0858: }
0859:
0860: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0861: // Private methods
0862: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0863:
0864: /**
0865: * Helper method to get Map of List objects containing nested TableColumn
0866: * children.
0867: * <p>
0868: * Note: Although not currently a requirement, nested TableColumn children
0869: * could render column footers that look like:
0870: * </p><pre>
0871: *
0872: * | | 1 | 2 | | 3 | 4 | 5 | 6 | |
0873: * | A | B | C | D | E |
0874: *
0875: * </pre><p>
0876: * In this case, components would be rendered on separate rows. Thus, we
0877: * need a map that contains List objects like so:
0878: * </p><pre>
0879: *
0880: * Key (row) 0: A, 1, 2, C, 3, 4, 5, 6, E
0881: * Key (row) 1: B, D
0882: *
0883: * </pre><p>
0884: * Obtaining the List for key 0 tells the renderer that A, 1, 2, C, 3, 4, 5,
0885: * 6, and E should be rendered for the first row. Obtaining the List for key
0886: * 1, tells the renderer that B and D should be rendered for the next row.
0887: * And so on...
0888: * </p>
0889: * @param component TableRowGroup to be rendered.
0890: * @return A Map of nested TableColumn children.
0891: */
0892: private Map getColumnFooterMap(TableRowGroup component) {
0893: Map map = getColumnHeaderMap(component); // Start with header map.
0894: if (map.size() == 0) {
0895: log("getColumnFooterMap", "Cannot obtain column footer map"); //NOI18N
0896: return map;
0897: }
0898:
0899: // Invert map.
0900: HashMap newMap = new HashMap();
0901: for (int i = 0; i < map.size(); i++) {
0902: newMap.put(new Integer(i), map.get(new Integer(map.size()
0903: - i - 1)));
0904: }
0905:
0906: // Move all non-nested components to the top row.
0907: List newList = (List) newMap.get(new Integer(0));
0908: for (int c = 1; c < newMap.size(); c++) { // Top row is set already.
0909: List list = (List) newMap.get(new Integer(c));
0910: for (int i = list.size() - 1; i >= 0; i--) { // Start with last component.
0911: TableColumn col = (TableColumn) list.get(i);
0912: if (col.getTableColumnChildren().hasNext()) {
0913: // Do not move TableColumn components with nested children.
0914: continue;
0915: }
0916:
0917: // Get colspan of all previous components.
0918: int colspan = 0;
0919: for (int k = i - 1; k >= 0; k--) {
0920: TableColumn prevCol = (TableColumn) list.get(k);
0921: if (prevCol.getTableColumnChildren().hasNext()) {
0922: // Count only nested TableColumn components. Other
0923: // components have not been moved to the to row, yet.
0924: colspan += prevCol.getColumnCount();
0925: }
0926: }
0927:
0928: // Set new position in the top row.
0929: newList.add(colspan, col);
0930: list.remove(i);
0931: }
0932: }
0933: return newMap;
0934: }
0935:
0936: /**
0937: * Helper method to get Map of List objects containing nested TableColumn
0938: * children.
0939: * <p>
0940: * Note: Nested TableColumn children may be rendered on separate rows. For
0941: * example, to render column footers that look like the following:
0942: * </p><pre>
0943: *
0944: * | A | B | C | D | E |
0945: * | | 1 | 2 | | 3 | 4 | 5 | 6 | |
0946: *
0947: * </pre><p>
0948: * In this case, components would be rendered on separate rows. Thus, we
0949: * need a map that contains List objects like so:
0950: * </p><pre>
0951: *
0952: * Key (row) 0: A, B, C, D, E
0953: * Key (row) 1: 1, 2, 3, 4, 5, 6
0954: *
0955: * </pre><p>
0956: * Obtaining the List for key 0 tells the renderer that A, B, C, D, and E
0957: * should be rendered for the first row. Obtaining the List for key 1, tells
0958: * the renderer that 1, 2, 3, 4, 5, and 6 should be rendered for the next
0959: * row. And so on...
0960: * </p>
0961: * @param component TableRowGroup to be rendered.
0962: * @return A Map of nested TableColumn children.
0963: */
0964: private Map getColumnHeaderMap(TableRowGroup component) {
0965: HashMap map = new HashMap();
0966: if (component == null) {
0967: log("getColumnHeaderMap", //NOI18N
0968: "Cannot obtain column header map, TableRowGroup is null"); //NOI18N
0969: return map;
0970: }
0971: Iterator kids = component.getTableColumnChildren();
0972: while (kids.hasNext()) {
0973: TableColumn col = (TableColumn) kids.next();
0974: initColumnHeaderMap(col, map, 0);
0975: }
0976: return map;
0977: }
0978:
0979: /**
0980: * Get component id.
0981: *
0982: * @param component The parent UIComponent component.
0983: * @param id The id of the the component to be rendered.
0984: */
0985: private String getId(UIComponent component, String id) {
0986: String clientId = component.getClientId(FacesContext
0987: .getCurrentInstance());
0988: return clientId + NamingContainer.SEPARATOR_CHAR + id;
0989: }
0990:
0991: /** Helper method to get Theme objects. */
0992: private Theme getTheme() {
0993: return ThemeUtilities.getTheme(FacesContext
0994: .getCurrentInstance());
0995: }
0996:
0997: /**
0998: * Helper method to get an array of stylesheet classes to be applied to each
0999: * row, in the order specified.
1000: * <p>
1001: * Note: This is a comma-delimited list of CSS style classes that will be
1002: * applied to the rows of this table. A space separated list of classes may
1003: * also be specified for any individual row. These styles are applied, in
1004: * turn, to each row in the table. For example, if the list has two
1005: * elements, the first style class in the list is applied to the first row,
1006: * the second to the second row, the first to the third row, the second to
1007: * the fourth row, etc. In other words, we keep iterating through the list
1008: * until we reach the end, and then we start at the beginning again.
1009: * </p>
1010: * @param component TableRowGroup component being rendered.
1011: * @return An array of stylesheet classes.
1012: */
1013: private String[] getRowStyleClasses(TableRowGroup component) {
1014: String values = (component != null) ? (String) component
1015: .getStyleClasses() : null;
1016:
1017: if (values == null) {
1018: return new String[0];
1019: }
1020:
1021: values = values.trim();
1022: ArrayList list = new ArrayList();
1023:
1024: while (values.length() > 0) {
1025: int comma = values.indexOf(","); //NOI18N
1026: if (comma >= 0) {
1027: list.add(values.substring(0, comma).trim());
1028: values = values.substring(comma + 1);
1029: } else {
1030: list.add(values.trim());
1031: values = ""; //NOI18N
1032: }
1033: }
1034:
1035: String results[] = new String[list.size()];
1036: return ((String[]) list.toArray(results));
1037: }
1038:
1039: /**
1040: * Helper method to initialize Map of List objects containing nested
1041: * TableColumn children.
1042: *
1043: * @param component TableColumn to be rendered.
1044: * @param map Map to save component List.
1045: * @param level The current level of the component tree.
1046: */
1047: private void initColumnHeaderMap(TableColumn component, Map map,
1048: int level) {
1049: if (component == null) {
1050: log("initColumnHeaderMap", //NOI18N
1051: "Cannot initialize column header map, TableColumn is null"); //NOI18N
1052: return;
1053: }
1054:
1055: // Get new List for nested TableColumn children.
1056: Iterator kids = component.getTableColumnChildren();
1057: if (kids.hasNext()) {
1058: ArrayList newList = new ArrayList();
1059: while (kids.hasNext()) {
1060: TableColumn col = (TableColumn) kids.next();
1061: if (!col.isRendered()) {
1062: continue;
1063: }
1064: initColumnHeaderMap(col, map, level + 1);
1065: }
1066: }
1067:
1068: // Create a new List if needed.
1069: Integer key = new Integer(level);
1070: List list = (List) map.get(key);
1071: if (list == null) {
1072: list = new ArrayList();
1073: }
1074: list.add(component); // Save component in List.
1075: map.put(key, list); // Save List in map.
1076: }
1077:
1078: /**
1079: * Log fine messages.
1080: */
1081: private void log(String method, String message) {
1082: // Get class.
1083: Class clazz = this .getClass();
1084: if (LogUtil.fineEnabled(clazz)) {
1085: // Log method name and message.
1086: LogUtil.fine(clazz, clazz.getName() + "." + method + ": "
1087: + message); //NOI18N
1088: }
1089: }
1090: }
|