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.event;
042:
043: import com.sun.data.provider.SortCriteria;
044: import com.sun.rave.web.ui.component.DropDown;
045: import com.sun.rave.web.ui.component.Table;
046: import com.sun.rave.web.ui.component.TableActions;
047: import com.sun.rave.web.ui.component.TableColumn;
048: import com.sun.rave.web.ui.component.TableHeader;
049: import com.sun.rave.web.ui.component.TablePanels;
050: import com.sun.rave.web.ui.component.TableRowGroup;
051: import com.sun.rave.web.ui.util.LogUtil;
052:
053: import java.util.Iterator;
054: import java.util.Map;
055:
056: import javax.faces.component.UIComponent;
057: import javax.faces.event.AbortProcessingException;
058: import javax.faces.event.ActionEvent;
059: import javax.faces.event.ActionListener;
060:
061: /**
062: * A listener for receiving sort action events. Depending on the id of the event
063: * source, SortCriteria objects are either added as next level sort, set as the
064: * primary sort, or all sorting currently applied is cleared.
065: * <p>
066: * A class that is interested in receiving such events registers itself with the
067: * source TableColumn of interest, by calling addActionListener().
068: * </p><p>
069: * Note: To see the messages logged by this class, set the following global
070: * defaults in your JDK's "jre/lib/logging.properties" file.
071: * </p><p><pre>
072: * java.util.logging.ConsoleHandler.level = FINE
073: * com.sun.rave.web.ui.event.TableSortActionListener.level = FINE
074: * </pre></p>
075: */
076: public class TableSortActionListener implements ActionListener {
077: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
078: // Process methods
079: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
080:
081: /**
082: * Invoked when the action described by the specified ActionEvent occurs.
083: * The source parent is expected to be a Table or TableColumn object.
084: *
085: * @param event The ActionEvent that has occurred
086: *
087: * @exception AbortProcessingException Signal the JavaServer Faces
088: * implementation that no further processing on the current event
089: * should be performed.
090: */
091: public void processAction(ActionEvent event)
092: throws AbortProcessingException {
093: UIComponent source = (event != null) ? (UIComponent) event
094: .getSource() : null;
095: if (source == null) {
096: log("processAction", //NOI18N
097: "Cannot process action, ActionEvent source is null"); //NOI18N
098: return;
099: }
100:
101: // If the parent is TableColumn, this is an action from a column sort
102: // button. If the parent is Table, this action is either from the clear
103: // sort button or the custom sort panel.
104: TableColumn col = getTableColumnAncestor(source);
105: if (col != null) {
106: processTableColumn(col, source.getId());
107: } else {
108: Table table = getTableAncestor(source);
109: if (table != null) {
110: processTable(table, source.getId());
111: } else {
112: log("processAction",
113: "Cannot process action, Table is null"); //NOI18N
114: }
115: }
116: }
117:
118: /**
119: * Helper method to process Table components.
120: *
121: * @param component Table component being sorted.
122: * @param id The source id.
123: */
124: private void processTable(Table component, String id) {
125: if (component == null) {
126: log("processTable",
127: "Cannot process Table action, Table is null"); //NOI18N
128: return;
129: }
130:
131: // Clear sort for each TableRowGroup child. This action is the same for
132: // both the clear sort button and the custom sort panel.
133: if (id.equals(TableActions.CLEAR_SORT_BUTTON_ID)
134: || id.equals(TablePanels.SORT_PANEL_SUBMIT_BUTTON_ID)) {
135: Iterator kids = component.getTableRowGroupChildren();
136: while (kids.hasNext()) {
137: TableRowGroup group = (TableRowGroup) kids.next();
138: group.clearSort();
139: }
140: }
141:
142: // Set sort for custom sort panel.
143: if (id.equals(TablePanels.SORT_PANEL_SUBMIT_BUTTON_ID)) {
144: customSort(component);
145: }
146: }
147:
148: /**
149: * Helper method to process TableColumn components.
150: *
151: * @param component TableColumn component being sorted.
152: * @param id The source id.
153: */
154: private void processTableColumn(TableColumn component, String id) {
155: if (component == null) {
156: log("processTableColumn", //NOI18N
157: "Cannot process TableColumn action, TableColumn is null"); //NOI18N
158: return;
159: }
160:
161: // We must determine if sorting applies to all TableRowGroup components
162: // or an individual component. That is, there could be a single column
163: // header for all row groups or one for each group. If there is more
164: // than one column header, we will apply the sort only for the group in
165: // which the TableColumn component belongs. If there is only one column
166: // header, sorting applies to all groups.
167: Table table = component.getTableAncestor();
168: if (table != null && table.getColumnHeadersCount() > 1) {
169: TableRowGroup group = component.getTableRowGroupAncestor();
170: setSort(group, id, component.getSortCriteria());
171: } else {
172: sort(table, id, component.getSortCriteria());
173: }
174: }
175:
176: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177: // Sort methods
178: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
179:
180: /**
181: * Set the sort for Table components. This sort is applied to
182: * all TableRowGroup chlidren.
183: *
184: * @param component Table component being sorted.
185: * @param id The source id.
186: * @param criteria SortCriteria to find column index.
187: */
188: private void sort(Table component, String id, SortCriteria criteria) {
189: if (component == null || criteria == null) {
190: log("sort", "Cannot sort, Table or SortCriteria is null"); //NOI18N
191: return;
192: }
193:
194: // Get the index associated with the TableColumn node to be sorted.
195: int index = getNodeIndex(component, criteria.getCriteriaKey());
196:
197: // Iterate over each TableRowGroup child and set the sort.
198: Iterator kids = component.getTableRowGroupChildren();
199: while (kids.hasNext()) {
200: // When multiple row groups are used, we need to find the unique
201: // sort value binding associated with the column for each group.
202: TableRowGroup group = (TableRowGroup) kids.next();
203: TableColumn col = getTableColumn(group, index);
204: setSort(group, id, (col != null) ? col.getSortCriteria()
205: : null);
206: }
207: }
208:
209: /**
210: * Set the sort for the given TableRowGroup component.
211: *
212: * @param component Table component being sorted.
213: * @param id The source id.
214: * @param criteria SortCriteria to find column index.
215: */
216: private void setSort(TableRowGroup component, String id,
217: SortCriteria criteria) {
218: if (component == null) {
219: log("setSort", "Cannot set sort, TableRowGroup is null"); //NOI18N
220: return;
221: }
222:
223: if (id.equals(TableHeader.ADD_SORT_BUTTON_ID)) {
224: component.addSort(criteria);
225: } else if (id.equals(TableHeader.SELECT_SORT_BUTTON_ID)
226: || id.equals(TableHeader.PRIMARY_SORT_BUTTON_ID)
227: || id.equals(TableHeader.PRIMARY_SORT_LINK_ID)) {
228: component.clearSort();
229: component.addSort(criteria);
230: } else if (id.equals(TableHeader.TOGGLE_SORT_BUTTON_ID)) {
231: if (criteria != null) {
232: criteria.setAscending(component
233: .isDescendingSort(criteria));
234: }
235: component.addSort(criteria);
236: } else {
237: log("setSort", "Cannot add sort, unknown component Id: "
238: + id); //NOI18N
239: }
240: }
241:
242: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
243: // Custom sort methods
244: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
245:
246: /**
247: * Process the properties for the custom table sort panel and set the sort.
248: *
249: * @param component Table component being sorted.
250: */
251: private void customSort(Table component) {
252: UIComponent panels = component
253: .getFacet(Table.EMBEDDED_PANELS_ID);
254: if (panels == null) {
255: log("customSort", //NOI18N
256: "Cannot custom sort, embedded panels facet is null"); //NOI18N
257: return;
258: }
259:
260: // Get menu children.
261: Map map = panels.getFacets();
262: UIComponent primarySortColumnMenu = (UIComponent) map
263: .get(TablePanels.PRIMARY_SORT_COLUMN_MENU_ID);
264: UIComponent primarySortOrderMenu = (UIComponent) map
265: .get(TablePanels.PRIMARY_SORT_ORDER_MENU_ID);
266: UIComponent secondarySortColumnMenu = (UIComponent) map
267: .get(TablePanels.SECONDARY_SORT_COLUMN_MENU_ID);
268: UIComponent secondarySortOrderMenu = (UIComponent) map
269: .get(TablePanels.SECONDARY_SORT_ORDER_MENU_ID);
270: UIComponent tertiarySortColumnMenu = (UIComponent) map
271: .get(TablePanels.TERTIARY_SORT_COLUMN_MENU_ID);
272: UIComponent tertiarySortOrderMenu = (UIComponent) map
273: .get(TablePanels.TERTIARY_SORT_ORDER_MENU_ID);
274:
275: // Set primary sort.
276: if (primarySortColumnMenu != null
277: && primarySortOrderMenu != null
278: && primarySortColumnMenu instanceof DropDown
279: && primarySortOrderMenu instanceof DropDown) {
280: int index = getNodeIndex(component,
281: (String) ((DropDown) primarySortColumnMenu)
282: .getSelected());
283: setCustomSort(component, index, Boolean.valueOf(
284: (String) ((DropDown) primarySortOrderMenu)
285: .getSelected()).booleanValue());
286: } else {
287: log("customSort", //NOI18N
288: "Cannot set custom sort, primary sort column menu is null"); //NOI18N
289: }
290:
291: // Set secondary sort.
292: if (secondarySortColumnMenu != null
293: && secondarySortOrderMenu != null
294: && secondarySortColumnMenu instanceof DropDown
295: && secondarySortOrderMenu instanceof DropDown) {
296: int index = getNodeIndex(component,
297: (String) ((DropDown) secondarySortColumnMenu)
298: .getSelected());
299: setCustomSort(component, index, Boolean.valueOf(
300: (String) ((DropDown) secondarySortOrderMenu)
301: .getSelected()).booleanValue());
302: } else {
303: log("customSort", //NOI18N
304: "Cannot set custom sort, secondary sort column menu is null"); //NOI18N
305: }
306:
307: // Set tertiary sort.
308: if (tertiarySortColumnMenu != null
309: && tertiarySortOrderMenu != null
310: && tertiarySortColumnMenu instanceof DropDown
311: && tertiarySortOrderMenu instanceof DropDown) {
312: int index = getNodeIndex(component,
313: (String) ((DropDown) tertiarySortColumnMenu)
314: .getSelected());
315: setCustomSort(component, index, Boolean.valueOf(
316: (String) ((DropDown) tertiarySortOrderMenu)
317: .getSelected()).booleanValue());
318: } else {
319: log("customSort", //NOI18N
320: "Cannot set custom sort, tertiary sort column menu is null"); //NOI18N
321: }
322: }
323:
324: /**
325: * Set the sort for the custom sort panel. This sort is applied to all
326: * TableRowGroup chlidren.
327: *
328: * @param component Table component being sorted.
329: * @param index The index associated with the TableColumn node to be sorted.
330: * @param descending The sort order to be applied.
331: */
332: private void setCustomSort(Table component, int index,
333: boolean descending) {
334: if (component == null || index < 0) {
335: log("setCustomSort", //NOI18N
336: "Cannot set custom sort, Table is null or index < 0"); //NOI18N
337: return;
338: }
339:
340: // Iterate over each TableRowGroup child and set the sort.
341: Iterator kids = component.getTableRowGroupChildren();
342: while (kids.hasNext()) {
343: // When multiple row groups are used, we need to find the sort value
344: // binding associated with the column for each group.
345: TableRowGroup group = (TableRowGroup) kids.next();
346: TableColumn col = getTableColumn(group, index);
347:
348: // Get new SortCriteria to add and set sort order.
349: SortCriteria criteria = (col != null) ? col
350: .getSortCriteria() : null;
351: if (criteria != null) {
352: criteria.setAscending(!descending);
353: }
354: group.addSort(criteria);
355: }
356: }
357:
358: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
359: // Nested TableColumn methods
360: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
361:
362: /**
363: * Helper method to get the node index associated with the given criteria
364: * key (i.e., a value binding expression string or FieldKey id).
365: *
366: * @param component Table for which to extract children.
367: * @param criteriaKey The criteria key associated with the column sort.
368: * @return The node index or -1 if TableColumn was not found.
369: */
370: private int getNodeIndex(Table component, String criteriaKey) {
371: // If the criteria key is an empty string, "None" has been selected.
372: if (component == null || criteriaKey == null
373: || criteriaKey.length() == 0) {
374: log("getNodeIndex", //NOI18N
375: "Cannot obtain node index, Table or sort criteria key is null"); //NOI18N
376: return -1;
377: }
378:
379: // Use the first TableRowGroup child to obtain column index.
380: TableRowGroup group = component.getTableRowGroupChild();
381: if (group != null) {
382: Integer index = new Integer(-1); // Initialize index for testing.
383:
384: // Get node index for nested TableColumn children.
385: Iterator kids = group.getTableColumnChildren();
386: while (kids.hasNext()) {
387: TableColumn col = (TableColumn) kids.next();
388: index = new Integer(index.intValue() + 1); // Increment index.
389: int result = getNodeIndex(col, criteriaKey, index);
390: if (result > -1) {
391: return result; // Match found.
392: }
393: }
394: } else {
395: log("getNodeIndex", //NOI18N
396: "Cannot obtain node index, TableRowGroup is null"); //NOI18N
397: }
398: return -1;
399: }
400:
401: /**
402: * Helper method to get the node index, associated with the given criteria
403: * key (i.e., a value binding expression string or FieldKey id), for nested
404: * TableColumn components.
405: *
406: * @param component Table for which to extract children.
407: * @param criteriaKey The criteria key associated with the column sort.
408: * @param index An index associated with the current TableColumn node.
409: * @return The node index or -1 if TableColumn was not found.
410: */
411: private int getNodeIndex(TableColumn component, String criteriaKey,
412: Integer index) {
413: // If the criteria key is an empty string, "None" has been selected.
414: if (component == null || criteriaKey == null
415: || criteriaKey.length() == 0) {
416: log("getNodeIndex", //NOI18N
417: "Cannot obtain node index, TableColumn or sort criteria key is null"); //NOI18N
418: return -1;
419: }
420:
421: // Get node index for nested TableColumn children.
422: Iterator kids = component.getTableColumnChildren();
423: if (kids.hasNext()) {
424: while (kids.hasNext()) {
425: TableColumn col = (TableColumn) kids.next();
426: index = new Integer(index.intValue() + 1); // Increment index.
427: int result = getNodeIndex(col, criteriaKey, index);
428: if (result > -1) {
429: return result; // Match found.
430: }
431: }
432: }
433:
434: // Find index by matching TableColumn SortCriteria with given key.
435: SortCriteria criteria = component.getSortCriteria();
436: String key = (criteria != null) ? criteria.getCriteriaKey()
437: : null;
438: return (key != null && key.equals(criteriaKey)) ? index
439: .intValue() : -1;
440: }
441:
442: /**
443: * Helper method to get the TableColumn associated with the given node index.
444: *
445: * @param component TableRowGroup for which to extract children.
446: * @param index The index associated with the TableColumn node to be sorted.
447: * @return The TableColumn associated with index.
448: */
449: private TableColumn getTableColumn(TableRowGroup component,
450: int index) {
451: if (component == null) {
452: log("getTableColumn", //NOI18N
453: "Cannot obtain TableColumn component, TableRowGroup is null"); //NOI18N
454: return null;
455: }
456:
457: // Initialize index for testing.
458: index++;
459:
460: // Get node index for nested TableColumn children.
461: Iterator kids = component.getTableColumnChildren();
462: while (kids.hasNext()) {
463: TableColumn col = (TableColumn) kids.next();
464: TableColumn result = getTableColumn(col, --index);
465: if (result != null) {
466: return result; // Match found.
467: }
468: }
469: return null;
470: }
471:
472: /**
473: * Helper method to get the TableColumn associated with the given node index.
474: *
475: * @param component TableColumn for which to extract children.
476: * @param index The index associated with the TableColumn node to be sorted.
477: * @return The TableColumn associated with index.
478: */
479: private TableColumn getTableColumn(TableColumn component, int index) {
480: if (component == null) {
481: log("getTableColumn", //NOI18N
482: "Cannot obtain TableColumn component, TableColumn is null"); //NOI18N
483: return null;
484: }
485:
486: // Get node index for nested TableColumn children.
487: Iterator kids = component.getTableColumnChildren();
488: if (kids.hasNext()) {
489: while (kids.hasNext()) {
490: TableColumn col = (TableColumn) kids.next();
491: TableColumn result = getTableColumn(col, --index);
492: if (result != null) {
493: return result; // Match found.
494: }
495: }
496: }
497:
498: // Find TableColumn by matching index.
499: if (index == 0) {
500: return component;
501: } else {
502: log("getTableColumn", //NOI18N
503: "Cannot obtain TableColumn component, cannot match node index"); //NOI18N
504: }
505: return null;
506: }
507:
508: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
509: // Child methods
510: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
511:
512: /**
513: * Get the closest Table ancestor that encloses this component.
514: *
515: * @component UIcomponent to retrieve ancestor.
516: * @return The Table ancestor.
517: */
518: private Table getTableAncestor(UIComponent component) {
519: while (component != null) {
520: component = component.getParent();
521: if (component instanceof Table) {
522: return (Table) component;
523: }
524: }
525: return null;
526: }
527:
528: /**
529: * Get the closest TableColumn ancestor that encloses this component.
530: *
531: * @component UIcomponent to retrieve ancestor.
532: * @return The TableColumn ancestor.
533: */
534: private TableColumn getTableColumnAncestor(UIComponent component) {
535: while (component != null) {
536: component = component.getParent();
537: if (component instanceof TableColumn) {
538: return (TableColumn) component;
539: }
540: }
541: return null;
542: }
543:
544: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
545: // Misc methods
546: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
547:
548: /**
549: * Log fine messages.
550: */
551: private void log(String method, String message) {
552: // Get class.
553: Class clazz = this .getClass();
554: if (LogUtil.fineEnabled(clazz)) {
555: // Log method name and message.
556: LogUtil.fine(clazz, clazz.getName() + "." + method + ": "
557: + message); //NOI18N
558: }
559: }
560: }
|