/*
* 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.commons.converter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.PartialStateHolder;
import javax.faces.component.StateHelper;
import javax.faces.component.StateHolder;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
import org.apache.myfaces.commons.util.MessageUtils;
/**
* Base converter implementation for Apache MyFaces Commons Converters.
*
*/
@JSFConverter(
configExcluded = true,
evaluateELOnExecution = true,
tagClass = "org.apache.myfaces.commons.converter.ConverterBaseTag",
tagHandler = "org.apache.myfaces.commons.converter.ConverterBaseTagHandler")
public abstract class ConverterBase implements PartialStateHolder, Converter
{
private boolean _transient = false;
private transient FacesContext _facesContext;
private StateHelper _stateHelper = null;
private boolean _initialStateMarked = false;
/**
* alternate conversion error summary message format string
*
* @return The summary message to be displayed
*/
@JSFProperty
public String getSummaryMessage()
{
return (String) getStateHelper().eval(PropertyKeys.summaryMessage);
}
/**
*
* @param message The summary message to be displayed.
*/
public void setSummaryMessage(String message)
{
getStateHelper().put(PropertyKeys.summaryMessage, message);
}
/**
* alternate conversion error detail message format string
* (use 'message' and 'detailMessage' alternatively)
*
* @return The detail message.
*/
@JSFProperty
public String getDetailMessage()
{
return (String) getStateHelper().eval(PropertyKeys.detailMessage);
}
/**
*
* @param message The detail message to be displayed.
*/
public void setDetailMessage(String message)
{
getStateHelper().put(PropertyKeys.detailMessage, message);
}
/**
* @param context
*/
public Object saveState(FacesContext context)
{
if (context == null)
{
throw new NullPointerException ("context");
}
StateHelper stateHelper = getStateHelper(false);
if (stateHelper != null)
{
return stateHelper.saveState(context);
}
else
{
return null;
}
}
public void restoreState(FacesContext context, Object state)
{
getStateHelper().restoreState(context, state);
}
public boolean isTransient()
{
return _transient;
}
public void setTransient(boolean newTransientValue)
{
_transient = newTransientValue;
}
// Utility methods
/**
* @param defaultMessage The default message we would expect.
* @param args Arguments for parsing this message.
* @return FacesMessage
*/
protected FacesMessage getFacesMessage(String defaultMessage, Object[] args)
{
FacesMessage msg;
if (getSummaryMessage() == null && getDetailMessage() == null)
{
msg = MessageUtils.getMessage(FacesMessage.SEVERITY_ERROR, defaultMessage, args);
}
else
{
Locale locale = MessageUtils.getCurrentLocale();
String summaryText = MessageUtils.substituteParams(locale, getSummaryMessage(), args);
String detailText = MessageUtils.substituteParams(locale, getDetailMessage(), args);
msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, summaryText, detailText);
}
return msg;
}
// --------------------- borrowed from UIComponentBase ------------
@SuppressWarnings("unchecked")
public ValueExpression getValueExpression(String name)
{
if (name == null)
{
throw new NullPointerException("name");
}
StateHelper helper = getStateHelper(false);
if (helper == null)
{
return null;
}
Map<String,Object> bindings = (Map<String,Object>) helper.get(PropertyKeys.bindings);
if (bindings == null)
{
return null;
}
else
{
return (ValueExpression) bindings.get(name);
}
}
public void setValueExpression(String name, ValueExpression expression)
{
if (name == null)
{
throw new NullPointerException("name");
}
if (expression == null)
{
getStateHelper().remove(PropertyKeys.bindings, name);
}
else
{
getStateHelper().put(PropertyKeys.bindings, name, expression);
}
}
/**
* Serializes objects which are "attached" to this component but which are
* not UIComponent children of it. Examples are converter and listener
* objects. To be precise, it returns an object which implements
* java.io.Serializable, and which when serialized will persist the
* state of the provided object.
* <p>
* If the attachedObject is a List then every object in the list is saved
* via a call to this method, and the returned wrapper object contains
* a List object.
* <p>
* If the object implements StateHolder then the object's saveState is
* called immediately, and a wrapper is returned which contains both
* this saved state and the original class name. However in the case
* where the StateHolder.isTransient method returns true, null is
* returned instead.
* <p>
* If the object implements java.io.Serializable then the object is simply
* returned immediately; standard java serialization will later be used
* to store this object.
* <p>
* In all other cases, a wrapper is returned which simply stores the type
* of the provided object. When deserialized, a default instance of that
* type will be recreated.
*/
public static Object saveAttachedState(FacesContext context, Object attachedObject)
{
if (context == null)
{
throw new NullPointerException ("context");
}
if (attachedObject == null)
{
return null;
}
// StateHolder interface should take precedence over
// List children
if (attachedObject instanceof StateHolder)
{
StateHolder holder = (StateHolder) attachedObject;
if (holder.isTransient())
{
return null;
}
return new _AttachedStateWrapper(attachedObject.getClass(), holder.saveState(context));
}
else if (attachedObject instanceof List)
{
List<Object> lst = new ArrayList<Object>(((List<?>) attachedObject).size());
for (Object item : (List<?>) attachedObject)
{
if (item != null)
{
lst.add(saveAttachedState(context, item));
}
}
return new _AttachedListStateWrapper(lst);
}
else if (attachedObject instanceof Serializable)
{
return attachedObject;
}
else
{
return new _AttachedStateWrapper(attachedObject.getClass(), null);
}
}
@SuppressWarnings("unchecked")
public static Object restoreAttachedState(FacesContext context, Object stateObj) throws IllegalStateException
{
if (context == null)
{
throw new NullPointerException("context");
}
if (stateObj == null)
{
return null;
}
if (stateObj instanceof _AttachedListStateWrapper)
{
List<Object> lst = ((_AttachedListStateWrapper) stateObj).getWrappedStateList();
List<Object> restoredList = new ArrayList<Object>(lst.size());
for (Object item : lst)
{
restoredList.add(restoreAttachedState(context, item));
}
return restoredList;
}
else if (stateObj instanceof _AttachedStateWrapper)
{
Class<?> clazz = ((_AttachedStateWrapper) stateObj).getClazz();
Object restoredObject;
try
{
restoredObject = clazz.newInstance();
}
catch (InstantiationException e)
{
throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
+ " (missing no-args constructor?)", e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
if (restoredObject instanceof StateHolder)
{
_AttachedStateWrapper wrapper = (_AttachedStateWrapper) stateObj;
Object wrappedState = wrapper.getWrappedStateObject();
StateHolder holder = (StateHolder) restoredObject;
holder.restoreState(context, wrappedState);
}
return restoredObject;
}
else
{
return stateObj;
}
}
protected FacesContext getFacesContext()
{
if (_facesContext == null)
{
return FacesContext.getCurrentInstance();
}
else
{
return _facesContext;
}
}
boolean isCachedFacesContext()
{
return _facesContext != null;
}
void setCachedFacesContext(FacesContext facesContext)
{
_facesContext = facesContext;
}
protected StateHelper getStateHelper()
{
return getStateHelper(true);
}
/**
* returns a delta state saving enabled state helper
* for the current component
* @param create if true a state helper is created if not already existing
* @return an implementation of the StateHelper interface or null if none exists and create is set to false
*/
protected StateHelper getStateHelper(boolean create)
{
if(_stateHelper != null)
{
return _stateHelper;
}
if(create)
{
_stateHelper = new _DeltaStateHelper(this);
}
return _stateHelper;
}
public void clearInitialState()
{
_initialStateMarked = false;
}
public boolean initialStateMarked()
{
return _initialStateMarked;
}
public void markInitialState()
{
_initialStateMarked = true;
}
protected String getStringValue(FacesContext context, ValueExpression vb)
{
Object value = vb.getValue(context.getELContext());
if (value != null)
{
return value.toString();
}
return null;
}
enum PropertyKeys
{
bindings,
summaryMessage,
detailMessage
}
}