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.component;
0042:
0043: import com.sun.data.provider.SortCriteria;
0044: import com.sun.data.provider.FieldKey;
0045: import com.sun.data.provider.impl.FieldIdSortCriteria;
0046: import com.sun.data.provider.TableDataProvider;
0047: import com.sun.rave.web.ui.component.Hyperlink;
0048: import com.sun.rave.web.ui.component.Icon;
0049: import com.sun.rave.web.ui.component.IconHyperlink;
0050: import com.sun.rave.web.ui.component.Label;
0051: import com.sun.rave.web.ui.component.util.Util;
0052: import com.sun.rave.web.ui.faces.ValueBindingSortCriteria;
0053: import com.sun.rave.web.ui.event.TableSortActionListener;
0054: import com.sun.rave.web.ui.theme.Theme;
0055: import com.sun.rave.web.ui.theme.ThemeImages;
0056: import com.sun.rave.web.ui.theme.ThemeStyles;
0057: import com.sun.rave.web.ui.util.LogUtil;
0058: import com.sun.rave.web.ui.util.ThemeUtilities;
0059:
0060: import java.io.IOException;
0061: import java.util.ArrayList;
0062: import java.util.Date;
0063: import java.util.HashMap;
0064: import java.util.Iterator;
0065: import java.util.List;
0066: import java.util.Map;
0067: import java.util.Properties;
0068:
0069: import javax.faces.context.FacesContext;
0070: import javax.faces.component.UIComponent;
0071: import javax.faces.component.NamingContainer;
0072: import javax.faces.el.MethodBinding;
0073: import javax.faces.el.ValueBinding;
0074:
0075: /**
0076: * Component that represents a table column.
0077: * <p>
0078: * The tableColumn component provides a layout mechanism for displaying columns
0079: * of data. UI guidelines describe specific behavior that can applied to the
0080: * rows and columns of data such as sorting, filtering, pagination, selection,
0081: * and custom user actions. In addition, UI guidelines also define sections of
0082: * the table that can be used for titles, row group headers, and placement of
0083: * pre-defined and user defined actions.
0084: * </p><p>
0085: * Note: Column headers and footers are rendered by TableRowGroupRenderer. Table
0086: * column footers are rendered by TableRenderer.
0087: * </p><p>
0088: * Note: To see the messages logged by this class, set the following global
0089: * defaults in your JDK's "jre/lib/logging.properties" file.
0090: * </p><p><pre>
0091: * java.util.logging.ConsoleHandler.level = FINE
0092: * com.sun.rave.web.ui.component.TableColumn.level = FINE
0093: * </pre></p><p>
0094: * See TLD docs for more information.
0095: * </p>
0096: */
0097: public class TableColumn extends TableColumnBase implements
0098: NamingContainer {
0099: /** The component id for the column footer. */
0100: public static final String COLUMN_FOOTER_ID = "_columnFooter"; //NOI18N
0101:
0102: /** The facet name for the column footer. */
0103: public static final String COLUMN_FOOTER_FACET = "columnFooter"; //NOI18N
0104:
0105: /** The component id for the column header. */
0106: public static final String COLUMN_HEADER_ID = "_columnHeader"; //NOI18N
0107:
0108: /** The facet name for the column header. */
0109: public static final String COLUMN_HEADER_FACET = "columnHeader"; //NOI18N
0110:
0111: /** The facet name for the header area. */
0112: public static final String HEADER_FACET = "header"; //NOI18N
0113:
0114: /** The component id for the embedded action separator icon. */
0115: public static final String EMBEDDED_ACTION_SEPARATOR_ICON_ID = "_embeddedActionSeparatorIcon"; //NOI18N
0116:
0117: /** The facet name for the embedded action separator icon. */
0118: public static final String EMBEDDED_ACTION_SEPARATOR_ICON_FACET = "embeddedActionSeparatorIcon"; //NOI18N
0119:
0120: /** The component id for the empty cell icon. */
0121: public static final String EMPTY_CELL_ICON_ID = "_emptyCellIcon"; //NOI18N
0122:
0123: /** The facet name for the empty cell icon. */
0124: public static final String EMPTY_CELL_ICON_FACET = "emptyCellIcon"; //NOI18N
0125:
0126: /** The facet name for the footer area. */
0127: public static final String FOOTER_FACET = "footer"; //NOI18N
0128:
0129: /** The component id for the table column footer. */
0130: public static final String TABLE_COLUMN_FOOTER_ID = "_tableColumnFooter"; //NOI18N
0131:
0132: /** The facet name for the table column footer. */
0133: public static final String TABLE_COLUMN_FOOTER_FACET = "tableColumnFooter"; //NOI18N
0134:
0135: /** The facet name for the table footer area. */
0136: public static final String TABLE_FOOTER_FACET = "tableFooter"; //NOI18N
0137:
0138: // Key prefix for properties cached in the request map.
0139: private static final String REQUEST_KEY_PREFIX = "com.sun.rave.web.ui_"; //NOI18N
0140:
0141: // Key for properties cached in the request map.
0142: private static final String PROPERTIES = "_properties"; //NOI18N
0143:
0144: // The Table ancestor enclosing this component.
0145: private Table table = null;
0146:
0147: /** Default constructor */
0148: public TableColumn() {
0149: super ();
0150: }
0151:
0152: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0153: // Child methods
0154: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0155:
0156: /**
0157: * Get the closest Table ancestor that encloses this component.
0158: *
0159: * @return The Table ancestor.
0160: */
0161: public Table getTableAncestor() {
0162: // Don't cache in the request since it's used as the properties key.
0163: if (table == null) {
0164: UIComponent component = this ;
0165: while (component != null) {
0166: component = component.getParent();
0167: if (component instanceof Table) {
0168: table = (Table) component;
0169: break;
0170: }
0171: }
0172: }
0173: return table;
0174: }
0175:
0176: /**
0177: * Get the closest TableColumn ancestor that encloses this component.
0178: *
0179: * @return The TableColumn ancestor.
0180: */
0181: public TableColumn getTableColumnAncestor() {
0182: // Get properties cached in request map.
0183: Properties properties = getProperties();
0184: TableColumn tableColumn = (properties != null) ? properties
0185: .getTableColumnAncestor() : null;
0186:
0187: // Get TableColumn ancestor.
0188: if (tableColumn == null) {
0189: UIComponent component = this ;
0190: while (component != null) {
0191: component = component.getParent();
0192: if (component instanceof TableColumn) {
0193: tableColumn = (TableColumn) component;
0194: break;
0195: }
0196: }
0197: // Save property in request map.
0198: if (properties != null) {
0199: properties.setTableColumnAncestor(tableColumn);
0200: }
0201: }
0202: return tableColumn;
0203: }
0204:
0205: /**
0206: * Get an Iterator over the TableColumn children found for
0207: * this component.
0208: *
0209: * @return An Iterator over the TableColumn children.
0210: */
0211: public Iterator getTableColumnChildren() {
0212: // Get properties cached in request map.
0213: Properties properties = getProperties();
0214: List tableColumnChildren = (properties != null) ? properties
0215: .getTableColumnChildren() : null;
0216:
0217: // Get TableColumn children.
0218: if (tableColumnChildren == null) {
0219: tableColumnChildren = new ArrayList();
0220: Iterator kids = getChildren().iterator();
0221: while (kids.hasNext()) {
0222: UIComponent kid = (UIComponent) kids.next();
0223: if ((kid instanceof TableColumn)) {
0224: tableColumnChildren.add(kid);
0225: }
0226: }
0227: // Save property in request map.
0228: if (properties != null) {
0229: properties.setTableColumnChildren(tableColumnChildren);
0230: }
0231: }
0232: return tableColumnChildren.iterator();
0233: }
0234:
0235: /**
0236: * Get the number of columns found for this component that have a rendered
0237: * property of true.
0238: *
0239: * @return The number of rendered columns.
0240: */
0241: public int getColumnCount() {
0242: // Get properties cached in request map.
0243: Properties properties = getProperties();
0244: int columnCount = (properties != null) ? properties
0245: .getColumnCount() : -1;
0246:
0247: // Get column count.
0248: if (columnCount == -1) {
0249: columnCount = getColumnCount(this );
0250:
0251: // Save property in request map.
0252: if (properties != null) {
0253: properties.setColumnCount(columnCount);
0254: }
0255: }
0256: return columnCount;
0257: }
0258:
0259: /**
0260: * Get the closest TableRowGroup ancestor that encloses this component.
0261: *
0262: * @return The TableRowGroup ancestor.
0263: */
0264: public TableRowGroup getTableRowGroupAncestor() {
0265: // Get properties cached in request map.
0266: Properties properties = getProperties();
0267: TableRowGroup tableRowGroup = (properties != null) ? properties
0268: .getTableRowGroupAncestor() : null;
0269:
0270: // Get TableRowGroup ancestor.
0271: if (tableRowGroup == null) {
0272: UIComponent component = this ;
0273: while (component != null) {
0274: component = component.getParent();
0275: if (component instanceof TableRowGroup) {
0276: tableRowGroup = (TableRowGroup) component;
0277: break;
0278: }
0279: }
0280: // Save property in request map.
0281: if (properties != null) {
0282: properties.setTableRowGroupAncestor(tableRowGroup);
0283: }
0284: }
0285: return tableRowGroup;
0286: }
0287:
0288: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0289: // Column methods
0290: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0291:
0292: /**
0293: * Get column footer.
0294: *
0295: * @return The column footer.
0296: */
0297: public UIComponent getColumnFooter() {
0298: UIComponent facet = getFacet(COLUMN_FOOTER_FACET);
0299: if (facet != null) {
0300: return facet;
0301: }
0302:
0303: // Get child.
0304: TableFooter child = new TableFooter();
0305: child.setId(COLUMN_FOOTER_ID);
0306: child.setAlign(getAlign());
0307: child.setExtraHtml(getExtraFooterHtml());
0308:
0309: // Set rendered.
0310: if (!(facet != null && facet.isRendered()
0311: || getFooterText() != null || isColumnFooterRendered())) {
0312: // Note: Footer may be initialized to force rendering. This allows
0313: // developers to omit the footerText property for select columns.
0314: child.setRendered(false);
0315: } else {
0316: log("getColumnFooter", //NOI18N
0317: "Column footer not rendered, nothing to display"); //NOI18N
0318: }
0319:
0320: // If only showing one level, don't set colspan or rowspan.
0321: TableRowGroup group = getTableRowGroupAncestor();
0322: if (group != null && group.isMultipleColumnFooters()) {
0323: // Set colspan for nested TableColumn children, else rowspan.
0324: Iterator kids = getTableColumnChildren();
0325: if (kids.hasNext()) {
0326: int colspan = getColumnCount();
0327: if (colspan > 1) {
0328: child.setColSpan(colspan);
0329: }
0330: } else {
0331: int rowspan = getRowCount();
0332: if (rowspan > 1) {
0333: child.setRowSpan(rowspan);
0334: }
0335: }
0336: }
0337:
0338: // Save facet and return child.
0339: getFacets().put(child.getId(), child);
0340: return child;
0341: }
0342:
0343: /**
0344: * Get column header.
0345: *
0346: * @return The column header.
0347: */
0348: public UIComponent getColumnHeader() {
0349: UIComponent facet = getFacet(COLUMN_HEADER_FACET);
0350: if (facet != null) {
0351: return facet;
0352: }
0353:
0354: // Get child.
0355: TableHeader child = new TableHeader();
0356: child.setId(COLUMN_HEADER_ID);
0357: child.setScope("col"); //NOI18N
0358: child.setAlign(getAlign());
0359: child.setWidth((getSelectId() != null) ? "3%" : null); //NOI18N
0360: child.setNoWrap((getSelectId() != null) ? true : false);
0361: child.setExtraHtml(getExtraHeaderHtml());
0362:
0363: // Set type of header to render.
0364: boolean emptyTable = isEmptyTable();
0365: SortCriteria criteria = getSortCriteria();
0366: if (criteria != null && getSelectId() != null && !emptyTable) {
0367: child.setSelectHeader(true);
0368: } else if (criteria != null && getHeaderText() != null
0369: && !emptyTable) {
0370: child.setSortHeader(true);
0371: } else {
0372: log("getColumnHeader", //NOI18N
0373: "Render default column header, no SortCriteria or selectId"); //NOI18N
0374: }
0375:
0376: // Set rendered.
0377: if (!(facet != null && facet.isRendered()
0378: || getHeaderText() != null || isColumnHeaderRendered())) {
0379: // Note: Footer may be initialized to force rendering. This allows
0380: // developers to omit the headerText property for select columns.
0381: log("getColumnHeader", //NOI18N
0382: "Column header not rendered, nothing to display"); //NOI18N
0383: child.setRendered(false);
0384: }
0385:
0386: // Set colspan for nested TableColumn children, else rowspan.
0387: Iterator kids = getTableColumnChildren();
0388: if (kids.hasNext()) {
0389: int colspan = getColumnCount();
0390: if (colspan > 1) {
0391: child.setColSpan(colspan);
0392: }
0393: } else {
0394: int rowspan = getRowCount();
0395: if (rowspan > 1) {
0396: child.setRowSpan(rowspan);
0397: }
0398: }
0399:
0400: // Save facet and return child.
0401: getFacets().put(child.getId(), child);
0402: return child;
0403: }
0404:
0405: /**
0406: * Get table column footer.
0407: *
0408: * @return The table column footer.
0409: */
0410: public UIComponent getTableColumnFooter() {
0411: UIComponent facet = getFacet(TABLE_COLUMN_FOOTER_FACET);
0412: if (facet != null) {
0413: return facet;
0414: }
0415:
0416: // Get child.
0417: TableFooter child = new TableFooter();
0418: child.setId(TABLE_COLUMN_FOOTER_ID);
0419: child.setAlign(getAlign());
0420: child.setExtraHtml(getExtraTableFooterHtml());
0421: child.setTableColumnFooter(true);
0422:
0423: // Set rendered.
0424: if (!(facet != null && facet.isRendered()
0425: || getTableFooterText() != null || isTableColumnFooterRendered())) {
0426: // Note: Footer may be initialized to force rendering. This allows
0427: // developers to omit the tableFooterText property for select columns.
0428: child.setRendered(false);
0429: } else {
0430: log("getTableColumnFooter", //NOI18N
0431: "Table column footer not rendered, nothing to display"); //NOI18N
0432: }
0433:
0434: // If only showing one level, don't set colspan or rowspan.
0435: TableRowGroup group = getTableRowGroupAncestor();
0436: if (group != null && group.isMultipleTableColumnFooters()) {
0437: // Set colspan for nested TableColumn children, else rowspan.
0438: Iterator kids = getTableColumnChildren();
0439: if (kids.hasNext()) {
0440: int colspan = getColumnCount();
0441: if (colspan > 1) {
0442: child.setColSpan(colspan);
0443: }
0444: } else {
0445: int rowspan = getRowCount();
0446: if (rowspan > 1) {
0447: child.setRowSpan(rowspan);
0448: }
0449: }
0450: }
0451:
0452: // Save facet and return child.
0453: getFacets().put(child.getId(), child);
0454: return child;
0455: }
0456:
0457: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0458: // TableColumnBase methods
0459: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0460:
0461: /**
0462: * Get the horizontal alignment for the cell.
0463: * <p>
0464: * Note: If the align property is specified, it is returned as is. However,
0465: * if the alignKey property is provided, alignment is based on the object
0466: * type of the data element. For example, Date and Number objects are
0467: * aligned using "right", Character and String objects are aligned using
0468: * "left", and Boolean objects are aligned using "center". Note that select
0469: * columns are aligned using "center" by default.
0470: * </p>
0471: * @return The horizontal alignment for the cell. If the align property is
0472: * null or the object type cannot be determined, "left" is returned by
0473: * default.
0474: */
0475: public String getAlign() {
0476: // Note: The align property overrides alignKey.
0477: if (super .getAlign() != null) {
0478: return super .getAlign();
0479: }
0480:
0481: // Get alignment.
0482: String result = null;
0483: Class type = getType();
0484: if (type != null
0485: && (type.equals(Character.class) || type
0486: .equals(String.class))) {
0487: result = "left"; //NOI18N
0488: } else if (type != null
0489: && (type.equals(Date.class) || type
0490: .equals(Number.class))) {
0491: result = "right"; //NOI18N
0492: } else if (type != null && type.equals(Boolean.class)) {
0493: result = "center"; //NOI18N
0494: } else {
0495: // Note: Select columns also default to "left".
0496: result = "left"; //NOI18N
0497: }
0498: return result;
0499: }
0500:
0501: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0502: // Empty cell methods
0503: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0504:
0505: /**
0506: * Get the icon used to display inapplicable or unexpectedly empty cells.
0507: * <p>
0508: * Note: UI guidelines suggest not to use this for a value that is truly
0509: * null, such as an empty alarm cell or a comment field which is blank,
0510: * neither of which should have the dash image. Further, it is recomended
0511: * not to use the dash image for cells that contain user interface elements
0512: * such as checkboxes or drop-down lists when these elements are not
0513: * applicable. Instead, simply do not display the user interface element.
0514: * </p>
0515: * @return The icon used to display empty cells.
0516: */
0517: public UIComponent getEmptyCellIcon() {
0518: UIComponent facet = getFacet(EMPTY_CELL_ICON_FACET);
0519: if (facet != null) {
0520: return facet;
0521: }
0522:
0523: Theme theme = getTheme();
0524:
0525: // Get child.
0526: Icon child = theme.getIcon(ThemeImages.TABLE_EMPTY_CELL);
0527: child.setId(EMPTY_CELL_ICON_ID);
0528: child.setBorder(0);
0529:
0530: // Set tool tip.
0531: String toolTip = theme.getMessage("table.emptyTableCell"); //NOI18N
0532: child.setToolTip(toolTip);
0533: child.setAlt(toolTip);
0534:
0535: // Save facet and return child.
0536: getFacets().put(child.getId(), child);
0537: return child;
0538: }
0539:
0540: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0541: // Separator methods
0542: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0543:
0544: /**
0545: * Get separator icon for embedded actions.
0546: *
0547: * @return The separator icon for embedded actions.
0548: */
0549: public UIComponent getEmbeddedActionSeparatorIcon() {
0550: UIComponent facet = getFacet(EMBEDDED_ACTION_SEPARATOR_ICON_FACET);
0551: if (facet != null) {
0552: return facet;
0553: }
0554:
0555: // Get child.
0556: Icon child = getTheme().getIcon(
0557: ThemeImages.TABLE_EMBEDDED_ACTIONS_SEPARATOR);
0558: child.setId(EMBEDDED_ACTION_SEPARATOR_ICON_ID);
0559: child.setBorder(0);
0560: child.setAlign("top"); //NOI18N
0561:
0562: // Save facet and return child.
0563: getFacets().put(child.getId(), child);
0564: return child;
0565: }
0566:
0567: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0568: // Sort methods
0569: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0570:
0571: /**
0572: * Get SortCriteria used for sorting the contents of a TableDataProvider.
0573: * <p>
0574: * Note: If the sortKey attribute resolves to a SortCriteria object, it is
0575: * returned as is. However, if there is a value binding, and it's not null,
0576: * a ValueBindingSortCriteria object is created. If there is no value
0577: * binding, a FieldIdSortCriteria object is created.
0578: * </p>
0579: * @return The SortCriteria used for sorting.
0580: */
0581: public SortCriteria getSortCriteria() {
0582: // Return if value binding resolves to a SortCriteria object.
0583: Object key = getSort();
0584: if (key instanceof SortCriteria) {
0585: return (SortCriteria) key;
0586: }
0587:
0588: SortCriteria result = null;
0589: ValueBinding vb = getValueBinding("sort"); //NOI18N
0590: if (vb != null) {
0591: ValueBindingSortCriteria vbsc = new ValueBindingSortCriteria(
0592: vb, !isDescending()); // Note: Constructor accepts ascending param.
0593: TableRowGroup group = getTableRowGroupAncestor();
0594: if (group != null) {
0595: vbsc.setRequestMapKey(group.getSourceVar());
0596: }
0597: result = vbsc;
0598: } else if (key != null) {
0599: result = new FieldIdSortCriteria(key.toString(),
0600: !isDescending());
0601: }
0602: return result;
0603: }
0604:
0605: /**
0606: * Get sort tool tip augment based on the value given to the align
0607: * property of the tableColumn component.
0608: *
0609: * @param descending Flag indicating descending sort.
0610: * @return The sort tool tip augment.
0611: */
0612: public String getSortToolTipAugment(boolean descending) {
0613: String result = null;
0614:
0615: // To do: Test for toolTip property? The alarm or other custom
0616: // components may need to set the tooltip. If so, do we need both
0617: // ascending and descending tooltips?
0618:
0619: // Get object type.
0620: Class type = getType();
0621:
0622: // Get tooltip.
0623: ValueBinding vb = getValueBinding("severity"); //NOI18N
0624: if (getSeverity() != null || vb != null) {
0625: result = (descending) ? "table.sort.augment.alarmDescending" //NOI18N
0626: : "table.sort.augment.alarmAscending"; //NOI18N
0627: } else if (getSelectId() != null
0628: || (type != null && type.equals(Boolean.class))) {
0629: result = (descending) ? "table.sort.augment.booleanDescending" //NOI18N
0630: : "table.sort.augment.booleanAscending"; //NOI18N
0631: } else if (type != null && type.equals(String.class)) {
0632: result = (descending) ? "table.sort.augment.stringDescending" //NOI18N
0633: : "table.sort.augment.stringAscending"; //NOI18N
0634: } else if (type != null && type.equals(Character.class)) {
0635: result = (descending) ? "table.sort.augment.charDescending" //NOI18N
0636: : "table.sort.augment.charAscending"; //NOI18N
0637: } else if (type != null && type.equals(Date.class)) {
0638: result = (descending) ? "table.sort.augment.dateDescending" //NOI18N
0639: : "table.sort.augment.dateAscending"; //NOI18N
0640: } else if (type != null && type.equals(Number.class)) {
0641: result = (descending) ? "table.sort.augment.numericDescending" //NOI18N
0642: : "table.sort.augment.numericAscending"; //NOI18N
0643: } else {
0644: result = (descending) ? "table.sort.augment.undeterminedDescending" //NOI18N
0645: : "table.sort.augment.undeterminedAscending"; //NOI18N
0646: }
0647: return getTheme().getMessage(result);
0648: }
0649:
0650: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0651: // Private methods
0652: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0653:
0654: /**
0655: * Helper method to get the number of columns found for this component that
0656: * have a rendered property of true.
0657: *
0658: * @param component TableColumn to be rendered.
0659: * @return The first selectId property found.
0660: */
0661: private int getColumnCount(TableColumn component) {
0662: int count = 0;
0663: if (component == null) {
0664: log("getColumnCount", //NOI18N
0665: "Cannot obtain column count, TableColumn is null"); //NOI18N
0666: return count;
0667: }
0668:
0669: // Get column count for nested TableColumn children.
0670: Iterator kids = component.getTableColumnChildren();
0671: if (kids.hasNext()) {
0672: while (kids.hasNext()) {
0673: TableColumn col = (TableColumn) kids.next();
0674: if (!col.isRendered()) {
0675: continue;
0676: }
0677: count += getColumnCount(col);
0678: }
0679: } else {
0680: // Do not include root TableColumn nodes in count.
0681: if (component.isRendered()) {
0682: count++;
0683: }
0684: }
0685: return count;
0686: }
0687:
0688: /**
0689: * Get the properties for this component cached in the request map.
0690: * <p>
0691: * Note: Properties may have been cached via the apply request values,
0692: * validate, and update phases and must be initialized for the render
0693: * response phase. Table components are forced to reinitialize by setting
0694: * the cached properties map to null when the table title and actions bar
0695: * are rendered. New column and row counts are required each time the table
0696: * is redisplayed, for example.
0697: * </p>
0698: * @return The properties for this component.
0699: */
0700: private Properties getProperties() {
0701: // Get Table ancestor.
0702: Table table = getTableAncestor();
0703: if (table == null) {
0704: log("getProperties",
0705: "Cannot obtain Properties, Table is null"); //NOI18N
0706: return null;
0707: }
0708:
0709: // Get properties for all components.
0710: FacesContext context = getFacesContext();
0711: Map requestMap = context.getExternalContext().getRequestMap();
0712: String propertiesMapId = REQUEST_KEY_PREFIX
0713: + table.getClientId(context) + PROPERTIES;
0714: Map propertiesMap = (Map) requestMap.get(propertiesMapId);
0715: if (propertiesMap == null) {
0716: propertiesMap = new HashMap();
0717: requestMap.put(propertiesMapId, propertiesMap);
0718: }
0719:
0720: // Get properties for this component.
0721: String propertiesId = getClientId(context);
0722: Properties properties = (Properties) propertiesMap
0723: .get(propertiesId);
0724: if (properties == null) {
0725: properties = new Properties();
0726: propertiesMap.put(propertiesId, properties);
0727: }
0728:
0729: return properties;
0730: }
0731:
0732: /**
0733: * Helper method to get the number of rows found for this component that
0734: * have a rendered property of true.
0735: *
0736: * @return The number of rendered rows.
0737: */
0738: private int getRowCount() {
0739: // Get properties cached in request map.
0740: Properties properties = getProperties();
0741: int rowCount = (properties != null) ? properties.getRowCount()
0742: : -1;
0743: if (rowCount == -1) {
0744: rowCount = 0; // Initialize min value.
0745:
0746: // Get all TableColumn children at the same level of the tree.
0747: Iterator kids = null;
0748: TableColumn col = getTableColumnAncestor();
0749: if (col != null) {
0750: kids = col.getTableColumnChildren();
0751: } else {
0752: TableRowGroup group = getTableRowGroupAncestor();
0753: kids = (group != null) ? group.getTableColumnChildren()
0754: : null;
0755: }
0756:
0757: // Get max row count for this level of the tree.
0758: if (kids != null) {
0759: while (kids.hasNext()) {
0760: int result = getRowCount((TableColumn) kids.next());
0761: if (rowCount < result) {
0762: rowCount = result;
0763: }
0764: }
0765: }
0766: }
0767: properties.setRowCount(rowCount);
0768: return rowCount;
0769: }
0770:
0771: /**
0772: * Helper method to get the number of rows found for this component that
0773: * have a rendered property of true.
0774: *
0775: * @param component TableColumn to be rendered.
0776: * @return The first selectId property found.
0777: */
0778: private int getRowCount(TableColumn component) {
0779: int count = 0;
0780: if (component == null) {
0781: log("getRowCount",
0782: "Cannot obtain row count, TableColumn is null"); //NOI18N
0783: return count;
0784: }
0785:
0786: // Get max row count for nested TableColumn children.
0787: Iterator kids = component.getTableColumnChildren();
0788: if (kids.hasNext()) {
0789: while (kids.hasNext()) {
0790: TableColumn col = (TableColumn) kids.next();
0791: if (!col.isRendered()) {
0792: continue;
0793: }
0794: int result = getRowCount(col);
0795: if (count < result) {
0796: count = result;
0797: }
0798: }
0799: }
0800: // Include root TableColumn component in count.
0801: return ++count;
0802: }
0803:
0804: /**
0805: * Helper method to get the data type of the data element referenced by the
0806: * alignKey property.
0807: *
0808: * @return The data type of the data element.
0809: */
0810: private Class getType() {
0811: // Note: Avoid using getSourceData when possible. If developers do not
0812: // cache their TableDataProvider objects, this may cause providers to be
0813: // recreated, for each reference, which affects performance. Instead,
0814: // get the type cached in TableRowGroup.
0815: TableRowGroup group = getTableRowGroupAncestor();
0816: if (group == null) {
0817: log("getType",
0818: "Cannot obtain data type, TableRowGroup is null"); //NOI18N
0819: return null;
0820: }
0821:
0822: // Get FieldKey.
0823: FieldKey key = null;
0824: if (getAlignKey() instanceof FieldKey) {
0825: // If value binding resolves to FieldKey, use as is.
0826: key = (FieldKey) getAlignKey();
0827: } else if (getAlignKey() != null) {
0828: key = group.getFieldKey(getAlignKey().toString());
0829: }
0830: return (key != null) ? group.getType(key) : String.class;
0831: }
0832:
0833: /**
0834: * Helper method to get Theme objects.
0835: *
0836: * @return The current theme.
0837: */
0838: private Theme getTheme() {
0839: return ThemeUtilities.getTheme(getFacesContext());
0840: }
0841:
0842: /**
0843: * Helper method to test if column footers should be rendered.
0844: * <p>
0845: * Note: Since headers and footers are optional, we do not render them by
0846: * default. However, if any of the properties above are set, they must be
0847: * set for all columns, including nested columns. Otherwise, we may end up
0848: * with no header or footer and columns shift left. Alternatively,
0849: * developers could add an empty string for each property.
0850: * </p>
0851: */
0852: private boolean isColumnFooterRendered() {
0853: boolean result = false; // Assume no headers or footers are used.
0854: TableRowGroup group = getTableRowGroupAncestor();
0855: if (group == null) {
0856: log("isColumnFooterRendered", //NOI18N
0857: "Cannot determine if column footer is rendered, TableRowGroup is null"); //NOI18N
0858: return result;
0859: }
0860:
0861: // Test the footerText property for all TableColumn components.
0862: Iterator kids = group.getTableColumnChildren();
0863: while (kids.hasNext()) {
0864: TableColumn col = (TableColumn) kids.next();
0865: if (isColumnFooterRendered(col)) {
0866: result = true;
0867: break;
0868: }
0869: }
0870: return result;
0871: }
0872:
0873: /**
0874: * Helper method to test the footerText property for nested TableColumn
0875: * components.
0876: *
0877: * @param component TableColumn component to render.
0878: */
0879: private boolean isColumnFooterRendered(TableColumn component) {
0880: boolean rendered = false;
0881: if (component == null) {
0882: log("isColumnFooterRendered", //NOI18N
0883: "Cannot determine if column footer is rendered, TableColumn is null"); //NOI18N
0884: return rendered;
0885: }
0886:
0887: // Test the footerText property for all TableColumn components.
0888: Iterator kids = component.getTableColumnChildren();
0889: if (kids.hasNext()) {
0890: while (kids.hasNext()) {
0891: TableColumn col = (TableColumn) kids.next();
0892: if (isColumnFooterRendered(col)) {
0893: // When footer text is found, don't go any further.
0894: return true;
0895: }
0896: }
0897: }
0898:
0899: // If either a facet or text are defined, set rendered property.
0900: UIComponent facet = component.getFacet(COLUMN_FOOTER_FACET);
0901: if (facet != null || component.getFooterText() != null) {
0902: rendered = true;
0903: }
0904: return rendered;
0905: }
0906:
0907: /**
0908: * Helper method to test if column headers should be rendered.
0909: * <p>
0910: * Note: Since headers and footers are optional, we do not render them by
0911: * default. However, if any of the properties above are set, they must be
0912: * set for all columns, including nested columns. Otherwise, we may end up
0913: * with no header or footer and columns shift left. Alternatively,
0914: * developers could add an empty string for each property.
0915: * </p>
0916: */
0917: private boolean isColumnHeaderRendered() {
0918: boolean result = false; // Assume no headers or footers are used.
0919: TableRowGroup group = getTableRowGroupAncestor();
0920: if (group == null) {
0921: log("isColumnHeaderRendered", //NOI18N
0922: "Cannot determine if column header is rendered, TableRowGroup is null"); //NOI18N
0923: return result;
0924: }
0925:
0926: // Test the headerText property for all TableColumn components.
0927: Iterator kids = group.getTableColumnChildren();
0928: while (kids.hasNext()) {
0929: TableColumn col = (TableColumn) kids.next();
0930: if (isColumnHeaderRendered(col)) {
0931: result = true;
0932: break;
0933: }
0934: }
0935: return result;
0936: }
0937:
0938: /**
0939: * Helper method to test the headerText property for nested TableColumn
0940: * components.
0941: *
0942: * @param component TableColumn component to render.
0943: */
0944: private boolean isColumnHeaderRendered(TableColumn component) {
0945: boolean rendered = false;
0946: if (component == null) {
0947: log("isColumnHeaderRendered", //NOI18N
0948: "Cannot determine if column header is rendered, TableColumn is null"); //NOI18N
0949: return rendered;
0950: }
0951:
0952: // Test the headerText property for all TableColumn components.
0953: Iterator kids = component.getTableColumnChildren();
0954: if (kids.hasNext()) {
0955: while (kids.hasNext()) {
0956: TableColumn col = (TableColumn) kids.next();
0957: if (isColumnHeaderRendered(col)) {
0958: // When header text is found, don't go any further.
0959: return true;
0960: }
0961: }
0962: }
0963:
0964: // If either a facet or text are defined, set rendered property.
0965: UIComponent facet = component.getFacet(COLUMN_HEADER_FACET);
0966: if (facet != null || component.getHeaderText() != null) {
0967: rendered = true;
0968: }
0969: return rendered;
0970: }
0971:
0972: /**
0973: * Helper method to determine if table is empty.
0974: * <p>
0975: * Note: We must determine if column headers are available for all or
0976: * individual TableRowGroup components. That is, there could be a single
0977: * column header for all row groups or one for each group. If there is more
0978: * than one column header, we must test the row count of all groups. If
0979: * there is only one column header and other groups have more than one row,
0980: * we want to make sorting available. Thus, sorting is available only there
0981: * is more than on row for all row groups.
0982: * </p>
0983: * @return true if sorting should be available, else false.
0984: */
0985: private boolean isEmptyTable() {
0986: boolean result = false;
0987: Table table = getTableAncestor();
0988: TableRowGroup group = getTableRowGroupAncestor();
0989: if (table != null && group != null) {
0990: // Get total rows and headers for all TableRowGroup components.
0991: int rows = table.getRowCount();
0992: int headers = table.getColumnHeadersCount();
0993: result = (headers > 1) ? !(group.getRowCount() > 1) // Test individual groups.
0994: : rows == 0 || rows == 1; // No sorting for single row.
0995: }
0996: return result;
0997: }
0998:
0999: /**
1000: * Helper method to test if table column footers should be rendered.
1001: * <p>
1002: * Note: Since headers and footers are optional, we do not render them by
1003: * default. However, if any of the properties above are set, they must be
1004: * set for all columns, including nested columns. Otherwise, we may end up
1005: * with no header or footer and columns shift left. Alternatively,
1006: * developers could add an empty string for each property.
1007: * </p>
1008: */
1009: private boolean isTableColumnFooterRendered() {
1010: boolean result = false; // Assume no headers or footers are used.
1011: TableRowGroup group = getTableRowGroupAncestor();
1012: if (group == null) {
1013: log("isTableColumnFooterRendered", //NOI18N
1014: "Cannot determine if table column footer is rendered, TableRowGroup is null"); //NOI18N
1015: return result;
1016: }
1017:
1018: // Test the tableFooterText property for all TableColumn components.
1019: Iterator kids = group.getTableColumnChildren();
1020: while (kids.hasNext()) {
1021: TableColumn col = (TableColumn) kids.next();
1022: if (isTableColumnFooterRendered(col)) {
1023: result = true;
1024: break;
1025: }
1026: }
1027: return result;
1028: }
1029:
1030: /**
1031: * Helper method to test the tableFooterText property for nested TableColumn
1032: * components.
1033: *
1034: * @param component TableColumn component to render.
1035: */
1036: private boolean isTableColumnFooterRendered(TableColumn component) {
1037: boolean rendered = false;
1038: if (component == null) {
1039: log("isTableColumnFooterRendered", //NOI18N
1040: "Cannot determine if table column footer is rendered, TableColumn is null"); //NOI18N
1041: return rendered;
1042: }
1043:
1044: // Test the tableFooterText property for all TableColumn components.
1045: Iterator kids = component.getTableColumnChildren();
1046: if (kids.hasNext()) {
1047: while (kids.hasNext()) {
1048: TableColumn col = (TableColumn) kids.next();
1049: if (isTableColumnFooterRendered(col)) {
1050: // When footer text is found, don't go any further.
1051: return true;
1052: }
1053: }
1054: }
1055:
1056: // If either a facet or text are defined, set rendered property.
1057: UIComponent facet = component
1058: .getFacet(TABLE_COLUMN_FOOTER_FACET);
1059: if (facet != null || component.getTableFooterText() != null) {
1060: rendered = true;
1061: }
1062: return rendered;
1063: }
1064:
1065: /**
1066: * Log fine messages.
1067: */
1068: private void log(String method, String message) {
1069: // Get class.
1070: Class clazz = this .getClass();
1071: if (LogUtil.fineEnabled(clazz)) {
1072: // Log method name and message.
1073: LogUtil.fine(clazz, clazz.getName() + "." + method + ": "
1074: + message); //NOI18N
1075: }
1076: }
1077:
1078: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1079: // Inner classes
1080: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1081:
1082: /**
1083: * Object used to cache properties in the request.
1084: */
1085: private class Properties {
1086: // The TableColumn ancestor enclosing this component.
1087: private TableColumn tableColumn = null;
1088:
1089: // A List of TableColumn children found for this component.
1090: private List tableColumnChildren = null;
1091:
1092: // The TableRowGroup ancestor enclosing this component.
1093: private TableRowGroup tableRowGroup = null;
1094:
1095: // The number of columns to be rendered.
1096: private int columnCount = -1;
1097:
1098: // The number of rows to be rendered for headers and footers.
1099: private int rowCount = -1;
1100:
1101: /** Default constructor. */
1102: public Properties() {
1103: }
1104:
1105: /**
1106: * Get the number of columns found for this component that have a
1107: * rendered property of true.
1108: *
1109: * @return The number of rendered columns.
1110: */
1111: public int getColumnCount() {
1112: return columnCount;
1113: }
1114:
1115: /**
1116: * Set the number of columns found for this component that have a
1117: * rendered property of true.
1118: *
1119: * @param columnCount The number of rendered columns.
1120: */
1121: public void setColumnCount(int columnCount) {
1122: this .columnCount = columnCount;
1123: }
1124:
1125: /**
1126: * Get the number of rows found for this component that have a rendered
1127: * property of true.
1128: *
1129: * @return The number of rendered rows.
1130: */
1131: private int getRowCount() {
1132: return rowCount;
1133: }
1134:
1135: /**
1136: * Set the number of rows found for this component that have a rendered
1137: * property of true.
1138: *
1139: * @param rowCount The number of rendered rows.
1140: */
1141: private void setRowCount(int rowCount) {
1142: this .rowCount = rowCount;
1143: }
1144:
1145: /**
1146: * Get the closest TableColumn ancestor that encloses this component.
1147: *
1148: * @return The TableColumn ancestor.
1149: */
1150: public TableColumn getTableColumnAncestor() {
1151: return tableColumn;
1152: }
1153:
1154: /**
1155: * Get the closest TableColumn ancestor that encloses this component.
1156: *
1157: * @param tableColumn The TableColumn ancestor.
1158: */
1159: public void setTableColumnAncestor(TableColumn tableColumn) {
1160: this .tableColumn = tableColumn;
1161: }
1162:
1163: /**
1164: * Get the TableColumn children found for this component.
1165: *
1166: * @return The TableColumn children.
1167: */
1168: public List getTableColumnChildren() {
1169: return tableColumnChildren;
1170: }
1171:
1172: /**
1173: * Set the TableColumn children found for this component.
1174: *
1175: * @param tableColumnChildren The TableColumn children.
1176: */
1177: public void setTableColumnChildren(List tableColumnChildren) {
1178: this .tableColumnChildren = tableColumnChildren;
1179: }
1180:
1181: /**
1182: * Get the closest TableRowGroup ancestor that encloses this component.
1183: *
1184: * @return The TableRowGroup ancestor.
1185: */
1186: public TableRowGroup getTableRowGroupAncestor() {
1187: return tableRowGroup;
1188: }
1189:
1190: /**
1191: * Set the closest TableRowGroup ancestor that encloses this component.
1192: *
1193: * @param tableRowGroup The TableRowGroup ancestor.
1194: */
1195: public void setTableRowGroupAncestor(TableRowGroup tableRowGroup) {
1196: this.tableRowGroup = tableRowGroup;
1197: }
1198: }
1199: }
|