All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.trinidad.component.UIXTreeTable Maven / Gradle / Ivy

The newest version!
// WARNING: This file was automatically generated. Do not edit it directly,
//          or you will lose your changes.

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
*/
package org.apache.myfaces.trinidad.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;
import org.apache.myfaces.trinidad.event.FocusEvent;
import org.apache.myfaces.trinidad.event.RangeChangeEvent;
import org.apache.myfaces.trinidad.event.RangeChangeListener;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.util.ComponentUtils;

/**
 *
 * 
 *           The Apache Trinidad TreeTable is used to display data that is structured in a hierarchical format.
 *           This component displays a hierarchy
 *           in a UI similar to a Trinidad Table, and is more elaborate than the Tree component.
 *           TreeTable supports displaying columns of data per element in the hierarchy.
 *           Unlike the Tree component, TreeTable
 *           only supports single rooted hierarchies. The features of the TreeTable
 *           component include mechanisms for focusing in on subtrees (within the main
 *           tree), as well as expanding and collapsing elements in the hierarchy.
 *         
 * 
 *         
 *           Like the Table, the TreeTable's children must be Trinidad
 *           Column components. Like the Tree, the TreeTable has a "nodeStamp"
 *           facet which renders the "Object Name" Column.
 * 
 *           The "Object Name" Column contains the primary identifier of an element
 *           in the hierarchy. For example, in an organization chart of employees, the "Object Name"
 *           Column might be the employee name.
 *         
 *
 * 

Events:

* * * * * * * * * * * * * * * * * * * * * * * * * * *
TypePhasesDescription
org.apache.myfaces.trinidad.event.RowDisclosureEventApply
Request
Values
Invoke
Application
The expansion event is generated for a table when the detail facet of a row is expanded or collapsed. For tree or a treeTable, the expansion event is generated when tree nodes are expanded or collapsed.
org.apache.myfaces.trinidad.event.SelectionEventApply
Request
Values
Invoke
Application
The selection event is delivered when the table selection changes.
org.apache.myfaces.trinidad.event.FocusEventApply
Request
Values
Invoke
Application
Event delivered when user clicks to focus on (or zoom into) a particular element's subtree of children. The TreeTable responds to this event by modifying the "focusPath" property appropriately. Subsequently, any registered FocusListener instances are called.
org.apache.myfaces.trinidad.event.AttributeChangeEventInvoke
Application
Apply
Request
Values
Event delivered to describe an attribute change. Attribute change events are not delivered for any programmatic change to a property. They are only delivered when a renderer changes a property without the application's specific request. An example of an attribute change event might include the width of a column that supported client-side resizing.
*/ public class UIXTreeTable extends UIXTree { static public final FacesBean.Type TYPE = new FacesBean.Type( UIXTree.TYPE); static public final PropertyKey ROOT_NODE_RENDERED_KEY = TYPE.registerKey("rootNodeRendered", Boolean.class, Boolean.TRUE); static public final PropertyKey ROWS_BY_DEPTH_KEY = TYPE.registerKey("rowsByDepth", int[].class, null, 0, PropertyKey.Mutable.SOMETIMES); static public final PropertyKey RANGE_CHANGE_LISTENER_KEY = TYPE.registerKey("rangeChangeListener", MethodExpression.class); static public final PropertyKey HEIGHT_KEY = TYPE.registerKey("height", String.class); static public final String COMPONENT_FAMILY = "org.apache.myfaces.trinidad.TreeTable"; static public final String COMPONENT_TYPE = "org.apache.myfaces.trinidad.TreeTable"; /** * Construct an instance of the UIXTreeTable. */ public UIXTreeTable() { super("org.apache.myfaces.trinidad.BaseTreeTable"); } /** * Override to update the container client id cache before decode */ @Override public void decode(FacesContext context) { _resetContainerClientIdCache(); super.decode(context); } /** * Override to update the container client id cache before validations */ @Override public void processValidators(FacesContext context) { _resetContainerClientIdCache(); super.processValidators(context); } /** * Override to update the container client id cache before updates */ @Override public void processUpdates(FacesContext context) { _resetContainerClientIdCache(); super.processUpdates(context); } /** * Override to update the container client id cache before encode */ @Override protected void __encodeBegin(FacesContext context) throws IOException { _resetContainerClientIdCache(); super.__encodeBegin(context); } /** * Override to return clientd ids with no currency for items in header/footer facets */ @Override public String getContainerClientId(FacesContext context, UIComponent child) { String id; if (_containerClientIdCache == null || _isStampedChild(child)) { // call the UIXCollection getContainerClientId, which attaches currency string to the client id id = getContainerClientId(context); } else { // The target is not a stamped child, so return a client id with no currency string id = getClientId(context); } return id; } @Deprecated public void setRangeChangeListener(MethodBinding binding) { setRangeChangeListener(adaptMethodBinding(binding)); } /** * Gets the maximum number of rows to show. * This changes depending on the depth of the current row in the tree * hierarchy. * The rows per depth is obtained from * {@link #getRowsByDepth}. * @return 0 if all rows must be shown at this level. */ @Override public final int getRows() { int depth = getTreeModel().getDepth(); assert depth >= 0; // the root element is selected when depth is zero: if (depth==0) return 1; // the treeTable only shows the first root node. int[] rows = getRowsByDepth(); if ((rows == null) || (rows.length == 0)) return 0; depth--; // in a treeTable, the the first "rows" property affects how many // children of the root element to show. return (depth >= rows.length) ? rows[rows.length - 1] : rows[depth]; } /** * Gets the range start index for the current collection. * The current collection is the children of the parent of the * current rowData. ie: the current collection is the collection of * siblings of the current rowData. * @return zero based index of the row that must be displayed first. * @see #getRowData() */ @Override public final int getFirst() { // "first" does not change per path. It changes per parent path. // this is because "first", "rows" and "rowCount" applies to the container // element and not the current element: Object container = _getContainerPath(); Integer first = _firstMap.get(container); return (first != null) ? first.intValue() : 0; } /** * Sets the range start index for the current collection. * The current collection is the children of the parent of the * current rowData. ie: the current collection is the collection of * siblings of the current rowData. * @param index zero based index of the row that must be displayed first. * @see #getRowData() */ public void setFirst(int index) { // "first" does not change per path. It changes per parent path. // this is because "first", "rows" and "rowCount" applies to the container // element and not the current element: Object container = _getContainerPath(); Map comparant = Collections.emptyMap(); if (_firstMap == comparant) _firstMap = new HashMap(3); if (index <= 0) _firstMap.remove(container); else _firstMap.put(container, Integer.valueOf(index)); } /** * Adds a RangeChangeListener. */ public void addRangeChangeListener(RangeChangeListener listener) { addFacesListener(listener); } /** * Removes a RangeChangeListener. */ public void removeRangeChangeListener(RangeChangeListener listener) { removeFacesListener(listener); } /** * Retrieves all RangeChangeListeners */ public RangeChangeListener[] getRangeChangeListeners() { return (RangeChangeListener[]) getFacesListeners(RangeChangeListener.class); } @Override public Object saveState(FacesContext context) { Object[] array = new Object[2]; array[0] = super.saveState(context); array[1] = (_firstMap.isEmpty()) ? null : _firstMap; if (array[0] == null && array[1] == null) return null; return array; } @Override @SuppressWarnings("unchecked") public void restoreState(FacesContext context, Object state) { Object[] array = (Object[]) state; super.restoreState(context, array[0]); _firstMap = (Map) array[1]; if (_firstMap == null) _firstMap = Collections.emptyMap(); } @Override public void broadcast(FacesEvent event) throws AbortProcessingException { // Notify the specified disclosure listener method (if any) if (event instanceof FocusEvent) { setFocusRowKey(getRowKey()); //pu: Implicitly record a Change for 'focusPath' attribute addAttributeChange("focusPath", getFocusRowKey()); // it is nice to expand the focused item: getDisclosedRowKeys().add(); broadcastToMethodExpression(event, getFocusListener()); } else if (event instanceof RangeChangeEvent) { RangeChangeEvent rce = (RangeChangeEvent) event; setFirst(rce.getNewStart()); broadcastToMethodExpression(event, getRangeChangeListener()); } // Perform standard superclass processing super.broadcast(event); } /** * Gets the stamps. This returns the children of this component plus * the nodeStamp stamp (if any). */ // TODO cache the result. @Override protected final List getStamps() { List children = getChildren(); List stamps; if (children.isEmpty()) { // no children, so use Node stamps as the stamp stamps = super.getStamps(); } else { UIComponent nodeStamp = getNodeStamp(); if (nodeStamp == null) { // no node stamp, so stamp, is only the children stamps = children; } else { // stamps are the children plus the node stamp stamps = new ArrayList(children.size() + 1); stamps.addAll(children); stamps.add(nodeStamp); } } return stamps; } /** * Restores the state for the given stamp. * This method avoids changing the state of facets on columns. */ @Override protected final void restoreStampState(FacesContext context, UIComponent stamp, Object stampState) { if (stamp instanceof UIXColumn) { // if it is a column, we don't want the facets processed. // Only the children: StampState.restoreChildStampState(context, stamp, this, stampState); } else super.restoreStampState(context, stamp, stampState); } /** * Saves the state for the given stamp. * This method avoids changing the state of facets on columns. */ @Override protected final Object saveStampState(FacesContext context, UIComponent stamp) { if (stamp instanceof UIXColumn) { // if it is a column, we don't want the facets processed. // Only the children: return StampState.saveChildStampState(context, stamp, this); } else return super.saveStampState(context, stamp); } @SuppressWarnings("unchecked") @Override protected void processFacetsAndChildren( FacesContext context, PhaseId phaseId) { // process all the facets of this hgrid just once // (except for the "nodeStamp" facet which must be processed once // per row): TableUtils.processFacets(context, this, this, phaseId, UIXTreeTable.NODE_STAMP_FACET); UIComponent nodeStamp = getNodeStamp(); // process any facets of the nodeStamp column: TableUtils.processFacets(context, this, nodeStamp, phaseId, null); // process all the facets of this table's column children: TableUtils.processColumnFacets(context, this, this, phaseId); // recursively process any grandchild columns of the nodeStamp column: TableUtils.processColumnFacets(context, this, nodeStamp, phaseId); Object oldPath = getRowKey(); RowKeySet state = getDisclosedRowKeys(); try { Object path = getFocusRowKey(); setRowKey(path); if (path == null) { HierarchyUtils.__iterateOverTree(context, phaseId, this, state, true); } else { TableUtils.processStampedChildren(context, this, phaseId); processComponent(context, nodeStamp, phaseId); // bug 4688568 if (state.isContained()) { enterContainer(); HierarchyUtils.__iterateOverTree(context, phaseId, this, state, true); } } } finally { setRowKey(oldPath); } } @Override protected boolean visitChildren( VisitContext visitContext, VisitCallback callback) { // need to override to do the default since our superclass // UIXTree does stuff here we don't want return defaultVisitChildren(visitContext, callback); } @Override protected boolean visitUnstampedFacets( VisitContext visitContext, VisitCallback callback) { // Visit the facets except for the node stamp int facetCount = getFacetCount(); if (facetCount > 0) { UIComponent nodeStamp = getNodeStamp(); // if our only facet is the node stamp, we don't need to do this if ((facetCount > 1) || (nodeStamp == null)) { for (UIComponent facet : getFacets().values()) { // ignore the nodeStamp facet, since it is stamped if (facet != nodeStamp) { if (UIXComponent.visitTree(visitContext, facet, callback)) { return true; } } } } } return false; } @Override protected boolean visitData( VisitContext visitContext, VisitCallback callback) { Object focusedPath = getFocusRowKey(); Object oldRowKey = null; // start from the focused area if (focusedPath != null) { oldRowKey = getRowKey(); setRowKey(focusedPath); } boolean done; try { done = super.visitData(new NoColumnFacetsVisitContext(visitContext), callback); } finally { if (focusedPath != null) { setRowKey(oldRowKey); } } return done; } /** * Gets the path of the parent */ private Object _getContainerPath() { Object parentKey = getTreeModel().getContainerRowKey(); return parentKey; } /** * Is target a stamped child UIComponent in the treeTable body */ private boolean _isStampedChild(UIComponent target) { assert _containerClientIdCache != null; return !_containerClientIdCache.containsKey(target); } /** * Reset the cache of child components used in getContainerClientId */ private void _resetContainerClientIdCache() { if(_containerClientIdCache == null) _containerClientIdCache = new IdentityHashMap(); else _containerClientIdCache.clear(); // cache treeTable header/footer items TableUtils.cacheHeaderFooterFacets(this, _containerClientIdCache); // cache child column header/footer items, including nested columns TableUtils.cacheColumnHeaderFooterFacets(this, _containerClientIdCache); UIComponent nodeStamp = getNodeStamp(); if(nodeStamp != null) { // cache nodeStamp header/footer items TableUtils.cacheHeaderFooterFacets(nodeStamp, _containerClientIdCache); // cache any nested columns in nodeStamp facet TableUtils.cacheColumnHeaderFooterFacets(nodeStamp, _containerClientIdCache); } } /** * Gets the internal state of this component. */ @Override Object __getMyStampState() { Object[] state = new Object[2]; state[0] = super.__getMyStampState(); state[1] = (_firstMap.isEmpty()) ? null : _firstMap; return state; } /** * Sets the internal state of this component. * @param stampState the internal state is obtained from this object. */ @Override @SuppressWarnings("unchecked") void __setMyStampState(Object stampState) { Object[] state = (Object[]) stampState; super.__setMyStampState(state[0]); _firstMap = (Map) state[1]; if (_firstMap == null) _firstMap = Collections.emptyMap(); } @Override void __resetMyStampState() { super.__resetMyStampState(); _firstMap = Collections.emptyMap(); } private Map _firstMap = Collections.emptyMap(); // cache of child components inside this treeTable header/footer facets and column header/footer // facets transient private IdentityHashMap _containerClientIdCache = null; /** * Gets If the root node should be rendered or not. Defaults to true. * * @return the new rootNodeRendered value */ final public boolean isRootNodeRendered() { return ComponentUtils.resolveBoolean(getProperty(ROOT_NODE_RENDERED_KEY), true); } /** * Sets If the root node should be rendered or not. Defaults to true. * * @param rootNodeRendered the new rootNodeRendered value */ final public void setRootNodeRendered(boolean rootNodeRendered) { setProperty(ROOT_NODE_RENDERED_KEY, rootNodeRendered ? Boolean.TRUE : Boolean.FALSE); } /** * Gets the maximum number of records that can be displayed at * one time (range size). * Each level of depth in the tree can have a different range size. * The first number in the array sets the range size for the root * collection. Each subsequent number sets the range size for the * corresponding depth. The last number becomes the default for * each subsequent level of depth. * * If a node has more children than * the range size, navigation rows will be rendered above and * below the child nodes. * * @return the new rowsByDepth value */ final public int[] getRowsByDepth() { return (int[])getProperty(ROWS_BY_DEPTH_KEY); } /** * Sets the maximum number of records that can be displayed at * one time (range size). * Each level of depth in the tree can have a different range size. * The first number in the array sets the range size for the root * collection. Each subsequent number sets the range size for the * corresponding depth. The last number becomes the default for * each subsequent level of depth. * * If a node has more children than * the range size, navigation rows will be rendered above and * below the child nodes. * * @param rowsByDepth the new rowsByDepth value */ final public void setRowsByDepth(int[] rowsByDepth) { setProperty(ROWS_BY_DEPTH_KEY, (rowsByDepth)); } /** * Gets a method reference to a rangeChange listener that * will be called when a new range is selected. * * @return the new rangeChangeListener value */ final public MethodExpression getRangeChangeListener() { return (MethodExpression)getProperty(RANGE_CHANGE_LISTENER_KEY); } /** * Sets a method reference to a rangeChange listener that * will be called when a new range is selected. * * @param rangeChangeListener the new rangeChangeListener value */ final public void setRangeChangeListener(MethodExpression rangeChangeListener) { setProperty(RANGE_CHANGE_LISTENER_KEY, (rangeChangeListener)); } /** * Gets Sets a height to the content of the table. * This generates a scrollbar to access all rows in the current range. * * @return the new height value */ final public String getHeight() { return ComponentUtils.resolveString(getProperty(HEIGHT_KEY)); } /** * Sets Sets a height to the content of the table. * This generates a scrollbar to access all rows in the current range. * * @param height the new height value */ final public void setHeight(String height) { setProperty(HEIGHT_KEY, (height)); } @Override public String getFamily() { return COMPONENT_FAMILY; } @Override protected FacesBean.Type getBeanType() { return TYPE; } /** * Construct an instance of the UIXTreeTable. */ protected UIXTreeTable( String rendererType ) { super(rendererType); } static { TYPE.lockAndRegister("org.apache.myfaces.trinidad.TreeTable","org.apache.myfaces.trinidad.BaseTreeTable"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy