/*
* 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 humanize.faces.renderkit.html;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.ProjectStage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.component.UIParameter;
import javax.faces.component.UIViewRoot;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
/**
* @author Manfred Geiler (latest modification by $Author: bommel $)
* @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct
* 2011) $
*/
public final class Utils
{
// private static final Log log = LogFactory.getLog(RendererUtils.class);
private static final Logger log = Logger.getLogger(Utils.class.getName());
public static final String SELECT_ITEM_LIST_ATTR = Utils.class.getName() + ".LIST";
public static final String EMPTY_STRING = "";
public static final String SEQUENCE_PARAM = "jsf_sequence";
// This nice constant is "specified" 13.1.1.2 The Resource API Approach in
// Spec as an example
public static final String RES_NOT_FOUND = "RES_NOT_FOUND";
public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class<?> compClass)
{
if (facesContext == null)
throw new NullPointerException("facesContext may not be null");
if (uiComponent == null)
throw new NullPointerException("uiComponent may not be null");
// if (compClass != null &&
// !(compClass.isAssignableFrom(uiComponent.getClass())))
// why isAssignableFrom with additional getClass method call if
// isInstance does the same?
if (compClass != null && !(compClass.isInstance(uiComponent)))
{
throw new IllegalArgumentException("uiComponent : " + getPathToComponent(uiComponent) +
" is not instance of " + compClass.getName() + " as it should be");
}
}
public static boolean getBooleanAttribute(UIComponent component, String attrName, boolean defaultValue)
{
Boolean b = (Boolean) component.getAttributes().get(attrName);
return b != null ? b.booleanValue() : defaultValue;
}
public static Boolean getBooleanValue(UIComponent component)
{
Object value = getObjectValue(component);
// Try to convert to Boolean if it is a String
if (value instanceof String)
{
value = Boolean.valueOf((String) value);
}
if (value == null || value instanceof Boolean)
{
return (Boolean) value;
}
throw new IllegalArgumentException("Expected submitted value of type Boolean for Component : " +
getPathToComponent(component));
}
public static String getClientId(FacesContext facesContext, UIComponent uiComponent, String forAttr)
{
UIComponent forComponent = uiComponent.findComponent(forAttr);
if (forComponent == null)
{
final char separatorChar = UINamingContainer.getSeparatorChar(facesContext);
if (log.isLoggable(Level.INFO))
{
log
.info("Unable to find component '"
+ forAttr
+ "' (calling findComponent on component '"
+ uiComponent.getClientId(facesContext)
+ "')."
+ " We'll try to return a guessed client-id anyways -"
+ " this will be a problem if you put the referenced component"
+ " into a different naming-container. If this is the case, you can always use the full client-id.");
}
if (forAttr.length() > 0 && forAttr.charAt(0) == separatorChar)
{
// absolute id path
return forAttr.substring(1);
}
// relative id path, we assume a component on the same level as the
// label component
String labelClientId = uiComponent.getClientId(facesContext);
int colon = labelClientId.lastIndexOf(separatorChar);
return colon == -1 ? forAttr : labelClientId.substring(0, colon + 1) + forAttr;
}
return forComponent.getClientId(facesContext);
}
public static Date getDateValue(UIComponent component)
{
Object value = getObjectValue(component);
if (value == null || value instanceof Date)
{
return (Date) value;
}
throw new IllegalArgumentException("Expected submitted value of type Date for component : "
+ getPathToComponent(component));
}
public static int getIntegerAttribute(UIComponent component, String attrName, int defaultValue)
{
Integer i = (Integer) component.getAttributes().get(attrName);
return i != null ? i.intValue() : defaultValue;
}
public static Object getObjectValue(UIComponent component)
{
if (!(component instanceof ValueHolder))
{
throw new IllegalArgumentException("Component : " +
getPathToComponent(component) + "is not a ValueHolder");
}
if (component instanceof EditableValueHolder)
{
Object value = ((EditableValueHolder) component).getSubmittedValue();
if (value != null)
{
return value;
}
}
return ((ValueHolder) component).getValue();
}
public static String getPathToComponent(UIComponent component)
{
StringBuffer buf = new StringBuffer();
if (component == null)
{
buf.append("{Component-Path : ");
buf.append("[null]}");
return buf.toString();
}
getPathToComponent(component, buf);
buf.insert(0, "{Component-Path : ");
Object location = component.getAttributes().get(UIComponent.VIEW_LOCATION_KEY);
if (location != null)
{
buf.append(" Location: ").append(location);
}
buf.append("}");
return buf.toString();
}
public static String getStringValue(FacesContext facesContext,
UIComponent component)
{
if (!(component instanceof ValueHolder))
{
throw new IllegalArgumentException("Component : " + getPathToComponent(component) + "is not a ValueHolder");
}
if (component instanceof EditableValueHolder)
{
Object submittedValue = ((EditableValueHolder) component).getSubmittedValue();
if (submittedValue != null)
{
if (log.isLoggable(Level.FINE))
log.fine("returning 1 '" + submittedValue + "'");
return submittedValue.toString();
}
}
Object value;
if (component instanceof EditableValueHolder)
{
EditableValueHolder holder = (EditableValueHolder) component;
if (holder.isLocalValueSet())
{
value = holder.getLocalValue();
} else
{
value = getValue(component);
}
}
else
{
value = getValue(component);
}
Converter converter = ((ValueHolder) component).getConverter();
if (converter == null && value != null)
{
try
{
converter = facesContext.getApplication().createConverter(value.getClass());
if (log.isLoggable(Level.FINE))
log.fine("the created converter is " + converter);
} catch (FacesException e)
{
log.log(Level.SEVERE, "No converter for class " + value.getClass().getName() + " found (component id="
+ component.getId() + ").", e);
// converter stays null
}
}
if (converter == null)
{
if (value == null)
{
if (log.isLoggable(Level.FINE))
log.fine("returning an empty string");
return "";
}
if (log.isLoggable(Level.FINE))
log.fine("returning an .toString");
return value.toString();
}
if (log.isLoggable(Level.FINE))
log.fine("returning converter get as string " + converter);
return converter.getAsString(facesContext, component, value);
}
public static String getStringValue(FacesContext context, ValueExpression ve)
{
Object value = ve.getValue(context.getELContext());
if (value != null)
{
return value.toString();
}
return null;
}
/**
* Calls getValidUIParameterChildren(facesContext, children, skipNullValue,
* skipUnrendered, true);
*
* @param facesContext
* @param children
* @param skipNullValue
* @param skipUnrendered
* @return
*/
public static List<UIParameter> getValidUIParameterChildren(
FacesContext facesContext, List<UIComponent> children,
boolean skipNullValue, boolean skipUnrendered)
{
return getValidUIParameterChildren(facesContext, children,
skipNullValue, skipUnrendered, true);
}
/**
* Returns a List of all valid UIParameter children from the given children.
* Valid means that the UIParameter is not disabled, its name is not null
* (if skipNullName is true), its value is not null (if skipNullValue is
* true) and it is rendered (if skipUnrendered is true). This method also
* creates a warning for every UIParameter with a null-name (again, if
* skipNullName is true) and, if ProjectStage is Development and
* skipNullValue is true, it informs the user about every null-value.
*
* @param facesContext
* @param children
* @param skipNullValue
* should UIParameters with a null value be skipped
* @param skipUnrendered
* should UIParameters with isRendered() returning false be
* skipped
* @param skipNullName
* should UIParameters with a null name be skipped (normally
* true, but in the case of h:outputFormat false)
* @return
*/
public static List<UIParameter> getValidUIParameterChildren(
FacesContext facesContext, List<UIComponent> children,
boolean skipNullValue, boolean skipUnrendered, boolean skipNullName)
{
List<UIParameter> params = null;
for (int i = 0, size = children.size(); i < size; i++)
{
UIComponent child = children.get(i);
if (child instanceof UIParameter)
{
UIParameter param = (UIParameter) child;
// check for the disable attribute (since 2.0)
// and the render attribute (only if skipUnrendered is true)
if (param.isDisable()
|| (skipUnrendered && !param.isRendered()))
{
// ignore this UIParameter and continue
continue;
}
// check the name
String name = param.getName();
if (skipNullName && (name == null || EMPTY_STRING.equals(name)))
{
// warn for a null-name
log.log(Level.WARNING,
"The UIParameter "
+ Utils.getPathToComponent(param)
+ " has a name of null or empty string and thus will not be added to the URL.");
// and skip it
continue;
}
// check the value
if (skipNullValue && param.getValue() == null)
{
if (facesContext.isProjectStage(ProjectStage.Development))
{
// inform the user about the null value when in
// Development stage
log.log(Level.INFO,
"The UIParameter "
+ Utils
.getPathToComponent(param)
+ " has a value of null and thus will not be added to the URL.");
}
// skip a null-value
continue;
}
// add the param
if (params == null)
{
params = new ArrayList<UIParameter>();
}
params.add(param);
}
}
if (params == null)
{
params = Collections.emptyList();
}
return params;
}
/**
* See JSF Spec. 8.5 Table 8-1
*
* @param value
* @return boolean
*/
public static boolean isDefaultAttributeValue(Object value)
{
if (value == null)
{
return true;
}
else if (value instanceof Boolean)
{
return !((Boolean) value).booleanValue();
}
else if (value instanceof Number)
{
if (value instanceof Integer)
{
return ((Number) value).intValue() == Integer.MIN_VALUE;
}
else if (value instanceof Double)
{
return Double.compare(((Number) value).doubleValue(), Double.MIN_VALUE) == 0;
}
else if (value instanceof Long)
{
return ((Number) value).longValue() == Long.MIN_VALUE;
}
else if (value instanceof Byte)
{
return ((Number) value).byteValue() == Byte.MIN_VALUE;
}
else if (value instanceof Float)
{
return Float.compare(((Number) value).floatValue(), Float.MIN_VALUE) == 0;
}
else if (value instanceof Short)
{
return ((Number) value).shortValue() == Short.MIN_VALUE;
}
}
return false;
}
/**
* @return true, if the attribute was written
* @throws java.io.IOException
*/
public static boolean renderHTMLAttribute(ResponseWriter writer,
String componentProperty, String attrName, Object value)
throws IOException
{
if (!isDefaultAttributeValue(value))
{
// render JSF "styleClass" and "itemStyleClass" attributes as
// "class"
String htmlAttrName = attrName.equals(HTML.STYLE_CLASS_ATTR) ? HTML.CLASS_ATTR
: attrName;
writer.writeAttribute(htmlAttrName, value, componentProperty);
return true;
}
return false;
}
/**
* @return true, if the attribute was written
* @throws java.io.IOException
*/
public static boolean renderHTMLAttribute(ResponseWriter writer,
UIComponent component, String componentProperty, String htmlAttrName)
throws IOException
{
Object value = component.getAttributes().get(componentProperty);
return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
value);
}
/**
* @return true, if an attribute was written
* @throws java.io.IOException
*/
public static boolean renderHTMLAttributes(ResponseWriter writer,
UIComponent component, String[] attributes) throws IOException
{
boolean somethingDone = false;
for (int i = 0, len = attributes.length; i < len; i++)
{
String attrName = attributes[i];
if (renderHTMLAttribute(writer, component, attrName, attrName))
{
somethingDone = true;
}
}
return somethingDone;
}
public static boolean renderHTMLAttributesWithOptionalStartElement(
ResponseWriter writer, UIComponent component, String elementName,
String[] attributes) throws IOException
{
boolean startElementWritten = false;
for (int i = 0, len = attributes.length; i < len; i++)
{
String attrName = attributes[i];
Object value = component.getAttributes().get(attrName);
if (!isDefaultAttributeValue(value))
{
if (!startElementWritten)
{
writer.startElement(elementName, component);
startElementWritten = true;
}
renderHTMLAttribute(writer, attrName, attrName, value);
}
}
return startElementWritten;
}
public static void writeIdIfNecessary(ResponseWriter writer, UIComponent component, FacesContext facesContext)
throws IOException
{
if (component.getId() != null
&& !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
{
writer.writeAttribute(HTML.ID_ATTR,
component.getClientId(facesContext), null);
}
}
private static void getPathToComponent(UIComponent component, StringBuffer buf)
{
if (component == null)
return;
StringBuffer intBuf = new StringBuffer();
intBuf.append("[Class: ");
intBuf.append(component.getClass().getName());
if (component instanceof UIViewRoot)
{
intBuf.append(",ViewId: ");
intBuf.append(((UIViewRoot) component).getViewId());
}
else
{
intBuf.append(",Id: ");
intBuf.append(component.getId());
}
intBuf.append("]");
buf.insert(0, intBuf.toString());
getPathToComponent(component.getParent(), buf);
}
private static Object getValue(UIComponent component)
{
Object value;
try
{
value = ((ValueHolder) component).getValue();
} catch (Exception ex)
{
throw new FacesException("Could not retrieve value of component with path : " +
getPathToComponent(component), ex);
}
return value;
}
private Utils()
{
// nope
}
}