package org.jboss.seam.ui.converter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.el.ValueExpression; import javax.faces.component.StateHolder; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; import javax.faces.component.ValueHolder; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; /** * This class provides a chainable converter for JSF. * * Any JSF converter can be placed at the end of the chain. A converter that is * placed higher up the chain should return ConverterChain.CONTINUE if * conversion has failed. If the all converters run return * ConverterChain.CONTINUE an unconverted value will be returned. * * A converter can be placed in the chain with a priority, the order in which * converters with the same priority is run is not specified. * */ public class ConverterChain implements Converter, StateHolder { public static final String CONTINUE = "org.jboss.seam.ui.ConverterChain.continue"; /** * This places the converter at the end of the chain. No garuntee is made * about the order converters which are placed on the queue with this * priority will be run */ public static final int CHAIN_END = Integer.MAX_VALUE; /** * This places the converter at the head of the chain. No garuntee is made * about the order converters which are placed on the queue with this * priority will be run */ public static final int CHAIN_START = 0; private List<PrioritizableConverter> converters; private boolean dirty; public ConverterChain() { // A Priority Queue would be nice but JSF has issues serializing that converters = new ArrayList<PrioritizableConverter>(); } /** * Set up a ConverterChain for this component. * * This replaces any existing converter with a ConverterChain with the * existing Converter at the end of the chain * * @param component */ public ConverterChain(UIComponent component) { this(); if (component instanceof ValueHolder) { ValueHolder valueHolder = (ValueHolder) component; if (!(valueHolder.getConverter() instanceof ConverterChain)) { ValueExpression converterValueExpression = component.getValueExpression("converter"); if (converterValueExpression != null) { addConverterToChain(converterValueExpression); } else if (valueHolder.getConverter() != null) { addConverterToChain(valueHolder.getConverter()); } else { ValueExpression valueExpression = component.getValueExpression("value"); FacesContext facesContext = FacesContext.getCurrentInstance(); if (valueExpression != null) { Class<?> type = valueExpression.getType(facesContext.getELContext()); if (type != null) { Converter converter = facesContext.getApplication().createConverter(type); if (converter != null) { addConverterToChain(converter); } } } } valueHolder.setConverter(this); } } } public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException { Object output = value; for (Converter converter : getConverters()) { Object result = converter.getAsObject(context, component, value); if (!CONTINUE.equals(result)) { output = result; break; } } return output; } public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException { String output = value == null ? null : value.toString(); for (Converter converter : getConverters()) { String result = converter.getAsString(context, component, value); if (!CONTINUE.equals(result)) { output = result; break; } } return output; } /** * Add a converter to the end of the chain */ public boolean addConverterToChain(Converter c) { return addConverterToChain(c, CHAIN_END); } /** * Add a converter to the end of the chain */ public boolean addConverterToChain(ValueExpression c) { return addConverterToChain(c, CHAIN_END); } /** * Add a converter to the chain with a defined priority */ public boolean addConverterToChain(Converter c, int priority) { if (c != null) { dirty = true; return converters.add(new PrioritizableConverter(c, priority)); } else { return false; } } /** * Add a converter to the chain with a defined priority */ public boolean addConverterToChain(ValueExpression c, int priority) { if (c != null) { dirty = true; return converters.add(new PrioritizableConverter(c, priority)); } else { return false; } } private boolean _transient; public boolean isTransient() { return _transient; } public void restoreState(FacesContext context, Object state) { Object[] values = (Object[]) state; converters = (List<PrioritizableConverter>) UIComponentBase.restoreAttachedState(context, values[0]); dirty = true; } public Object saveState(FacesContext context) { Object[] values = new Object[1]; values[0] = UIComponentBase.saveAttachedState(context, converters); return values; } public void setTransient(boolean newTransientValue) { this._transient = newTransientValue; } private List<PrioritizableConverter> getConverters() { if (dirty) { Collections.sort(converters); } return converters; } public boolean containsConverterType(Converter converter) { // TODO Improve this for (Converter c : converters) { if (c.getClass().equals(converter.getClass())) { return true; } } return false; } }