org.apache.myfaces.trinidad.component.TableUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trinidad-api Show documentation
Show all versions of trinidad-api Show documentation
Public API for the Apache MyFaces Trinidad project
The newest version!
/*
* 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.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import org.apache.myfaces.trinidad.event.DisclosureEvent;
import org.apache.myfaces.trinidad.event.FocusEvent;
import org.apache.myfaces.trinidad.event.RangeChangeEvent;
import org.apache.myfaces.trinidad.event.RowDisclosureEvent;
import org.apache.myfaces.trinidad.event.SelectionEvent;
import org.apache.myfaces.trinidad.event.SortEvent;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.model.ModelUtils;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.SortStrength;
import org.apache.myfaces.trinidad.model.TreeModel;
/**
* utility methods for dealing with tables.
*/
public final class TableUtils
{
/**
* Gets the index of the last visible row that should be
* displayed by the given table. This is usually
* {@link CollectionComponent#getFirst} added to
* {@link CollectionComponent#getRows} minus 1, but it changes if
* {@link CollectionComponent#getRowCount} returns
* insufficient rows.
* @return if this table is empty, this returns
* {@link CollectionComponent#getFirst()} - 1
*/
public static int getLast(CollectionComponent table)
{
return getLast(table, table.getFirst());
}
/**
* Gets the index of the last visible row that should be
* displayed by the given table. This is usually
* rangeStart added to
* {@link CollectionComponent#getRows} minus 1, but it changes if
* {@link CollectionComponent#getRowCount} returns
* insufficient rows.
* @return if this table is empty, this returns
* rangeStart - 1
*/
public static int getLast(CollectionComponent table, int rangeStart)
{
final int rangeEnd;
int blockSize = table.getRows();
// if the blockSize is zero, that means show everthing.
if (blockSize <= 0)
{
rangeEnd = Integer.MAX_VALUE;
}
else
{
rangeEnd = rangeStart + blockSize;
}
return ModelUtils.findLastIndex(table, rangeStart, rangeEnd) - 1;
}
/**
* Sets up an EL variable on the requestScope.
* @param name The name of the EL variable
* @param value The value of the EL variable
* @return any old value that was bound to the EL variable, or null
* if there was no old value.
*/
@SuppressWarnings("unchecked")
public static Object setupELVariable(FacesContext context, String name, Object value)
{
Map requestMap = context.getExternalContext().getRequestMap();
if (value == null)
return requestMap.remove(name);
else
return requestMap.put(name, value);
}
/**
* Perform a safe expand all.
* Checks to make sure that a complete expand-all operation won't result in
* too many nodes being displayed to the user. If too many nodes will end
* up being displayed, this method only expands the immediate children before
* terminating. Otherwise, a complete expand-all operation is performed.
* @param maxSize the maximum number of nodes to display. A complete expand-all
* operation will not take place if more than this number of nodes
* will end up being displayed.
* @param model this tree model must have its path pointing to a particular
* node which must be a container.
*/
static void __doSafeExpandAll(
TreeModel model,
RowKeySet state,
int maxSize)
{
int size = _getSizeOfTree(model, maxSize);
if ((size < 0) || (size > maxSize))
{
// not safe to do expand all.
// first expand the current node:
state.add();
// now only expand immediate children:
model.enterContainer();
int i=0;
while(true)
{
model.setRowIndex(i++);
if (!model.isRowAvailable())
break;
state.add();
}
model.exitContainer();
}
else // safe to do expand all:
state.addAll();
}
/**
* Computes the number of nodes in a subtree.
* @param model the path must point to a node.
* @param maxSize the maximum number of nodes that will be searched.
* this method returns once this limit is reached.
* @return -1 if the number of nodes is unknown.
* If the limit is hit, then returns the number of nodes that are known
* to exist at the time the limit was hit. This number may be larger than
* maxSize.
*/
static int _getSizeOfTree(TreeModel model, int maxSize)
{
if (model.isRowAvailable() && model.isContainer())
{
model.enterContainer();
try
{
int size = model.getRowCount();
for(int i=0, sz=size; i maxSize)
return size;
model.setRowIndex(i);
int kidSize = _getSizeOfTree(model, maxSize - size);
if (kidSize < 0)
return -1;
size += kidSize;
}
return size;
}
finally
{
model.exitContainer();
}
}
return 0;
}
/**
* This method sets the phaseID of the event
* according to the "immediate" property of this
* component.
* If "immediate" is set to true, this calls
* {@link FacesContext#renderResponse}
*/
static void __handleQueueEvent(UIComponent comp, FacesEvent event)
{
if (_isImmediateEvent(comp, event))
{
String immediateAttr = UIXTree.IMMEDIATE_KEY.getName();
Object isImmediate = comp.getAttributes().get(immediateAttr);
if (Boolean.TRUE.equals(isImmediate))
{
event.setPhaseId(PhaseId.ANY_PHASE);
FacesContext context = FacesContext.getCurrentInstance();
context.renderResponse();
}
else
{
// the event should not execute before model updates are done.
// otherwise, the updates will be done to the wrong rows.
// we can't do this at the end of the UPDATE_MODEL phase because
// if there are errors during that phase, then we want to immediately render
// the response, and not deliver this ui event:
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
}
/**
* Process all the facets of a component; these are
* generally not processed once per row.
* @param skipFacet the name of any facet that should not be processed
* at this time.
*/
@SuppressWarnings("unchecked")
public static void processFacets(
FacesContext context,
final UIXCollection table,
UIComponent component,
final PhaseId phaseId,
String skipFacet)
{
Map facets = component.getFacets();
final UIComponent skip = (skipFacet != null)
? (UIComponent) facets.get(skipFacet)
: null;
new ChildLoop()
{
@Override
protected void process(FacesContext context, UIComponent facet, ComponentProcessingContext cpContext)
{
if (facet != skip)
table.processComponent(context, facet, phaseId);
}
}.runAlways(context, facets.values());
}
/**
* Process all the facets of any children that are columns; these are
* generally not processed once per row.
*/
public static void processColumnFacets(
FacesContext context,
final UIXCollection table,
UIComponent column,
final PhaseId phaseId)
{
new ChildLoop()
{
@Override
protected void process(FacesContext context, UIComponent child, ComponentProcessingContext cpContext)
{
if (child instanceof UIXColumn && child.isRendered())
{
// process any facets of the child column:
processFacets(context, table, child, phaseId, null);
// recursively process the facets of any grandchild columns:
processColumnFacets(context, table, child, phaseId);
}
}
}.runAlways(context, column);
}
/**
* Process all the children of the given table
*/
public static void processStampedChildren(
FacesContext context,
final UIXCollection table,
final PhaseId phaseId)
{
new ChildLoop()
{
@Override
protected void process(FacesContext context, UIComponent child, ComponentProcessingContext cpContext)
{
// make sure that any cached clientIds are cleared so that
// the clientIds are recalculated with the new row index
UIXComponent.clearCachedClientIds(child);
table.processComponent(context, child, phaseId);
}
}.runAlways(context, table);
}
/**
* Retrieves the sort strength for the column with the given sort property from the given table.
* @param parent the Collection object whose columns are searched for matching sortProperty and retrieving
* sort strength from.
* @param sortProperty sort property value to match against column's sortProperty property.
* @return sort strength for the column with the given sort property from the given table.
*/
public static SortStrength findSortStrength(UIXCollection parent, String sortProperty)
{
SortStrength sortStrength = null;
if (sortProperty == null || sortProperty.isEmpty())
return null;
List children = parent.getChildren();
for (UIComponent child : children)
{
if (child instanceof UIXColumn)
{
UIXColumn targetColumn = (UIXColumn)child;
if (sortProperty.equals(targetColumn.getSortProperty()))
{
String strength = targetColumn.getSortStrength();
sortStrength = _toSortStrength(strength);
break;
}
}
}
return sortStrength;
}
/**
* Convert the string value of sort strength to the SortStrength type.
*/
private static SortStrength _toSortStrength(String strength)
{
SortStrength sortStrength = null;
if (strength != null && !strength.isEmpty())
{
try
{
sortStrength = SortStrength.valueOf(strength.toUpperCase());
}
catch (IllegalArgumentException iae)
{
_LOG.warning("INVALID_SORT_STRENGTH_PROPERTY", strength);
}
}
return sortStrength;
}
/**
* Process all the children of the given table
*/
@SuppressWarnings("unchecked")
static void __processChildren(
FacesContext context,
final UIXCollection comp,
final PhaseId phaseId)
{
// process the children
int childCount = comp.getChildCount();
if (childCount != 0)
{
List children = comp.getChildren();
for (int i = 0; i < childCount; i++)
{
UIComponent child = children.get(i);
comp.processComponent(context, child, phaseId);
}
}
}
static void cacheHeaderFooterFacets(UIComponent parent, Map cache)
{
// grab the header facet and it's children
UIComponent headerFacet = parent.getFacets().get("header");
if (headerFacet != null)
{
_cacheDescendants(headerFacet, cache, true);
}
// grab the footer facet and it's children
UIComponent footerFacet = parent.getFacets().get("footer");
if (footerFacet != null)
{
_cacheDescendants(footerFacet, cache, true);
}
}
static void cacheColumnHeaderFooterFacets(UIComponent parent, Map cache)
{
List children = parent.getChildren();
for (UIComponent child : children)
{
if (child instanceof UIXColumn)
{
cacheHeaderFooterFacets(child, cache);
cacheColumnHeaderFooterFacets(child, cache);
}
}
}
private static void _cacheDescendants(UIComponent parent, Map cache, boolean inclusive)
{
if(inclusive)
cache.put(parent, Boolean.TRUE);
List children = parent.getChildren();
for (UIComponent child : children)
{
_cacheDescendants(child, cache, true);
}
}
/**
* Checks to see if the given event could possible be affected by the
* "immediate" property of the given component.
*/
private static boolean _isImmediateEvent(UIComponent comp, FacesEvent event)
{
if (event.getComponent() == comp)
{
return
(event instanceof RangeChangeEvent) ||
(event instanceof DisclosureEvent) ||
(event instanceof RowDisclosureEvent) ||
(event instanceof SelectionEvent) ||
(event instanceof SortEvent) ||
(event instanceof FocusEvent);
}
return false;
}
private TableUtils()
{
}
private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(TableUtils.class);
}