javax.faces.component.UIInput Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 Oracle and/or its affiliates. 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_1_1.html
* or packager/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 packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.ExceptionHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.MethodBinding;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.PhaseId;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreValidateEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
import javax.faces.render.Renderer;
import javax.faces.validator.BeanValidator;
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
. If the validation
* fails, the implementation must call {@link
* FacesContext#validationFailed}.
* 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 {
/* PENDING_2_1 (edburns,rogerk) this should be exposed as public constant */
private static final String EMPTY_STRING_AS_NULL =
"javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
private static final String BEANS_VALIDATION_AVAILABLE =
"javax.faces.private.BEANS_VALIDATION_AVAILABLE";
// ------------------------------------------------------ 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";
/**
* The name of an application parameter
* that indicates how empty values should be handled with respect to
* validation. See {@link #validateValue} for the allowable values
* and specification of how they should be interpreted.
*/
public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME =
"javax.faces.VALIDATE_EMPTY_FIELDS";
private static final Validator[] EMPTY_VALIDATOR = new Validator[0];
private transient Boolean emptyStringIsNull;
private transient Boolean validateEmptyFields;
enum PropertyKeys {
/**
* The "localValueSet" state for this component.
*/
localValueSet,
/**
*
If the input is required or not.
*/
required,
/**
* Custom message to be displayed if input is required but non was submitted.
*/
requiredMessage,
/**
* Custom message to be displayed when conversion fails.
*/
converterMessage,
/**
* Custom message to be displayed when validation fails.
*/
validatorMessage,
/**
* Flag indicating whether or not this component is valid.
*/
valid,
/**
* Flag indicating when conversion/validation should occur.
*/
immediate,
}
// ------------------------------------------------------------ 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 transient 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;
}
/**
* If there is a local value, return it,
* otherwise return the result of calling {@code super.getVaue()}.
*
* @since 2.2
*/
@Override
public Object getValue() {
return isLocalValueSet() ? getLocalValue() : super.getValue();
}
@Override
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 UIOutput#setValue}.
*
* Call {@link #setSubmittedValue} passing null
.
*
* Clear state for property localValueSet
.
*
* Clear state for property valid
.
*
* 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()
.
*/
@Override
public void resetValue() {
super.resetValue();
this.setSubmittedValue(null);
getStateHelper().remove(PropertyKeys.localValueSet);
getStateHelper().remove(PropertyKeys.valid);
}
/**
* Return the "local value set" state for this component.
* Calls to setValue()
automatically reset
* this property to true
.
*/
public boolean isLocalValueSet() {
return (Boolean) getStateHelper().eval(PropertyKeys.localValueSet, false);
}
/**
* Sets the "local value set" state for this component.
*/
public void setLocalValueSet(boolean localValueSet) {
getStateHelper().put(PropertyKeys.localValueSet, localValueSet);
}
/**
* Return the "required field" state for this component.
*/
public boolean isRequired() {
return (Boolean) getStateHelper().eval(PropertyKeys.required, false);
}
/**
* 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() {
return (String) getStateHelper().eval(PropertyKeys.requiredMessage);
}
/**
*
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) {
getStateHelper().put(PropertyKeys.requiredMessage, message);
}
/**
* 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() {
return (String) getStateHelper().eval(PropertyKeys.converterMessage);
}
/**
*
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) {
getStateHelper().put(PropertyKeys.converterMessage, message);
}
/**
* 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() {
return (String) getStateHelper().eval(PropertyKeys.validatorMessage);
}
/**
*
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) {
getStateHelper().put(PropertyKeys.validatorMessage, message);
}
public boolean isValid() {
return (Boolean) getStateHelper().eval(PropertyKeys.valid, true);
}
public void setValid(boolean valid) {
getStateHelper().put(PropertyKeys.valid, valid);
}
/**
* Set the "required field" state for this component.
*
* @param required The new "required field" state
*/
public void setRequired(boolean required) {
getStateHelper().put(PropertyKeys.required, required);
}
public boolean isImmediate() {
return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false);
}
public void setImmediate(boolean immediate) {
getStateHelper().put(PropertyKeys.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
/**
*
* In addition to the actions taken in {@link UIOutput}
* when {@link PartialStateHolder#markInitialState()} is called,
* check if any of the installed {@link Validator}s are PartialStateHolders and
* if so, call {@link javax.faces.component.PartialStateHolder#markInitialState()}
* as appropriate.
*
*/
@Override
public void markInitialState() {
super.markInitialState();
if (validators != null) {
validators.markInitialState();
}
}
@Override
public void clearInitialState() {
if (initialStateMarked()) {
super.clearInitialState();
if (validators != null) {
validators.clearInitialState();
}
}
}
/**
* 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;
}
pushComponentToEL(context, this);
if (!isImmediate()) {
Application application = context.getApplication();
application.publishEvent(context, PreValidateEvent.class, this);
executeValidate(context);
application.publishEvent(context, PostValidateEvent.class, this);
}
for (Iterator i = getFacetsAndChildren(); i.hasNext(); ) {
i.next().processValidators(context);
}
popComponentFromEL(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 throws an Exception:
*
* - Enqueue an error message. Create a
* {@link FacesMessage} with the id {@link #UPDATE_MESSAGE_ID}. Create a
* {@link UpdateModelException}, passing the
FacesMessage
and
* the caught exception to the constructor. Create an
* {@link ExceptionQueuedEventContext}, passing the FacesContext
,
* the UpdateModelException
, this component instance, and
* {@link PhaseId#UPDATE_MODEL_VALUES} to its constructor. Call
* {@link FacesContext#getExceptionHandler} and then call
* {@link ExceptionHandler#processEvent}, passing the
* ExceptionQueuedEventContext
.
*
* - Set the
valid
property of this {@link UIInput}
* to false
.
*
* The exception must not be re-thrown. This enables tree traversal
* to continue for this lifecycle phase, as in all the other lifecycle
* phases.
*
*
* @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) {
Throwable caught = null;
FacesMessage message = null;
try {
ve.setValue(context.getELContext(), getLocalValue());
resetValue();
} catch (ELException e) {
caught = e;
String messageStr = e.getMessage();
Throwable result = e.getCause();
while (null != result &&
result.getClass().isAssignableFrom(ELException.class)) {
messageStr = result.getMessage();
result = result.getCause();
}
if (null == messageStr) {
message =
MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
} else {
message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
messageStr,
messageStr);
}
setValid(false);
} catch (Exception e) {
caught = e;
message =
MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
MessageFactory.getLabel(
context, this));
setValid(false);
}
if (caught != null) {
assert(message != null);
// PENDING(edburns): verify this is in the spec.
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
UpdateModelException toQueue =
new UpdateModelException(message, caught);
ExceptionQueuedEventContext eventContext =
new ExceptionQueuedEventContext(context,
toQueue,
this,
PhaseId.UPDATE_MODEL_VALUES);
context.getApplication().publishEvent(context,
ExceptionQueuedEvent.class,
eventContext);
}
}
}
// ------------------------------------------------------ Validation Methods
/**
* Perform the following algorithm to
* validate the local value of this {@link UIInput}.
*
* - Retrieve the submitted value with {@link #getSubmittedValue}.
* If this returns
null
, exit without further
* processing. (This indicates that no value was submitted for this
* component.)
*
* - If the
*
javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
* context parameter value is true
(ignoring case), and
* getSubmittedValue()
returns a zero-length
* String
call {@link #setSubmittedValue}
,
* passing null
as the argument and continue processing
* using null
as the current submitted
* value.
*
*
-
*
- Enqueue an appropriate error message by calling the
*
addMessage()
method on the *FacesContext
.
* - Set the
valid
property * on this component tofalse
*
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
* with a call to {@link #setSubmittedValue}
* passing {@code null} as the argument.
* If the local value is different from the previous value of this
* component, as determined by a
* call to {@link #compareValues}, 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.
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".
Object submittedValue = getSubmittedValue();
if (submittedValue == null) {
return;
}
// If non-null, an instanceof String, and we're configured to treat
// zero-length Strings as null:
// call setSubmittedValue(null)
if ((considerEmptyStringNull(context)
&& submittedValue instanceof String
&& ((String) submittedValue).length() == 0)) {
setSubmittedValue(null);
submittedValue = null;
}
Object newValue = null;
try {
newValue = getConvertedValue(context, submittedValue);
}
catch (ConverterException ce) {
addConversionErrorMessage(context, ce);
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, * callgetType()
on it. *-
*
- If this call returns
null
, assume the output * type isString
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 this call returns
- If
- If a {@link Converter} instance was located, call its
*
getAsObject()
method to perform the conversion. * If conversion fails, the *Converter
will have thrown * aConverterException
which is declared as a checked exception * on this method, and thus must be handled by the caller.
* - 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 * stilltrue
, and therequired
property * is alsotrue
, ensure that the local value is not * empty (where "empty" is defined asnull
or a * zero-length String). If the local value is empty:-
*
Enqueue an appropriate error message by calling the *
addMessage()
method on theFacesContext
* instance for the current request. If the {@link * #getRequiredMessage} returns non-null
, use the value * as thesummary
anddetail
in the {@link * FacesMessage} that is enqueued on theFacesContext
, * otherwise use the message for the {@link #REQUIRED_MESSAGE_ID}. *- Set the
valid
property on this component * tofalse
.
* If calling {@link * ValidatorException#getFacesMessages} returns * non-
null
, each message should be added to the *FacesContext
. Otherwise the single message returned * from {@link ValidatorException#getFacesMessage} should be * added.
*
*
*
*
* Otherwise, if the *
*valid
property on this component is still *true
, take the following action to determine if * validation of this component should proceed.-
*
*
If the value is not empty, validation should proceed.
* If the value is empty, but the system has been directed to * validate empty fields, validation should proceed. The * implementation must obtain the init parameter
Map
* from theExternalContext
and inspect the value for * the key given by the value of the symbolic constant {@link * #VALIDATE_EMPTY_FIELDS_PARAM_NAME}. If there is no value under * that key, use the same key and look in the application map from * theExternalContext
. If the value is *null
or equal to the string * “auto
” (without the quotes) take * appropriate action to determine if Bean Validation is present in * the runtime environment. If not, validation should not proceed. * If so, validation should proceed. If the value is equal * (ignoring case) to “true
” (without the * quotes) validation should proceed. Otherwise, validation should * not proceed.
*
If the above determination indicates that validation should * proceed, call the
validate()
method of each {@link * Validator} registered for this {@link UIInput}, followed by the * method pointed at by thevalidatorBinding
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.
*
Return true
if the new value is different from
* the previous value. First compare the two values by passing
* value to the equals
method on argument
* previous. If that method returns true
,
* return true
. If that method returns
* false
, and both arguments implement
* java.lang.Comparable
, compare the two values by
* passing value to the compareTo
method on
* argument previous. Return true
if this
* method returns 0
, false
otherwise.
The set of {@link Validator}s associated with this
* UIComponent
.
Add a {@link Validator} instance to the set associated with * this {@link UIInput}.
* * @param validator The {@link Validator} to add * @throws NullPointerException ifvalidator
* is null
*/
public void addValidator(Validator validator) {
if (validator == null) {
throw new NullPointerException();
}
if (validators == null) {
validators = new AttachedObjectListHolderReturn 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() { return ((validators != null) ? validators.asArray(Validator.class) : EMPTY_VALIDATOR); } /** *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 (validator == null) { return; } 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 iflistener
* 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 iflistener
* is null
*/
public void removeValueChangeListener(ValueChangeListener listener) {
removeFacesListener(listener);
}
// ----------------------------------------------------- StateHolder Methods
@Override
public Object saveState(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
Object[] result = null;
Object superState = super.saveState(context);
Object validatorsState = ((validators != null) ? validators.saveState(context) : null);
if (superState != null || validatorsState != null) {
result = new Object[] { superState, validatorsState};
}
return (result);
}
@Override
public void restoreState(FacesContext context, Object state) {
if (context == null) {
throw new NullPointerException();
}
if (state == null) {
return;
}
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
if (values[1] != null) {
if (validators == null) {
validators = new AttachedObjectListHolder