javax.faces.component.UIInput Maven / Gradle / Ivy
Show all versions of jsf-api Show documentation
/*
* $Id: UIInput.java,v 1.93.4.2 2007/11/05 21:48:57 rlubke Exp $
*/
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.faces.component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.MethodBinding;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
import javax.faces.render.Renderer;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
/**
* UIInput is a {@link UIComponent} that represents
* a component that both displays output to the user (like
* {@link UIOutput} components do) and processes request parameters on the
* subsequent request that need to be decoded. There are no restrictions
* on the data type of the local value, or the object referenced by the
* value binding expression (if any); however, individual
* {@link javax.faces.render.Renderer}s will generally impose restrictions
* on the type of data they know how to display.
*
* During the Apply Request Values phase of the request
* processing lifecycle, the decoded value of this component, usually
* but not necessarily a String, must be stored - but not yet converted -
* using setSubmittedValue()
. If the component wishes
* to indicate that no particular value was submitted, it can either
* do nothing, or set the submitted value to null
.
*
* By default, during the Process Validators phase of the
* request processing lifecycle, the submitted value will be converted
* to a typesafe object, and, if validation succeeds, stored as a
* local value using setValue()
. However, if the
* immediate
property is set to true
, this
* processing will occur instead at the end of the
* Apply Request Values phase.
*
* During the Render Response phase of the request processing
* lifecycle, conversion for output occurs as for {@link UIOutput}.
*
* When the validate()
method of this {@link UIInput}
* detects that a value change has actually occurred, and that all validations
* have been successfully passed, it will queue a
* {@link ValueChangeEvent}. Later on, the broadcast()
* method will ensure that this event is broadcast to all interested
* listeners. This event will be delivered by default in the
* Process Validators phase, but can be delivered instead
* during Apply Request Values if the immediate
* property is set to true
.
*
* By default, the rendererType
property must be set to
* "Text
". This value can be changed by calling the
* setRendererType()
method.
*/
public class UIInput extends UIOutput implements EditableValueHolder {
// ------------------------------------------------------ Manifest Constants
/**
* The standard component type for this component.
*/
public static final String COMPONENT_TYPE = "javax.faces.Input";
/**
* The standard component family for this component.
*/
public static final String COMPONENT_FAMILY = "javax.faces.Input";
/**
* The message identifier of the
* {@link javax.faces.application.FacesMessage} to be created if
* a conversion error occurs, and neither the page author nor
* the {@link ConverterException} provides a message.
*/
public static final String CONVERSION_MESSAGE_ID =
"javax.faces.component.UIInput.CONVERSION";
/**
* The message identifier of the
* {@link javax.faces.application.FacesMessage} to be created if
* a required check fails.
*/
public static final String REQUIRED_MESSAGE_ID =
"javax.faces.component.UIInput.REQUIRED";
/**
* The message identifier of the
* {@link javax.faces.application.FacesMessage} to be created if
* a model update error occurs, and the thrown exception has
* no message.
*/
public static final String UPDATE_MESSAGE_ID =
"javax.faces.component.UIInput.UPDATE";
private static final Validator[] EMPTY_VALIDATOR = new Validator[0];
/**
* The Logger
for this class.
*/
private static final Logger LOGGER =
Logger.getLogger("javax.faces.component", "javax.faces.LogStrings");
// ------------------------------------------------------------ Constructors
/**
* Create a new {@link UIInput} instance with default property
* values.
*/
public UIInput() {
super();
setRendererType("javax.faces.Text");
}
// -------------------------------------------------------------- Properties
public String getFamily() {
return (COMPONENT_FAMILY);
}
/**
* The submittedValue value of this {@link UIInput} component.
*/
private Object submittedValue = null;
/**
* Return the submittedValue value of this {@link UIInput} component.
* This method should only be used by the decode()
and
* validate()
method of this component, or
* its corresponding {@link Renderer}.
*/
public Object getSubmittedValue() {
return (this.submittedValue);
}
/**
* Set the submittedValue value of this {@link UIInput} component.
* This method should only be used by the decode()
and
* validate()
method of this component, or
* its corresponding {@link Renderer}.
*
* @param submittedValue The new submitted value
*/
public void setSubmittedValue(Object submittedValue) {
this.submittedValue = submittedValue;
}
public void setValue(Object value) {
super.setValue(value);
// Mark the local value as set.
setLocalValueSet(true);
}
/**
* Convenience method to reset this component's value to the
* un-initialized state. This method does the following:
*
* Call {@link #setValue} passing null
.
*
* Call {@link #setSubmittedValue} passing null
.
*
* Call {@link #setLocalValueSet} passing false
.
*
* Call {@link #setValid} passing true
.
*
* Upon return from this call if the instance had a
* ValueBinding
associated with it for the "value"
* property, this binding is evaluated when {@link
* UIOutput#getValue} is called. Otherwise, null
is
* returned from getValue()
.
*/
public void resetValue() {
this.setValue(null);
this.setSubmittedValue(null);
this.setLocalValueSet(false);
this.setValid(true);
}
/**
* The "localValueSet" state for this component.
*/
private boolean localValueSet;
/**
* Return the "local value set" state for this component.
* Calls to setValue()
automatically reset
* this property to true
.
*/
public boolean isLocalValueSet() {
return localValueSet;
}
/**
* Sets the "local value set" state for this component.
*/
public void setLocalValueSet(boolean localValueSet) {
this.localValueSet = localValueSet;
}
/**
*
The "required field" state for this component.
*/
private Boolean required;
/**
* Return the "required field" state for this component.
*/
public boolean isRequired() {
if (required != null) {
return (this.required);
}
ValueExpression ve = getValueExpression("required");
if (ve != null) {
try {
return (Boolean.TRUE.equals(ve.getValue(getFacesContext().getELContext())));
}
catch (ELException e) {
throw new FacesException(e);
}
} else {
return (false);
}
}
private String requiredMessage;
/**
* If there has been a call to {@link #setRequiredMessage} on this
* instance, return the message. Otherwise, call {@link #getValueExpression}
* passing the key "requiredMessage", get the result of the expression, and return it.
* Any {@link ELException}s thrown during the call to getValue()
* must be wrapped in a {@link FacesException} and rethrown.
*/
public String getRequiredMessage() {
if (requiredMessage != null) {
return requiredMessage;
}
ValueExpression ve = getValueExpression("requiredMessage");
if (ve != null) {
try {
return ((String) ve.getValue(getFacesContext().getELContext()));
}
catch (ELException e) {
throw new FacesException(e);
}
} else {
return (null);
}
}
/**
*
Override any {@link ValueExpression} set for the "requiredMessage"
* with the literal argument provided to this method. Subsequent calls
* to {@link #getRequiredMessage} will return this value;
*
* @param message the literal message value to be displayed in the event
* the user hasn't supplied a value and one is required.
*/
public void setRequiredMessage(String message) {
requiredMessage = message;
}
private String converterMessage;
/**
* If there has been a call to {@link #setConverterMessage} on this
* instance, return the message. Otherwise, call {@link #getValueExpression}
* passing the key "converterMessage", get the result of the expression, and return it.
* Any {@link ELException}s thrown during the call to getValue()
* must be wrapped in a {@link FacesException} and rethrown.
*/
public String getConverterMessage() {
if (converterMessage != null) {
return converterMessage;
}
ValueExpression ve = getValueExpression("converterMessage");
if (ve != null) {
try {
return ((String) ve.getValue(getFacesContext().getELContext()));
}
catch (ELException e) {
throw new FacesException(e);
}
} else {
return (null);
}
}
/**
*
Override any {@link ValueExpression} set for the "converterMessage"
* with the literal argument provided to this method. Subsequent calls
* to {@link #getConverterMessage} will return this value;
*
* @param message the literal message value to be displayed in the event
* conversion fails.
*/
public void setConverterMessage(String message) {
converterMessage = message;
}
private String validatorMessage;
/**
* If there has been a call to {@link #setValidatorMessage} on this
* instance, return the message. Otherwise, call {@link #getValueExpression}
* passing the key "validatorMessage", get the result of the expression, and return it.
* Any {@link ELException}s thrown during the call to getValue()
* must be wrapped in a {@link FacesException} and rethrown.
*/
public String getValidatorMessage() {
if (validatorMessage != null) {
return validatorMessage;
}
ValueExpression ve = getValueExpression("validatorMessage");
if (ve != null) {
try {
return ((String) ve.getValue(getFacesContext().getELContext()));
}
catch (ELException e) {
throw new FacesException(e);
}
} else {
return (null);
}
}
/**
*
Override any {@link ValueExpression} set for the "validatorMessage"
* with the literal argument provided to this method. Subsequent calls
* to {@link #getValidatorMessage} will return this value;
*
* @param message the literal message value to be displayed in the event
* validation fails.
*/
public void setValidatorMessage(String message) {
validatorMessage = message;
}
private boolean valid = true;
public boolean isValid() {
return (this.valid);
}
public void setValid(boolean valid) {
this.valid = valid;
}
/**
* Set the "required field" state for this component.
*
* @param required The new "required field" state
*/
public void setRequired(boolean required) {
this.required = required;
}
/**
* The immediate flag.
*/
private Boolean immediate;
public boolean isImmediate() {
if (this.immediate != null) {
return (this.immediate);
}
ValueExpression ve = getValueExpression("immediate");
if (ve != null) {
try {
return (Boolean.TRUE.equals(ve.getValue(getFacesContext().getELContext())));
}
catch (ELException e) {
throw new FacesException(e);
}
} else {
return (false);
}
}
public void setImmediate(boolean immediate) {
this.immediate = immediate;
}
/**
* Return a MethodBinding
pointing at a
* method that will be called during Process Validations
* phase of the request processing lifecycle, to validate the current
* value of this component.
*
* @deprecated {@link #getValidators} should be used instead.
*/
public MethodBinding getValidator() {
MethodBinding result = null;
Validator[] curValidators = getValidators();
// go through our lisetners list and find the one and only
// MethodBindingValidator instance, if present.
if (null != curValidators) {
for (int i = 0; i < curValidators.length; i++) {
// We are guaranteed to have at most one instance of
// MethodBindingValidator in the curValidators list.
if (MethodBindingValidator.class ==
curValidators[i].getClass()) {
result = ((MethodBindingValidator) curValidators[i]).
getWrapped();
break;
}
}
}
return result;
}
/**
* Set a MethodBinding
pointing at a
* method that will be called during Process Validations
* phase of the request processing lifecycle, to validate the current
* value of this component.
*
* Any method referenced by such an expression must be public, with
* a return type of void
, and accept parameters of type
* {@link FacesContext}, {@link UIComponent}, and Object
.
*
* @param validatorBinding The new MethodBinding
instance
* @deprecated Use {@link #addValidator} instead, obtaining the
* argument {@link Validator} by creating an instance of {@link
* javax.faces.validator.MethodExpressionValidator}.
*/
public void setValidator(MethodBinding validatorBinding) {
Validator[] curValidators = getValidators();
// see if we need to null-out, or replace an existing validator
if (null != curValidators) {
for (int i = 0; i < curValidators.length; i++) {
// if we want to remove the validatorBinding
if (null == validatorBinding) {
// We are guaranteed to have at most one instance of
// MethodBindingValidator in the curValidators
// list.
if (MethodBindingValidator.class ==
curValidators[i].getClass()) {
removeValidator(curValidators[i]);
return;
}
}
// if we want to replace the validatorBinding
else //noinspection ObjectEquality
if (validatorBinding == curValidators[i]) {
removeValidator(curValidators[i]);
break;
}
}
}
addValidator(new MethodBindingValidator(validatorBinding));
}
public MethodBinding getValueChangeListener() {
MethodBinding result = null;
ValueChangeListener[] curListeners = getValueChangeListeners();
// go through our lisetners list and find the one and only
// MethodBindingValueChangeListener instance, if present.
if (null != curListeners) {
for (int i = 0; i < curListeners.length; i++) {
// We are guaranteed to have at most one instance of
// MethodBindingValueChangeListener in the curListeners list.
if (MethodBindingValueChangeListener.class ==
curListeners[i].getClass()) {
result = ((MethodBindingValueChangeListener) curListeners[i]).
getWrapped();
break;
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link #addValueChangeListener} instead, obtaining the
* argument {@link ValueChangeListener} by creating an instance of {@link
* javax.faces.event.MethodExpressionValueChangeListener}.
*/
public void setValueChangeListener(MethodBinding valueChangeListener) {
ValueChangeListener[] curListeners = getValueChangeListeners();
// see if we need to null-out, or replace an existing listener
if (null != curListeners) {
for (int i = 0; i < curListeners.length; i++) {
// if we want to remove the valueChangeListener
if (null == valueChangeListener) {
// We are guaranteed to have at most one instance of
// MethodBindingValueChangeListener in the curListeners
// list.
if (MethodBindingValueChangeListener.class ==
curListeners[i].getClass()) {
removeFacesListener(curListeners[i]);
return;
}
}
// if we want to replace the valueChangeListener
else //noinspection ObjectEquality
if (valueChangeListener == curListeners[i]) {
removeFacesListener(curListeners[i]);
break;
}
}
}
addValueChangeListener(new MethodBindingValueChangeListener(valueChangeListener));
}
// ----------------------------------------------------- UIComponent Methods
/**
* Specialized decode behavior on top of that provided by the
* superclass. In addition to the standard
* processDecodes
behavior inherited from {@link
* UIComponentBase}, calls validate()
if the the
* immediate
property is true; if the component is
* invalid afterwards or a RuntimeException
is thrown,
* calls {@link FacesContext#renderResponse}.
*
* @throws NullPointerException {@inheritDoc}
*/
public void processDecodes(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
super.processDecodes(context);
if (isImmediate()) {
executeValidate(context);
}
}
/**
* In addition to the standard processValidators
behavior
* inherited from {@link UIComponentBase}, calls validate()
* if the immediate
property is false (which is the
* default); if the component is invalid afterwards, calls
* {@link FacesContext#renderResponse}.
* If a RuntimeException
is thrown during
* validation processing, calls {@link FacesContext#renderResponse}
* and re-throw the exception.
*
*
* @throws NullPointerException {@inheritDoc}
*/
public void processValidators(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
super.processValidators(context);
if (!isImmediate()) {
executeValidate(context);
}
}
/**
* In addition to the standard processUpdates
behavior
* inherited from {@link UIComponentBase}, calls
* updateModel()
.
* If the component is invalid afterwards, calls
* {@link FacesContext#renderResponse}.
* If a RuntimeException
is thrown during
* update processing, calls {@link FacesContext#renderResponse}
* and re-throw the exception.
*
*
* @throws NullPointerException {@inheritDoc}
*/
public void processUpdates(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
super.processUpdates(context);
try {
updateModel(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.renderResponse();
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
public void decode(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Force validity back to "true"
setValid(true);
super.decode(context);
}
/**
* Perform the following algorithm to update the model data
* associated with this {@link UIInput}, if any, as appropriate.
*
* - If the
valid
property of this component is
* false
, take no further action.
* - If the
localValueSet
property of this component is
* false
, take no further action.
* - If no {@link ValueExpression} for
value
exists,
* take no further action.
* - Call
setValue()
method of the {@link ValueExpression}
* to update the value that the {@link ValueExpression} points at.
* - If the
setValue()
method returns successfully:
*
* - Clear the local value of this {@link UIInput}.
* - Set the
localValueSet
property of this
* {@link UIInput} to false.
*
* - If the
setValue()
method call fails:
*
* - Enqueue an error message by calling
addMessage()
* on the specified {@link FacesContext} instance.
* - Set the
valid
property of this {@link UIInput}
* to false
.
*
*
*
* @param context {@link FacesContext} for the request we are processing
* @throws NullPointerException if context
* is null
*/
public void updateModel(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (!isValid() || !isLocalValueSet()) {
return;
}
ValueExpression ve = getValueExpression("value");
if (ve != null) {
try {
ve.setValue(context.getELContext(), getLocalValue());
setValue(null);
setLocalValueSet(false);
} catch (ELException e) {
String messageStr = e.getMessage();
Throwable result = e.getCause();
while (null != result &&
result.getClass().isAssignableFrom(ELException.class)) {
messageStr = result.getMessage();
result = result.getCause();
}
FacesMessage message;
if (null == messageStr) {
message =
MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
} else {
message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
messageStr,
messageStr);
}
LOGGER.log(Level.SEVERE, message.getSummary(), result);
context.addMessage(getClientId(context), message);
setValid(false);
} catch (IllegalArgumentException e) {
FacesMessage message =
MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
LOGGER.log(Level.SEVERE, message.getSummary(), e);
context.addMessage(getClientId(context), message);
setValid(false);
} catch (Exception e) {
FacesMessage message =
MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
LOGGER.log(Level.SEVERE, message.getSummary(), e);
context.addMessage(getClientId(context), message);
setValid(false);
}
}
}
// ------------------------------------------------------ Validation Methods
/**
* Perform the following algorithm to validate the local value of
* this {@link UIInput}.
*
* - Retrieve the submitted value with
getSubmittedValue()
.
* If this returns null, exit without further processing. (This
* indicates that no value was submitted for this component.)
*
* - Convert the submitted value into a "local value" of the
* appropriate data type by calling {@link #getConvertedValue}.
*
* - Validate the property by calling {@link #validateValue}.
*
* - If the
valid
property of this component is still
* true
, retrieve the previous value of the component
* (with getValue()
), store the new local value using
* setValue()
, and reset the submitted value to
* null. If the local value is different from
* the previous value of this component, fire a
* {@link ValueChangeEvent} to be broadcast to all interested
* listeners.
*
*
* Application components implementing {@link UIInput} that wish to
* perform validation with logic embedded in the component should perform
* their own correctness checks, and then call the
* super.validate()
method to perform the standard
* processing described above.
*
* @param context The {@link FacesContext} for the current request
* @throws NullPointerException if context
* is null
*/
public void validate(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Submitted value == null means "the component was not submitted
// at all"; validation should not continue
Object submittedValue = getSubmittedValue();
if (submittedValue == null) {
return;
}
Object newValue = null;
try {
newValue = getConvertedValue(context, submittedValue);
}
catch (ConverterException ce) {
addConversionErrorMessage(context, ce, submittedValue);
setValid(false);
}
validateValue(context, newValue);
// If our value is valid, store the new value, erase the
// "submitted" value, and emit a ValueChangeEvent if appropriate
if (isValid()) {
Object previous = getValue();
setValue(newValue);
setSubmittedValue(null);
if (compareValues(previous, newValue)) {
queueEvent(new ValueChangeEvent(this, previous, newValue));
}
}
}
/**
* Convert the submitted value into a "local value" of the
* appropriate data type, if necessary. Employ the following
* algorithm to do so:
*
* - If a
Renderer
is present, call
* getConvertedValue()
to convert the submitted
* value.
* - If no
Renderer
is present, and the submitted
* value is a String, locate a {@link Converter} as follows:
*
* - If
getConverter()
returns a non-null {@link Converter},
* use that instance.
* - Otherwise, if a value binding for
value
exists,
* call getType()
on it.
*
* - If this call returns
null
, assume the output
* type is String
and perform no conversion.
* - Otherwise, call
*
Application.createConverter(Class)
* to locate any registered {@link Converter} capable of
* converting data values of the specified type.
*
*
*
* - If a {@link Converter} instance was located, call its
*
getAsObject()
method to perform the conversion.
* If conversion fails:
*
* - Enqueue an appropriate error message by calling the
*
addMessage()
method on the
* FacesContext
.
* - Set the
valid
property
* on this component to false
*
* - Otherwise, use the submitted value without any conversion
*
*
*
*
*
* This method can be overridden by subclasses for more specific
* behavior.
*/
protected Object getConvertedValue(FacesContext context,
Object newSubmittedValue) throws ConverterException {
Renderer renderer = getRenderer(context);
Object newValue;
if (renderer != null) {
newValue = renderer.getConvertedValue(context, this,
newSubmittedValue);
} else if (newSubmittedValue instanceof String) {
// If there's no Renderer, and we've got a String,
// run it through the Converter (if any)
Converter converter = getConverterWithType(context);
if (converter != null) {
newValue = converter.getAsObject(context, this,
(String) newSubmittedValue);
} else {
newValue = newSubmittedValue;
}
} else {
newValue = newSubmittedValue;
}
return newValue;
}
/**
* Set the "valid" property according to the below algorithm.
*
*
*
* - If the
valid
property on this component is still
* true
, and the required
property is also
* true, ensure that the local value is not empty (where "empty" is
* defined as null
or a zero-length String. If the local
* value is empty:
*
* - Enqueue an appropriate error message by calling the
*
addMessage()
method on the FacesContext
* instance for the current request. If the {@link #getRequiredMessage}
* returns non-null
, use the value as the summary
* and detail
in the {@link FacesMessage} that
* is enqueued on the FacesContext
, otherwise
* use the message for the {@link #REQUIRED_MESSAGE_ID}.
*
* - Set the
valid
property on this component to
* false
.
*
* - If the
valid
property on this component is still
* true
, and the local value is not empty, call the
* validate()
method of each {@link Validator}
* registered for this {@link UIInput}, followed by the method
* pointed at by the validatorBinding
property (if any).
* If any of these validators or the method throws a
* {@link ValidatorException}, catch the exception, add
* its message (if any) to the {@link FacesContext}, and set
* the valid
property of this component to false.
*
*
*/
protected void validateValue(FacesContext context, Object newValue) {
// If our value is valid, enforce the required property if present
if (isValid() && isRequired() && isEmpty(newValue)) {
String requiredMessageStr = getRequiredMessage();
FacesMessage message;
if (null != requiredMessageStr) {
message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
requiredMessageStr,
requiredMessageStr);
} else {
message =
MessageFactory.getMessage(context, REQUIRED_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
}
context.addMessage(getClientId(context), message);
setValid(false);
}
// If our value is valid and not empty, call all validators
if (isValid() && !isEmpty(newValue)) {
if (this.validators != null) {
for (Validator validator : this.validators) {
try {
validator.validate(context, this, newValue);
}
catch (ValidatorException ve) {
// If the validator throws an exception, we're
// invalid, and we need to add a message
setValid(false);
FacesMessage message;
String validatorMessageString = getValidatorMessage();
if (null != validatorMessageString) {
message =
new FacesMessage(FacesMessage.SEVERITY_ERROR,
validatorMessageString,
validatorMessageString);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
} else {
message = ve.getFacesMessage();
}
if (message != null) {
context.addMessage(getClientId(context), message);
}
}
}
}
}
}
/**
* Return true
if the new value is different from the
* previous value.
*
* @param previous old value of this component (if any)
* @param value new value of this component (if any)
*/
protected boolean compareValues(Object previous, Object value) {
if (previous == null) {
return (value != null);
} else if (value == null) {
return (true);
} else {
return (!(previous.equals(value)));
}
}
/**
* Executes validation logic.
*/
private void executeValidate(FacesContext context) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.renderResponse();
}
}
private static boolean isEmpty(Object value) {
if (value == null) {
return (true);
} else if ((value instanceof String) &&
(((String) value).length() < 1)) {
return (true);
} else if (value.getClass().isArray()) {
if (0 == java.lang.reflect.Array.getLength(value)) {
return (true);
}
} else if (value instanceof List) {
if (((List) value).isEmpty()) {
return (true);
}
}
return (false);
}
/**
* The set of {@link Validator}s associated with this
* UIComponent
.
*/
List validators = null;
/**
* Add a {@link Validator} instance to the set associated with
* this {@link UIInput}.
*
* @param validator The {@link Validator} to add
* @throws NullPointerException if validator
* is null
*/
public void addValidator(Validator validator) {
if (validator == null) {
throw new NullPointerException();
}
if (validators == null) {
//noinspection CollectionWithoutInitialCapacity
validators = new ArrayList();
}
validators.add(validator);
}
/**
* Return the set of registered {@link Validator}s for this
* {@link UIInput} instance. If there are no registered validators,
* a zero-length array is returned.
*/
public Validator[] getValidators() {
if (validators == null) {
return EMPTY_VALIDATOR;
} else {
return (validators.toArray(new Validator[validators.size()]));
}
}
/**
* Remove a {@link Validator} instance from the set associated with
* this {@link UIInput}, if it was previously associated.
* Otherwise, do nothing.
*
* @param validator The {@link Validator} to remove
*/
public void removeValidator(Validator validator) {
if (validators != null) {
validators.remove(validator);
}
}
// ------------------------------------------------ Event Processing Methods
/**
* Add a new {@link ValueChangeListener} to the set of listeners
* interested in being notified when {@link ValueChangeEvent}s occur.
*
* @param listener The {@link ValueChangeListener} to be added
* @throws NullPointerException if listener
* is null
*/
public void addValueChangeListener(ValueChangeListener listener) {
addFacesListener(listener);
}
/**
* Return the set of registered {@link ValueChangeListener}s for this
* {@link UIInput} instance. If there are no registered listeners,
* a zero-length array is returned.
*/
public ValueChangeListener[] getValueChangeListeners() {
return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
}
/**
* Remove an existing {@link ValueChangeListener} (if any) from the
* set of listeners interested in being notified when
* {@link ValueChangeEvent}s occur.
*
* @param listener The {@link ValueChangeListener} to be removed
* @throws NullPointerException if listener
* is null
*/
public void removeValueChangeListener(ValueChangeListener listener) {
removeFacesListener(listener);
}
// ----------------------------------------------------- StateHolder Methods
private Object[] values;
public Object saveState(FacesContext context) {
if (values == null) {
values = new Object[9];
}
values[0] = super.saveState(context);
values[1] = localValueSet;
values[2] = required;
values[3] = requiredMessage;
values[4] = converterMessage;
values[5] = validatorMessage;
values[6] = valid;
values[7] = immediate;
values[8] = saveAttachedState(context, validators);
return (values);
}
public void restoreState(FacesContext context, Object state) {
values = (Object[]) state;
super.restoreState(context, values[0]);
localValueSet = (Boolean) values[1];
required = (Boolean) values[2];
requiredMessage = ((String) values[3]);
converterMessage = ((String) values[4]);
validatorMessage = ((String) values[5]);
valid = (Boolean) values[6];
immediate = (Boolean) values[7];
List restoredValidators;
Iterator iter;
if (null != (restoredValidators = TypedCollections.dynamicallyCastList((List)
restoreAttachedState(context, values[8]), Validator.class))) {
// if there were some validators registered prior to this
// method being invoked, merge them with the list to be
// restored.
if (null != validators) {
iter = restoredValidators.iterator();
while (iter.hasNext()) {
validators.add(iter.next());
}
} else {
validators = restoredValidators;
}
}
}
private Converter getConverterWithType(FacesContext context) {
Converter converter = getConverter();
if (converter != null) {
return converter;
}
ValueExpression valueExpression = getValueExpression("value");
if (valueExpression == null) {
return null;
}
Class converterType;
try {
converterType = valueExpression.getType(context.getELContext());
}
catch (ELException e) {
throw new FacesException(e);
}
// if converterType is null, String, or Object, assume
// no conversion is needed
if (converterType == null ||
converterType == String.class ||
converterType == Object.class) {
return null;
}
// if getType returns a type for which we support a default
// conversion, acquire an appropriate converter instance.
try {
Application application = context.getApplication();
return application.createConverter(converterType);
} catch (Exception e) {
return (null);
}
}
private void addConversionErrorMessage(FacesContext context,
ConverterException ce, Object value) {
FacesMessage message;
String converterMessageString = getConverterMessage();
if (null != converterMessageString) {
message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
converterMessageString,
converterMessageString);
} else {
message = ce.getFacesMessage();
if (message == null) {
message = MessageFactory.getMessage(context,
CONVERSION_MESSAGE_ID);
if (message.getDetail() == null) {
message.setDetail(ce.getMessage());
}
}
}
context.addMessage(getClientId(context), message);
}
}