org.apache.myfaces.shared_tomahawk.renderkit._SharedRendererUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tomahawk21 Show documentation
Show all versions of tomahawk21 Show documentation
JSF components and utilities that can be used with any JSF implementation.
This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build
changes to take advantage of JSF2.1 features. A JSF2.1 implementation is required to use this
version of the Tomahawk library.
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.shared_tomahawk.renderkit;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UIOutput;
import javax.faces.component.UISelectMany;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import org.apache.myfaces.shared_tomahawk.util.SelectItemsIterator;
/**
* The util methods in this class are shared between the javax.faces.component package and
* the org.apache.myfaces.renderkit package.
* Please note: Any changes here must also apply to the class in the other package!
*
* @author Manfred Geiler (latest modification by $Author: lu4242 $)
* @version $Revision: 1349673 $ $Date: 2012-06-13 03:14:15 -0500 (Wed, 13 Jun 2012) $
*/
class _SharedRendererUtils
{
static final String COLLECTION_TYPE_KEY = "collectionType";
static final String VALUE_TYPE_KEY = "valueType";
static Converter findUIOutputConverter(FacesContext facesContext,
UIOutput component)
{
// Attention!
// This code is duplicated in jsfapi component package.
// If you change something here please do the same in the other class!
Converter converter = component.getConverter();
if (converter != null)
{
return converter;
}
//Try to find out by value expression
ValueExpression expression = component.getValueExpression("value");
if (expression == null)
{
return null;
}
Class valueType = expression.getType(facesContext.getELContext());
if (valueType == null)
{
return null;
}
if (Object.class.equals(valueType))
{
return null; //There is no converter for Object class
}
try
{
return facesContext.getApplication().createConverter(valueType);
}
catch (FacesException e)
{
log(facesContext, "No Converter for type " + valueType.getName()
+ " found", e);
return null;
}
}
static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,
String[] submittedValue) throws ConverterException
{
return getConvertedUISelectManyValue(facesContext, component,
submittedValue, false);
}
/**
* Gets the converted value of a UISelectMany component.
* If the considerValueType is true, this method will also consider the
* valueType attribute of Tomahawk UISelectMany components.
*
* @param facesContext
* @param component
* @param submittedValue
* @param considerValueType
* @return
* @throws ConverterException
*/
static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany component,
String[] submittedValue, boolean considerValueType) throws ConverterException
{
// Attention!
// This code is duplicated in jsfapi component package (except for considerValueType).
// If you change something here please do the same in the other class!
if (submittedValue == null)
{
throw new NullPointerException("submittedValue");
}
ValueExpression expression = component.getValueExpression("value");
Object targetForConvertedValues = null;
// if the component has an attached converter, use it
Converter converter = component.getConverter();
if (converter == null && considerValueType)
{
// try to get a converter from the valueType attribute
converter = getValueTypeConverter(facesContext, component);
}
if (expression != null)
{
Class modelType = expression
.getType(facesContext.getELContext());
if (modelType == null)
{
// FIXME temporal workaround for MYFACES-2552
return submittedValue;
}
else if (modelType.isArray())
{
// the target should be an array
Class componentType = modelType.getComponentType();
// check for optimization if the target is
// a string array --> no conversion needed
if (String.class.equals(componentType))
{
return submittedValue;
}
if (converter == null)
{
// the compononent does not have an attached converter
// --> try to get a registered-by-class converter
converter = facesContext.getApplication().createConverter(
componentType);
if (converter == null)
{
// could not obtain a Converter
// --> check if we maybe do not really have to convert
if (!Object.class.equals(componentType))
{
// target is not an Object array
// and not a String array (checked some lines above)
// and we do not have a Converter
throw new ConverterException(
"Could not obtain a Converter for "
+ componentType.getName());
}
}
}
// instantiate the array
targetForConvertedValues = Array.newInstance(componentType,
submittedValue.length);
}
else if (Collection.class.isAssignableFrom(modelType) || Object.class.equals(modelType))
{
if (converter == null)
{
// try to get the by-type-converter from the type of the SelectItems
SelectItemsIterator iterator = new SelectItemsIterator(component, facesContext);
converter = getSelectItemsValueConverter(iterator, facesContext);
}
Object collectionTypeAttr = component.getAttributes().get(
COLLECTION_TYPE_KEY);
if (collectionTypeAttr != null)
{
Class collectionType = getClassFromAttribute(facesContext, collectionTypeAttr);
if (collectionType == null)
{
throw new FacesException(
"The attribute "
+ COLLECTION_TYPE_KEY
+ " of component "
+ component.getClientId(facesContext)
+ " does not evaluate to a "
+ "String, a Class object or a ValueExpression pointing "
+ "to a String or a Class object.");
}
// now we have a collectionType --> but is it really some kind of Collection
if (!Collection.class.isAssignableFrom(collectionType))
{
throw new FacesException("The attribute "
+ COLLECTION_TYPE_KEY + " of component "
+ component.getClientId(facesContext)
+ " does not point to a valid type of Collection.");
}
// now we have a real collectionType --> try to instantiate it
try
{
targetForConvertedValues = collectionType.newInstance();
}
catch (Exception e)
{
throw new FacesException("The Collection "
+ collectionType.getName()
+ "can not be instantiated.", e);
}
}
else if (Collection.class.isAssignableFrom(modelType))
{
// component.getValue() will implement Collection at this point
Collection componentValue = (Collection) component
.getValue();
// can we clone the Collection
if (componentValue instanceof Cloneable)
{
// clone method of Object is protected --> use reflection
try
{
Method cloneMethod = componentValue.getClass()
.getMethod("clone");
Collection clone = (Collection) cloneMethod
.invoke(componentValue);
clone.clear();
targetForConvertedValues = clone;
}
catch (Exception e)
{
log(facesContext, "Could not clone "
+ componentValue.getClass().getName(), e);
}
}
// if clone did not work
if (targetForConvertedValues == null)
{
// try to create the (concrete) collection from modelType
// or with the class object of componentValue (if any)
try
{
targetForConvertedValues = (componentValue != null
? componentValue.getClass()
: modelType).newInstance();
}
catch (Exception e)
{
// this did not work either
// use the standard concrete type
if (SortedSet.class.isAssignableFrom(modelType))
{
targetForConvertedValues = new TreeSet();
}
else if (Queue.class.isAssignableFrom(modelType))
{
targetForConvertedValues = new LinkedList();
}
else if (Set.class.isAssignableFrom(modelType))
{
targetForConvertedValues = new HashSet(
submittedValue.length);
}
else
{
targetForConvertedValues = new ArrayList(
submittedValue.length);
}
}
}
}
else /* if (Object.class.equals(modelType)) */
{
// a modelType of Object is also permitted, in order to support
// managed bean properties of type Object
// optimization: if we don't have a converter, we can return the submittedValue
if (converter == null)
{
return submittedValue;
}
targetForConvertedValues = new Object[submittedValue.length];
}
}
else
{
// the expression does neither point to an array nor to a collection
throw new ConverterException(
"ValueExpression for UISelectMany must be of type Collection or Array.");
}
}
else
{
targetForConvertedValues = new Object[submittedValue.length];
}
// convert the values with the selected converter (if any)
// and store them in targetForConvertedValues
boolean isArray = (targetForConvertedValues.getClass().isArray());
for (int i = 0; i < submittedValue.length; i++)
{
// get the value
Object value;
if (converter != null)
{
value = converter.getAsObject(facesContext, component,
submittedValue[i]);
}
else
{
value = submittedValue[i];
}
// store it in targetForConvertedValues
if (isArray)
{
Array.set(targetForConvertedValues, i, value);
}
else
{
((Collection) targetForConvertedValues).add(value);
}
}
return targetForConvertedValues;
}
/**
* Gets a Class object from a given component attribute. The attribute can
* be a ValueExpression (that evaluates to a String or a Class) or a
* String (that is a fully qualified Java class name) or a Class object.
*
* @param facesContext
* @param attribute
* @return
* @throws FacesException if the value is a String and the represented
* class cannot be found
*/
static Class getClassFromAttribute(FacesContext facesContext,
Object attribute) throws FacesException
{
// Attention!
// This code is duplicated in jsfapi component package.
// If you change something here please do the same in the other class!
Class type = null;
// if there is a value, it must be a ...
// ... a ValueExpression that evaluates to a String or a Class
if (attribute instanceof ValueExpression)
{
// get the value of the ValueExpression
attribute = ((ValueExpression) attribute)
.getValue(facesContext.getELContext());
}
// ... String that is a fully qualified Java class name
if (attribute instanceof String)
{
try
{
type = Class.forName((String) attribute);
}
catch (ClassNotFoundException cnfe)
{
throw new FacesException(
"Unable to find class "
+ attribute
+ " on the classpath.", cnfe);
}
}
// ... a Class object
else if (attribute instanceof Class)
{
type = (Class) attribute;
}
return type;
}
/**
* Uses the valueType attribute of the given UISelectMany component to
* get a by-type converter.
*
* @param facesContext
* @param component
* @return
*/
static Converter getValueTypeConverter(FacesContext facesContext, UISelectMany component)
{
Converter converter = null;
Object valueTypeAttr = component.getAttributes().get(VALUE_TYPE_KEY);
if (valueTypeAttr != null)
{
// treat the valueType attribute exactly like the collectionType attribute
Class valueType = getClassFromAttribute(facesContext, valueTypeAttr);
if (valueType == null)
{
throw new FacesException(
"The attribute "
+ VALUE_TYPE_KEY
+ " of component "
+ component.getClientId(facesContext)
+ " does not evaluate to a "
+ "String, a Class object or a ValueExpression pointing "
+ "to a String or a Class object.");
}
// now we have a valid valueType
// --> try to get a registered-by-class converter
converter = facesContext.getApplication().createConverter(valueType);
if (converter == null)
{
log.log(Level.WARNING, "Found attribute valueType on component " +
RendererUtils.getPathToComponent(component) +
", but could not get a by-type converter for type " +
valueType.getName());
}
}
return converter;
}
/**
* Iterates through the SelectItems with the given Iterator and tries to obtain
* a by-class-converter based on the Class of SelectItem.getValue().
* @param iterator
* @param facesContext
* @return The first suitable Converter for the given SelectItems or null.
*/
static Converter getSelectItemsValueConverter(Iterator iterator, FacesContext facesContext)
{
// Attention!
// This code is duplicated in jsfapi component package.
// If you change something here please do the same in the other class!
Converter converter = null;
while (converter == null && iterator.hasNext())
{
SelectItem item = iterator.next();
if (item instanceof SelectItemGroup)
{
Iterator groupIterator = Arrays.asList(
((SelectItemGroup) item).getSelectItems()).iterator();
converter = getSelectItemsValueConverter(groupIterator, facesContext);
}
else
{
Class selectItemsType = item.getValue().getClass();
// optimization: no conversion for String values
if (String.class.equals(selectItemsType))
{
return null;
}
try
{
converter = facesContext.getApplication().createConverter(selectItemsType);
}
catch (FacesException e)
{
// nothing - try again
}
}
}
return converter;
}
//private static final Log log = LogFactory.getLog(_SharedRendererUtils.class);
private static final Logger log = Logger.getLogger(_SharedRendererUtils.class.getName());
/**
* This method is different in the two versions of _SharedRendererUtils.
*/
private static void log(FacesContext context, String msg, Exception e)
{
log.log(Level.SEVERE, msg, e);
}
}