/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package javax.faces.component; import java.util.*; import java.util.logging.*; import javax.el.*; import javax.faces.*; import javax.faces.application.*; import javax.faces.context.*; import javax.faces.convert.*; import javax.faces.el.*; import javax.faces.event.*; import javax.faces.render.*; import javax.faces.validator.*; public class UIInput extends UIOutput implements EditableValueHolder { private static final Logger log = Logger.getLogger(UIInput.class.getName()); public static final String COMPONENT_FAMILY = "javax.faces.Input"; public static final String COMPONENT_TYPE = "javax.faces.Input"; public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION"; public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED"; public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE"; private static final Validator []NULL_VALIDATORS = new Validator[0]; private static final HashMap<String,PropEnum> _propMap = new HashMap<String,PropEnum>(); private ValueExpression _valueExpr; private Boolean _required; private ValueExpression _requiredExpr; private Boolean _immediate; private ValueExpression _immediateExpr; private String _requiredMessage; private ValueExpression _requiredMessageExpr; private String _converterMessage; private ValueExpression _converterMessageExpr; private String _validatorMessage; private ValueExpression _validatorMessageExpr; // private boolean _isValid = true; private boolean _isLocalValueSet; private Object _submittedValue; private ArrayList<Validator> _validatorList; private Validator []_validators = NULL_VALIDATORS; public UIInput() { setRendererType("javax.faces.Text"); } /** * Returns the component family, used to select the renderer. */ public String getFamily() { return COMPONENT_FAMILY; } // // properties // public String getRequiredMessage() { if (_requiredMessage != null) return _requiredMessage; else if (_requiredMessageExpr != null) return Util.evalString(_requiredMessageExpr, getFacesContext()); else return null; } public void setRequiredMessage(String value) { _requiredMessage = value; } public String getConverterMessage() { if (_converterMessage != null) return _converterMessage; else if (_converterMessageExpr != null) return Util.evalString(_converterMessageExpr, getFacesContext()); else return null; } public void setConverterMessage(String value) { _converterMessage = value; } public String getValidatorMessage() { if (_validatorMessage != null) return _validatorMessage; else if (_validatorMessageExpr != null) return Util.evalString(_validatorMessageExpr, getFacesContext()); else return null; } public void setValidatorMessage(String value) { _validatorMessage = value; } /** * Returns the value expression with the given name. */ @Override public ValueExpression getValueExpression(String name) { PropEnum prop = _propMap.get(name); if (prop != null) { switch (_propMap.get(name)) { case VALUE: return _valueExpr; case IMMEDIATE: return _immediateExpr; case REQUIRED: return _requiredExpr; case REQUIRED_MESSAGE: return _requiredMessageExpr; case CONVERTER_MESSAGE: return _converterMessageExpr; case VALIDATOR_MESSAGE: return _validatorMessageExpr; } } return super.getValueExpression(name); } /** * Sets the value expression with the given name. */ @Override public void setValueExpression(String name, ValueExpression expr) { PropEnum prop = _propMap.get(name); if (prop != null) { switch (_propMap.get(name)) { case VALUE: if (expr != null && ! expr.isLiteralText()) _valueExpr = expr; break; case IMMEDIATE: if (expr != null && expr.isLiteralText()) { _immediate = (Boolean) expr.getValue(null); return; } else _immediateExpr = expr; break; case REQUIRED: if (expr != null && expr.isLiteralText()) { _required = (Boolean) expr.getValue(null); return; } else _requiredExpr = expr; break; case REQUIRED_MESSAGE: if (expr != null && expr.isLiteralText()) { _requiredMessage = (String) expr.getValue(null); return; } else _requiredMessageExpr = expr; break; case CONVERTER_MESSAGE: if (expr != null && expr.isLiteralText()) { _converterMessage = (String) expr.getValue(null); return; } else _converterMessageExpr = expr; break; case VALIDATOR_MESSAGE: if (expr != null && expr.isLiteralText()) { _validatorMessage = (String) expr.getValue(null); return; } else _validatorMessageExpr = expr; break; } } super.setValueExpression(name, expr); } // // EditableValueHolder properties. // public boolean isRequired() { if (_required != null) return _required; else if (_requiredExpr != null) return Util.evalBoolean(_requiredExpr, getFacesContext()); else return false; } public void setRequired(boolean required) { _required = required; } public boolean isImmediate() { if (_immediate != null) return _immediate; else if (_immediateExpr != null) return Util.evalBoolean(_immediateExpr, getFacesContext()); else return false; } public void setImmediate(boolean immediate) { _immediate = immediate; } public Object getSubmittedValue() { return _submittedValue; } public void setSubmittedValue(Object submittedValue) { _submittedValue = submittedValue; } public void setValue(Object value) { super.setValue(value); setLocalValueSet(true); } public boolean isLocalValueSet() { return _isLocalValueSet; } public void setLocalValueSet(boolean isSet) { _isLocalValueSet = isSet; } public void resetValue() { setValue(null); setSubmittedValue(null); setLocalValueSet(false); setValid(true); } public boolean isValid() { return _isValid; } public void setValid(boolean valid) { _isValid = valid; } /** * @deprecated */ public MethodBinding getValueChangeListener() { FacesListener []listeners = getFacesListeners(FacesListener.class); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof ValueChangeListenerAdapter) { ValueChangeListenerAdapter adapter = (ValueChangeListenerAdapter) listeners[i]; return adapter.getBinding(); } } return null; } /** * @deprecated */ public void setValueChangeListener(MethodBinding binding) { ValueChangeListener listener = new ValueChangeListenerAdapter(binding); FacesListener []listeners = getFacesListeners(FacesListener.class); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof ValueChangeListenerAdapter) { removeFacesListener(listeners[i]); } } addValueChangeListener(listener); } public void addValidator(Validator validator) { Validator []newValidators = new Validator[_validators.length + 1]; System.arraycopy(_validators, 0, newValidators, 0, _validators.length); newValidators[_validators.length] = validator; _validators = newValidators; } public void removeValidator(Validator validator) { int length = _validators.length; for (int i = 0; i < length; i++) { if (_validators[i] == validator) { Validator []newValidators = new Validator[length - 1]; System.arraycopy(_validators, 0, newValidators, 0, i); System.arraycopy(_validators, i + 1, newValidators, i, length - i - 1); _validators = newValidators; return; } } } public Validator []getValidators() { return _validators; } /** * @deprecated */ public MethodBinding getValidator() { int length = _validators.length; for (int i = 0; i < length; i++) { Validator validator = _validators[i]; if (validator instanceof ValidatorAdapter) { ValidatorAdapter adapter = (ValidatorAdapter) validator; return adapter.getBinding(); } } return null; } /** * @deprecated */ public void setValidator(MethodBinding binding) { ValidatorAdapter adapter = new ValidatorAdapter(binding); int length = _validators.length; for (int i = 0; i < length; i++) { Validator validator = _validators[i]; if (validator instanceof ValidatorAdapter) { _validators[i] = adapter; return; } } addValidator(adapter); } public void addValueChangeListener(ValueChangeListener listener) { addFacesListener(listener); } public void removeValueChangeListener(ValueChangeListener listener) { removeFacesListener(listener); } public ValueChangeListener []getValueChangeListeners() { return (ValueChangeListener []) getFacesListeners(ValueChangeListener.class); } // // processing // @Override public void decode(FacesContext context) { setValid(true); super.decode(context); } public void processDecodes(FacesContext context) { if (isRendered()) { super.processDecodes(context); if (isImmediate()) { try { validate(context); } catch (RuntimeException e) { context.renderResponse(); throw e; } if (! isValid()) context.renderResponse(); } } } public void processUpdates(FacesContext context) { if (isRendered()) { super.processUpdates(context); try { updateModel(context); } catch (RuntimeException e) { context.renderResponse(); throw e; } if (! isValid()) context.renderResponse(); } } public void updateModel(FacesContext context) { if (! isValid()) return; if (! isLocalValueSet()) return; if (_valueExpr == null) return; try { _valueExpr.setValue(context.getELContext(), getLocalValue()); setValue(null); setLocalValueSet(false); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); setValid(false); String summary = Util.l10n(context, UPDATE_MESSAGE_ID, "{0}: An error occurred while processing your submitted information.", Util.getLabel(context, this)); String detail = summary; FacesMessage msg = new FacesMessage(summary, detail); context.addMessage(getClientId(context), msg); } } @Override public void processValidators(FacesContext context) { if (isRendered()) { super.processValidators(context); try { if (! isImmediate()) validate(context); if (! isValid()) context.renderResponse(); } catch (RuntimeException e) { context.renderResponse(); throw e; } } } public void validate(FacesContext context) { Object submittedValue = getSubmittedValue(); if (submittedValue == null) return; Object value; try { value = getConvertedValue(context, submittedValue); validateValue(context, value); if (! isValid()) { context.renderResponse(); return; } } catch (ConverterException e) { log.log(Level.FINE, e.toString(), e); setValid(false); context.renderResponse(); final String converterMessage = getConverterMessage(); FacesMessage msg = e.getFacesMessage(); if (msg == null) { String summary = null; if (converterMessage != null) summary = converterMessage; else summary = Util.l10n(context, CONVERSION_MESSAGE_ID, "{0}: Conversion error occurred.", Util.getLabel(context, this)); String detail = summary; msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail); } else if (converterMessage != null) { msg.setSummary(converterMessage); msg.setDetail(converterMessage); } context.addMessage(getClientId(context), msg); return; } Object oldValue = getValue(); setValue(value); setSubmittedValue(null); if (compareValues(oldValue, value) && getFacesListeners(FacesListener.class).length > 0) { ValueChangeEvent event = new ValueChangeEvent(this, oldValue, value); broadcast(event); } } protected Object getConvertedValue(FacesContext context, Object submittedValue) throws ConverterException { Renderer renderer = getRenderer(context); if (renderer != null) return renderer.getConvertedValue(context, this, submittedValue); else if (submittedValue instanceof String) { Converter converter = getConverter(); if (converter != null) return converter.getAsObject(context, this, (String) submittedValue); if (_valueExpr != null) { Class type = _valueExpr.getType(context.getELContext()); if (type != null) { converter = context.getApplication().createConverter(type); if (converter != null) { return converter.getAsObject(context, this, (String) submittedValue); } } } } return submittedValue; } protected boolean compareValues(Object oldValue, Object newValue) { if (oldValue == newValue) return false; else if (oldValue == null || newValue == null) return true; else return ! oldValue.equals(newValue); } protected void validateValue(FacesContext context, Object value) { if (! isValid()) { } else if (value != null && ! "".equals(value) && ! (value.getClass().isArray() && java.lang.reflect.Array.getLength(value) == 0)) { for (Validator validator : getValidators()) { try { validator.validate(context, this, value); } catch (ValidatorException e) { log.log(Level.FINER, e.toString(), e); FacesMessage msg = e.getFacesMessage(); String validatorMessage = getValidatorMessage(); if (msg == null) { final String summary; final String detail; if (validatorMessage != null) { summary = validatorMessage; detail = validatorMessage; } else { summary = e.getMessage(); detail = e.toString(); } msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail); } else { if (validatorMessage != null) { msg.setSummary(validatorMessage); msg.setDetail(validatorMessage); } } context.addMessage(getClientId(context), msg); setValid(false); } } } else if (isRequired()) { final FacesMessage msg; String requiredMessage = getRequiredMessage(); if (requiredMessage != null) msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, requiredMessage, requiredMessage); else { String summary = Util.l10n(context, REQUIRED_MESSAGE_ID, "{0}: UIInput validation Error: Value is required.", Util.getLabel(context, this)); String detail = summary; msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail); } context.addMessage(getClientId(context), msg); setValid(false); return; } } // // state // public Object saveState(FacesContext context) { int offset = 6; Object []state = new Object[offset + 2 * _validators.length]; state[0] = super.saveState(context); state[1] = _immediate; state[2] = _required; state[3] = _requiredMessage; state[4] = _converterMessage; state[5] = _validatorMessage; if (_validators.length > 0) { for (int i = 0; i < _validators.length; i++) { Validator validator = _validators[i]; int index = offset + 2 * i; state[index] = validator.getClass(); if (validator instanceof StateHolder) { StateHolder holder = (StateHolder) validator; state[index + 1] = holder.saveState(context); } } } return state; } public void restoreState(FacesContext context, Object value) { Object []state = (Object []) value; super.restoreState(context, state[0]); _immediate = (Boolean) state[1]; _required = (Boolean) state[2]; _requiredMessage = (String) state[3]; _converterMessage = (String) state[4]; _validatorMessage = (String) state[5]; _valueExpr = super.getValueExpression("value"); int offset = 6; if (offset < state.length) { _validators = new Validator[(state.length - offset) / 2]; for (int i = 0; i < _validators.length; i++) { int index = offset + 2 * i; Class cl = (Class) state[index]; try { Validator validator = (Validator) cl.newInstance(); if (validator instanceof StateHolder) { StateHolder holder = (StateHolder) validator; holder.restoreState(context, state[index + 1]); } _validators[i] = validator; } catch (Exception e) { throw new FacesException(e); } } } } // // private helpers // private static enum PropEnum { IMMEDIATE, REQUIRED, VALUE, REQUIRED_MESSAGE, CONVERTER_MESSAGE, VALIDATOR_MESSAGE, } private static class ValueChangeListenerAdapter implements ValueChangeListener, StateHolder { private MethodBinding _binding; private boolean _transient; public ValueChangeListenerAdapter() { } public ValueChangeListenerAdapter(MethodBinding binding) { _binding = binding; } MethodBinding getBinding() { return _binding; } public void processValueChange(ValueChangeEvent event) throws AbortProcessingException { FacesContext facesContext = FacesContext.getCurrentInstance(); _binding.invoke(facesContext, new Object[] { event }); } public Object saveState(FacesContext context) { return _binding; } public void restoreState(FacesContext context, Object state) { _binding = (MethodBinding) state; } public boolean isTransient() { return _transient; } public void setTransient(boolean isTransient) { _transient = isTransient; } public String toString() { return "ValueChangeListenerAdapter[" + _binding + "]"; } } private static class ValidatorAdapter implements Validator, StateHolder { private MethodBinding _binding; private boolean _transient; public ValidatorAdapter() { } public ValidatorAdapter(MethodBinding binding) { _binding = binding; } MethodBinding getBinding() { return _binding; } public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { try { FacesContext facesContext = FacesContext.getCurrentInstance(); _binding.invoke(facesContext, new Object[] { context, component, value }); } catch (EvaluationException e) { if (e.getCause() instanceof ValidatorException) throw (ValidatorException) e.getCause(); else if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause(); else throw e; } } public Object saveState(FacesContext context) { return _binding; } public void restoreState(FacesContext context, Object state) { _binding = (MethodBinding) state; } public boolean isTransient() { return _transient; } public void setTransient(boolean isTransient) { _transient = isTransient; } public String toString() { return "ValueChangeListenerAdapter[" + _binding + "]"; } } static { _propMap.put("value", PropEnum.VALUE); _propMap.put("immediate", PropEnum.IMMEDIATE); _propMap.put("required", PropEnum.REQUIRED); _propMap.put("requiredMessage", PropEnum.REQUIRED_MESSAGE); _propMap.put("converterMessage", PropEnum.CONVERTER_MESSAGE); _propMap.put("validatorMessage", PropEnum.VALIDATOR_MESSAGE); } }