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

org.apache.myfaces.custom.crosstable.UIColumns Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is compatible with both JSF1.1 and JSF1.2; however for JSF1.2 users there is an alternative build of Tomahawk available that takes advantage of JSF1.2 features to offer some additional benefits.

There is a newer version: 1.1.14
Show 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.custom.crosstable;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.ResultDataModel;
import javax.faces.model.ResultSetDataModel;
import javax.faces.model.ScalarDataModel;
import javax.servlet.jsp.jstl.sql.Result;

/**
 * An object which can be a child of a t:dataTable, and inserts a dynamic
 * set of columns into the parent table. The set of columns inserted are
 * defined by a collection returned from a value-binding.
 * 

* Class org.apache.myfaces.component.html.ext.HtmlDataTable (aka t:dataTable) * has special-case code to detect a child of this type, and invoke the * necessary methods on this class. This class does not work with other * UIData implementations. There is no renderer for this component; the * HtmlTableRenderer associated with ext.HtmlDataTable is UIColumns-aware, * and implements the necessary logic itself. *

* This class is actually a UIData itself, which is effectively "merged" * with the parent HtmlDataTable. It can't be used as a stand-alone table, * however, because it uses the DataModel returned by the "value" value-binding * to define the columns, and depends on the parent table's "value" * value-binding to define the row DataModel. *

* The "value" attribute of this class must be a value-binding which * returns a DataModel of objects (or a List, Array or ResultSet which * automatically gets wrapped in the appropriate DataModel). *

* In a normal table, each UIColumn child has facets and child components * which define that column. In this component, the same child components * apply to each column (ie are repeated dataModel.size times). However * as the columns are rendered, the current DataModel object (ie the object * defining the current column) is stored into a variable whose name * is defined by attribute "var". This allows the child components of * this component to refer to attributes on those objects to set things * like the current column's name. When the objects must be rendered as * different components (eg h:outputText or h:outputDate), multiple * child components can be used with rendered attributes selecting the * appropriate one depending upon the current column object's data. * * @author Mathias Broekelmann (latest modification by $Author: grantsmith $) * @version $Revision: 472630 $ $Date: 2006-11-08 21:40:03 +0100 (Mi, 08 Nov 2006) $ */ public class UIColumns extends UIData { public static final String COMPONENT_TYPE = "org.apache.myfaces.Columns"; private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass(); private static final int PROCESS_DECODES = 1; private static final int PROCESS_VALIDATORS = 2; private static final int PROCESS_UPDATES = 3; private boolean _isValidChilds = true; // holds the the state for each cell of the child components of this UIColumns private Map _cellStates = new HashMap(); private Object _initialDescendantComponentState = null; private int _colIndex = -1; private UIData _parentUIData; private Map _dataModelMap = new HashMap(); public UIColumns() { super(); } /** * Return true if there are any more columns to process. */ public boolean isRowAvailable() { return getDataModel().isRowAvailable(); } /** * Get the number of dynamic columns represented by this instance. */ public int getRowCount() { return getDataModel().getRowCount(); } /** Get the object representing the current column. */ public Object getRowData() { return getDataModel().getRowData(); } /** Get the currently selected column index. */ public int getRowIndex() { return _colIndex; } /** Set the currently selected column. */ public void setRowIndex(int colIndex) { if (colIndex < -1) { throw new IllegalArgumentException("colIndex is less than -1"); } if (_colIndex == colIndex) { return; } FacesContext facesContext = getFacesContext(); if (_colIndex == -1) { if (_initialDescendantComponentState == null) { _initialDescendantComponentState = saveDescendantComponentStates(getFacetsAndChildren()); } } else { _cellStates.put(getClientId(facesContext), saveDescendantComponentStates(getFacetsAndChildren())); } _colIndex = colIndex; DataModel dataModel = getDataModel(); dataModel.setRowIndex(colIndex); String var = getVar(); if (colIndex == -1) { if (var != null) { facesContext.getExternalContext().getRequestMap().remove(var); } } else { if (var != null) { if (isRowAvailable()) { Object rowData = dataModel.getRowData(); facesContext.getExternalContext().getRequestMap().put(var, rowData); } else { facesContext.getExternalContext().getRequestMap().remove(var); } } } if (_colIndex == -1) { restoreDescendantComponentStates(getFacetsAndChildren(), _initialDescendantComponentState); } else { Object rowState = _cellStates.get(getClientId(facesContext)); if (rowState == null) { restoreDescendantComponentStates(getFacetsAndChildren(), _initialDescendantComponentState); } else { restoreDescendantComponentStates(getFacetsAndChildren(), rowState); } } } protected void restoreDescendantComponentStates(Iterator childIterator, Object state) { Iterator descendantStateIterator = null; while (childIterator.hasNext()) { if (descendantStateIterator == null && state != null) { descendantStateIterator = ((Collection) state).iterator(); } UIComponent component = (UIComponent) childIterator.next(); // reset the client id (see spec 3.1.6) component.setId(component.getId()); if (!component.isTransient()) { Object childState = null; Object descendantState = null; if (descendantStateIterator != null && descendantStateIterator.hasNext()) { Object[] object = (Object[]) descendantStateIterator.next(); childState = object[0]; descendantState = object[1]; } if (component instanceof EditableValueHolder) { ((EditableValueHolderState) childState).restoreState((EditableValueHolder) component); } restoreDescendantComponentStates(component.getFacetsAndChildren(), descendantState); } } } protected Object saveDescendantComponentStates(Iterator childIterator) { Collection childStates = null; while (childIterator.hasNext()) { if (childStates == null) { childStates = new ArrayList(); } UIComponent child = (UIComponent) childIterator.next(); if (!child.isTransient()) { Object descendantState = saveDescendantComponentStates(child.getFacetsAndChildren()); Object state = null; if (child instanceof EditableValueHolder) { state = new EditableValueHolderState((EditableValueHolder) child); } childStates.add(new Object[] { state, descendantState }); } } return childStates; } public void setValue(Object value) { super.setValue(value); _dataModelMap.clear(); _cellStates.clear(); _isValidChilds = true; } public void setValueBinding(String name, ValueBinding binding) { if (name == null) { throw new NullPointerException("name"); } else if (name.equals("value")) { _dataModelMap.clear(); } else if (name.equals("var") || name.equals("rowIndex")) { throw new IllegalArgumentException("name " + name); } super.setValueBinding(name, binding); } /** * Get a DataModel whose wrapped data contains a collection of * objects representing columns. */ protected DataModel getDataModel() { String clientID = ""; UIComponent parent = getParentUIData().getParent(); if (parent != null) { clientID = parent.getClientId(getFacesContext()); } DataModel dataModel = (DataModel) _dataModelMap.get(clientID); if (dataModel == null) { dataModel = createDataModel(); _dataModelMap.put(clientID, dataModel); } return dataModel; } protected void setDataModel(DataModel dataModel) { UIComponent parent = getParentUIData().getParent(); String clientID = ""; if (parent != null) { clientID = parent.getClientId(getFacesContext()); } _dataModelMap.put(clientID, dataModel); } /** * Creates a new DataModel around the current value. */ protected DataModel createDataModel() { Object value = getValue(); if (value == null) { return EMPTY_DATA_MODEL; } else if (value instanceof DataModel) { return (DataModel) value; } else if (value instanceof List) { return new ListDataModel((List) value); } else if (value instanceof Collection) { return new ListDataModel(new ArrayList((Collection) value)); } else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass())) { return new ArrayDataModel((Object[]) value); } else if (value instanceof ResultSet) { return new ResultSetDataModel((ResultSet) value); } else if (value instanceof Result) { return new ResultDataModel((Result) value); } else { return new ScalarDataModel(value); } } private static final DataModel EMPTY_DATA_MODEL = new DataModel() { public boolean isRowAvailable() { return false; } public int getRowCount() { return 0; } public Object getRowData() { throw new IllegalArgumentException(); } public int getRowIndex() { return -1; } public void setRowIndex(int i) { if (i < -1) throw new IndexOutOfBoundsException("Index < 0 : " + i); } public Object getWrappedData() { return null; } public void setWrappedData(Object obj) { if (obj == null) return; //Clearing is allowed throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException"); } }; /** * Update the input component (if any) in each dynamic column. * * @see javax.faces.component.UIData#processDecodes(javax.faces.context.FacesContext) */ public void processDecodes(FacesContext context) { if (context == null) throw new NullPointerException("context"); if (!isRendered()) return; setRowIndex(-1); processColumnsFacets(context, PROCESS_DECODES); processRows(context, PROCESS_DECODES); setRowIndex(-1); try { decode(context); } catch (RuntimeException e) { context.renderResponse(); throw e; } } private void processColumnsFacets(FacesContext context, int processAction) { int first = getFirst(); int cols = getRows(); int last; if (cols == 0) { last = getRowCount(); } else { last = first + cols; } for (int colIndex = first; colIndex < last; colIndex++) { setRowIndex(colIndex); if (isRowAvailable()) { for (Iterator facetsIter = getFacets().values().iterator(); facetsIter.hasNext();) { UIComponent facet = (UIComponent) facetsIter.next(); process(context, facet, processAction); } } } setRowIndex(-1); } private void processRows(FacesContext context, int processAction) { UIData parentUIData = getParentUIData(); int first = parentUIData.getFirst(); int rows = parentUIData.getRows(); int last; if (rows == 0) { last = parentUIData.getRowCount(); } else { last = first + rows; } for (int rowIndex = first; rowIndex < last; rowIndex++) { parentUIData.setRowIndex(rowIndex); if (parentUIData.isRowAvailable()) { processColumns(context, processAction); } } } /** * Return the UIData this component is nested within. *

* Actually, this component will only function correctly when nested within * an org.apache.myfaces.component.html.ext.HtmlDataTable. */ private UIData getParentUIData() { if (_parentUIData == null) { UIComponent parent = getParent(); if (!(parent instanceof UIData)) { throw new IllegalStateException("UIColumns component must be a child of a UIData component"); } _parentUIData = (UIData) parent; } return _parentUIData; } private void processColumns(FacesContext context, int processAction) { int first = getFirst(); int cols = getRows(); int last; if (cols == 0) { last = getRowCount(); } else { last = first + cols; } for (int colIndex = first; colIndex < last; colIndex++) { setRowIndex(colIndex); if (isRowAvailable()) { for (Iterator columnChildIter = getChildren().iterator(); columnChildIter.hasNext();) { UIComponent columnChild = (UIComponent) columnChildIter.next(); process(context, columnChild, processAction); } } } setRowIndex(-1); } /** * @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext) */ public void processValidators(FacesContext context) { if (context == null) throw new NullPointerException("context"); if (!isRendered()) return; setRowIndex(-1); processColumnsFacets(context, PROCESS_VALIDATORS); processRows(context, PROCESS_VALIDATORS); setRowIndex(-1); // check if an validation error forces the render response for our data if (context.getRenderResponse()) { _isValidChilds = false; } } /** * @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext) */ public void processUpdates(FacesContext context) { if (context == null) throw new NullPointerException("context"); if (!isRendered()) return; setRowIndex(-1); processColumnsFacets(context, PROCESS_UPDATES); processRows(context, PROCESS_UPDATES); setRowIndex(-1); // check if an validation error forces the render response for our data if (context.getRenderResponse()) { _isValidChilds = false; } } private void process(FacesContext context, UIComponent component, int processAction) { switch (processAction) { case PROCESS_DECODES: component.processDecodes(context); break; case PROCESS_VALIDATORS: component.processValidators(context); break; case PROCESS_UPDATES: component.processUpdates(context); break; } } /** * Called from HtmlDataTable.encodeBegin, ie called once when rendering * for the entire table starts. */ public void encodeTableBegin(FacesContext context) { setRowIndex(-1); _initialDescendantComponentState = null; if (_isValidChilds && !hasErrorMessages(context)) { //Refresh DataModel for rendering: _dataModelMap.clear(); _cellStates.clear(); } } protected boolean hasErrorMessages(FacesContext context) { for (Iterator iter = context.getMessages(); iter.hasNext();) { FacesMessage message = (FacesMessage) iter.next(); if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) { return true; } } return false; } /** * Called from HtmlDataTable.encodeEnd, ie called once after rendering * for the entire table has completed. */ public void encodeTableEnd(FacesContext context) { setRowIndex(-1); } private class EditableValueHolderState { private final Object _value; private final boolean _localValueSet; private final boolean _valid; private final Object _submittedValue; public EditableValueHolderState(EditableValueHolder evh) { _value = evh.getLocalValue(); _localValueSet = evh.isLocalValueSet(); _valid = evh.isValid(); _submittedValue = evh.getSubmittedValue(); } public void restoreState(EditableValueHolder evh) { evh.setValue(_value); evh.setLocalValueSet(_localValueSet); evh.setValid(_valid); evh.setSubmittedValue(_submittedValue); } } /// ------ EUR/SI/BS/JC MODIFICATIONS START HERE --------- public String getClientId(FacesContext context) { String clientId2 = super.getClientId(context); int rowIndex = getRowIndex(); if (rowIndex == -1) { return clientId2; } else { return clientId2 + "_" + rowIndex; } } /// ------ EUR/SI/BS/JC MODIFICATIONS END HERE --------- }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy