/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2015 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.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;
/**
* <p><span class="changed_modified_2_0 changed_modified_2_0_rev_a
* changed_modified_2_2 changed_modified_2_3"><strong>UIInput</strong></span> 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.</p>
*
* <p>During the <em>Apply Request Values</em> 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 <code>setSubmittedValue()</code>. If the
* component wishes to indicate that no particular value was submitted,
* it can either do nothing, or set the submitted value to
* <code>null</code>.</p>
* <p>By default, during the <em>Process Validators</em> 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 <code>setValue()</code>. However, if the
* <code>immediate</code> property is set to <code>true</code>, this
* processing will occur instead at the end of the <em>Apply Request
* Values</em> phase.</p>
* <p>During the <em>Render Response</em> phase of the request
* processing lifecycle, conversion for output occurs as for {@link
* UIOutput}.</p>
* <p>When the <code>validate()</code> 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 <code>broadcast()</code> method
* will ensure that this event is broadcast to all interested listeners.
* This event will be delivered by default in the <em>Process
* Validators</em> phase, but can be delivered instead during <em>Apply
* Request Values</em> if the <code>immediate</code> property is set to
* <code>true</code>. <span class="changed_added_2_0">If the validation
* fails, the implementation must call {@link
* FacesContext#validationFailed}.</span></p>
* <p>By default, the <code>rendererType</code> property must be set to
* "<code>Text</code>". This value can be changed by calling the
* <code>setRendererType()</code> method.</p>
*/
public class UIInput extends UIOutput implements EditableValueHolder {
private static final String BEANS_VALIDATION_AVAILABLE =
"javax.faces.private.BEANS_VALIDATION_AVAILABLE";
// ------------------------------------------------------ Manifest Constants
/**
* <p>The standard component type for this component.</p>
*/
public static final String COMPONENT_TYPE = "javax.faces.Input";
/**
* <p>The standard component family for this component.</p>
*/
public static final String COMPONENT_FAMILY = "javax.faces.Input";
/**
* <p>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.</p>
*/
public static final String CONVERSION_MESSAGE_ID =
"javax.faces.component.UIInput.CONVERSION";
/**
* <p>The message identifier of the
* {@link javax.faces.application.FacesMessage} to be created if
* a required check fails.</p>
*/
public static final String REQUIRED_MESSAGE_ID =
"javax.faces.component.UIInput.REQUIRED";
/**
* <p>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.</p>
*/
public static final String UPDATE_MESSAGE_ID =
"javax.faces.component.UIInput.UPDATE";
/**
* <p class="changed_added_2_0">The name of a context 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.</p>
*/
public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME =
"javax.faces.VALIDATE_EMPTY_FIELDS";
/**
* <p class="changed_modified_2_3">The name of a context parameter
* that indicates how empty strings need to be interpreted.</p>
*/
public static final String EMPTY_STRING_AS_NULL_PARAM_NAME =
"javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
/**
* <p class="changed_modified_2_3">If this param is set, and calling
* toLowerCase().equals("true") on a
* String representation of its value returns true, validation
* must be performed, even when there is no corresponding value for this
* component in the incoming request. See {@link #validate}.</p>
*/
public static final String ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE =
"javax.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE";
private static final Validator[] EMPTY_VALIDATOR = new Validator[0];
private transient Boolean emptyStringIsNull;
private transient Boolean validateEmptyFields;
private transient Boolean isSetAlwaysValidateRequired;
enum PropertyKeys {
/**
* <p>The "localValueSet" state for this component.
*/
localValueSet,
/**
* <p>If the input is required or not.</p>
*/
required,
/**
* <p>Custom message to be displayed if input is required but non was submitted.</p>
*/
requiredMessage,
/**
* <p>Custom message to be displayed when conversion fails.</p>
*/
converterMessage,
/**
* <p>Custom message to be displayed when validation fails.</p>
*/
validatorMessage,
/**
* <p>Flag indicating whether or not this component is valid.</p>
*/
valid,
/**
* <p>Flag indicating when conversion/validation should occur.</p>
*/
immediate,
}
// ------------------------------------------------------------ Constructors
/**
* <p>Create a new {@link UIInput} instance with default property
* values.</p>
*/
public UIInput() {
super();
setRendererType("javax.faces.Text");
}
// -------------------------------------------------------------- Properties
@Override
public String getFamily() {
return (COMPONENT_FAMILY);
}
/**
* <p>The submittedValue value of this {@link UIInput} component.</p>
*/
private transient Object submittedValue = null;
/**
* <p>Return the submittedValue value of this {@link UIInput} component.
* This method should only be used by the <code>decode()</code> and
* <code>validate()</code> method of this component, or
* its corresponding {@link Renderer}.</p>
*/
@Override
public Object getSubmittedValue() {
if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) { // JAVASERVERFACES_SPEC_PUBLIC-671
return "";
} else {
return submittedValue;
}
}
/**
* <p>Set the submittedValue value of this {@link UIInput} component.
* This method should only be used by the <code>decode()</code> and
* <code>validate()</code> method of this component, or
* its corresponding {@link Renderer}.</p>
*
* @param submittedValue The new submitted value
*/
@Override
public void setSubmittedValue(Object submittedValue) {
this.submittedValue = submittedValue;
}
/**
* <p class="changed_added_2_2">If there is a local value, return it,
* otherwise return the result of calling {@code super.getVaue()}.</p>
*
* @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);
}
/**
* <p><span class="changed_modified_2_2">Convenience</span> method to reset
* this component's value to the
* un-initialized state. This method does the following:</p>
*
* <p class="changed_modified_2_2">Call {@link UIOutput#setValue}.</p>
*
* <p>Call {@link #setSubmittedValue} passing <code>null</code>.</p>
*
* <p>Clear state for property <code>localValueSet</code>.</p>
*
* <p>Clear state for property <code>valid</code>.</p>
*
* <p>Upon return from this call if the instance had a
* <code>ValueBinding</code> associated with it for the "value"
* property, this binding is evaluated when {@link
* UIOutput#getValue} is called. Otherwise, <code>null</code> is
* returned from <code>getValue()</code>.</p>
*/
@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 <code>setValue()</code> automatically reset
* this property to <code>true</code>.
*/
@Override
public boolean isLocalValueSet() {
return (Boolean) getStateHelper().eval(PropertyKeys.localValueSet, false);
}
/**
* Sets the "local value set" state for this component.
*/
@Override
public void setLocalValueSet(boolean localValueSet) {
getStateHelper().put(PropertyKeys.localValueSet, localValueSet);
}
/**
* <p>Return the "required field" state for this component.</p>
*/
@Override
public boolean isRequired() {
return (Boolean) getStateHelper().eval(PropertyKeys.required, false);
}
/**
* <p>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 <code>getValue()</code>
* must be wrapped in a {@link FacesException} and rethrown.
*
* @return the required message.
*/
public String getRequiredMessage() {
return (String) getStateHelper().eval(PropertyKeys.requiredMessage);
}
/**
* <p>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;</p>
*
* @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);
}
/**
* <p>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 <code>getValue()</code>
* must be wrapped in a {@link FacesException} and rethrown.
*
* @return the converter message.
*/
public String getConverterMessage() {
return (String) getStateHelper().eval(PropertyKeys.converterMessage);
}
/**
* <p>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;</p>
*
* @param message the literal message value to be displayed in the event
* conversion fails.
*/
public void setConverterMessage(String message) {
getStateHelper().put(PropertyKeys.converterMessage, message);
}
/**
* <p>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 <code>getValue()</code>
* must be wrapped in a {@link FacesException} and rethrown.
*
* @return the validator message.
*/
public String getValidatorMessage() {
return (String) getStateHelper().eval(PropertyKeys.validatorMessage);
}
/**
* <p>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;</p>
*
* @param message the literal message value to be displayed in the event
* validation fails.
*/
public void setValidatorMessage(String message) {
getStateHelper().put(PropertyKeys.validatorMessage, message);
}
@Override
public boolean isValid() {
return (Boolean) getStateHelper().eval(PropertyKeys.valid, true);
}
@Override
public void setValid(boolean valid) {
getStateHelper().put(PropertyKeys.valid, valid);
}
/**
* <p>Set the "required field" state for this component.</p>
*
* @param required The new "required field" state
*/
@Override
public void setRequired(boolean required) {
getStateHelper().put(PropertyKeys.required, required);
}
@Override
public boolean isImmediate() {
return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false);
}
@Override
public void setImmediate(boolean immediate) {
getStateHelper().put(PropertyKeys.immediate, immediate);
}
/**
* <p>Return a <code>MethodBinding</code> pointing at a
* method that will be called during <em>Process Validations</em>
* phase of the request processing lifecycle, to validate the current
* value of this component.</p>
*
* @deprecated {@link #getValidators} should be used instead.
*/
@Override
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;
}
/**
* <p>Set a <code>MethodBinding</code> pointing at a
* method that will be called during <em>Process Validations</em>
* phase of the request processing lifecycle, to validate the current
* value of this component.</p>
*
* <p>Any method referenced by such an expression must be public, with
* a return type of <code>void</code>, and accept parameters of type
* {@link FacesContext}, {@link UIComponent}, and <code>Object</code>.</p>
*
* @param validatorBinding The new <code>MethodBinding</code> instance
* @deprecated Use {@link #addValidator} instead, obtaining the
* argument {@link Validator} by creating an instance of {@link
* javax.faces.validator.MethodExpressionValidator}.
*/
@Override
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));
}
@Override
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}
*
* @param valueChangeListener the value change listener.
* @deprecated Use {@link #addValueChangeListener} instead, obtaining the
* argument {@link ValueChangeListener} by creating an instance of {@link
* javax.faces.event.MethodExpressionValueChangeListener}.
*/
@Override
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
/**
* <p>
* 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.
* </p>
*/
@Override
public void markInitialState() {
super.markInitialState();
if (validators != null) {
validators.markInitialState();
}
}
@Override
public void clearInitialState() {
if (initialStateMarked()) {
super.clearInitialState();
if (validators != null) {
validators.clearInitialState();
}
}
}
/**
* <p>Specialized decode behavior on top of that provided by the
* superclass. In addition to the standard
* <code>processDecodes</code> behavior inherited from {@link
* UIComponentBase}, calls <code>validate()</code> if the the
* <code>immediate</code> property is true; if the component is
* invalid afterwards or a <code>RuntimeException</code> is thrown,
* calls {@link FacesContext#renderResponse}. </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
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);
}
}
/**
* <p><span class="changed_modified_2_3">In</span> addition to the standard
* <code>processValidators</code> behavior
* inherited from {@link UIComponentBase}, calls <code>validate()</code>
* if the <code>immediate</code> property is false (which is the
* default); if the component is invalid afterwards, calls
* {@link FacesContext#renderResponse}.
* <span class="changed_added_2_3">To ensure the {@code PostValidateEvent}
* is published at the proper time, this component must be validated first,
* followed by the component's children and facets.</span>
* If a <code>RuntimeException</code> is thrown during
* validation processing, calls {@link FacesContext#renderResponse}
* and re-throw the exception.
* </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
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<UIComponent> i = getFacetsAndChildren(); i.hasNext(); ) {
i.next().processValidators(context);
}
popComponentFromEL(context);
}
/**
* <p>In addition to the standard <code>processUpdates</code> behavior
* inherited from {@link UIComponentBase}, calls
* <code>updateModel()</code>.
* If the component is invalid afterwards, calls
* {@link FacesContext#renderResponse}.
* If a <code>RuntimeException</code> is thrown during
* update processing, calls {@link FacesContext#renderResponse}
* and re-throw the exception.
* </p>
*
* @throws NullPointerException {@inheritDoc}
*/
@Override
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);
pushComponentToEL(context, this);
try {
updateModel(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
} finally {
popComponentFromEL(context);
}
if (!isValid()) {
context.renderResponse();
}
}
/**
* @throws NullPointerException {@inheritDoc}
*/
@Override
public void decode(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Force validity back to "true"
setValid(true);
super.decode(context);
}
/**
* <p><span class="changed_modified_2_0">Perform</span>
* the following algorithm to update the model data
* associated with this {@link UIInput}, if any, as appropriate.</p>
* <ul>
* <li>If the <code>valid</code> property of this component is
* <code>false</code>, take no further action.</li>
* <li>If the <code>localValueSet</code> property of this component is
* <code>false</code>, take no further action.</li>
* <li>If no {@link ValueExpression} for <code>value</code> exists,
* take no further action.</li>
* <li>Call <code>setValue()</code> method of the {@link ValueExpression}
* to update the value that the {@link ValueExpression} points at.</li>
* <li>If the <code>setValue()</code> method returns successfully:
* <ul>
* <li>Clear the local value of this {@link UIInput}.</li>
* <li>Set the <code>localValueSet</code> property of this
* {@link UIInput} to false.</li>
* </ul></li>
* <li>If the <code>setValue()</code> method throws an Exception:
* <ul>
* <li class="changed_modified_2_0">Enqueue an error message. Create a
* {@link FacesMessage} with the id {@link #UPDATE_MESSAGE_ID}. Create a
* {@link UpdateModelException}, passing the <code>FacesMessage</code> and
* the caught exception to the constructor. Create an
* {@link ExceptionQueuedEventContext}, passing the <code>FacesContext</code>,
* the <code>UpdateModelException</code>, this component instance, and
* {@link PhaseId#UPDATE_MODEL_VALUES} to its constructor. Call
* {@link FacesContext#getExceptionHandler} and then call
* {@link ExceptionHandler#processEvent}, passing the
* <code>ExceptionQueuedEventContext</code>.
* </li>
* <li>Set the <code>valid</code> property of this {@link UIInput}
* to <code>false</code>.</li>
* </ul>
* The exception must not be re-thrown. This enables tree traversal
* to continue for this lifecycle phase, as in all the other lifecycle
* phases.
* </li></ul>
*
* @param context {@link FacesContext} for the request we are processing
* @throws NullPointerException if <code>context</code>
* is <code>null</code>
*/
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
/**
* <p><span class="changed_modified_2_0
* changed_modified_2_2 changed_modified_2_3">Perform</span> the following algorithm to
* validate the local value of this {@link UIInput}.</p>
* <ul>
* <li>Retrieve the submitted value with {@link #getSubmittedValue}.
* If this returns <code>null</code>, <span class="changed_modified_2_3">and
* the value of the {@link #ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE}
* context-param is true (ignoring case), examine the value of the "required"
* property. If the value of "required" is true, continue as below. If
* the value of "required" is false or the required attribute is not set,
* exit without further processing. If the context-param is not set, or is
* set to false (ignoring case),</span> exit without further processing.
* (This indicates that no value was submitted for this
* component.)</li>
* <li><span class="changed_modified_2_0">If the
* <code>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</code>
* context parameter value is <code>true</code> (ignoring case), and
* <code>getSubmittedValue()</code> returns a zero-length
* <code>String</code> call <code>{@link #setSubmittedValue}</code>,
* passing <code>null</code> as the argument and continue processing
* using <code>null</code> as the current submitted
* value.</span></li>
* <li> Convert the submitted value into a "local value" of the
* appropriate data type by calling {@link #getConvertedValue}.</li>
* <li><span class="changed_added_2_0_rev_a">If conversion fails</span>:
* <ul>
* <li>Enqueue an appropriate error message by calling the
* <code>addMessage()</code> method on the
* <code>FacesContext</code>.</li>
* <li>Set the <code>valid</code> property
* on this component to <code>false</code> </li>
* </ul>
* </li>
* <li>Validate the property by calling {@link #validateValue}.</li>
*
* <li>If the <code>valid</code> property of this component is still
* <code>true</code>, retrieve the previous value of the component
* (with <code>getValue()</code>), store the new local value using
* <code>setValue()</code>, and reset the submitted value to null
* <span class="changed_added_2_2">with a call to {@link #setSubmittedValue}
* passing {@code null} as the argument</span>.
* If the local value is different from the previous value of this
* component, <span class="changed_modified_2_1">as determined by a
* call to {@link #compareValues}</span>, fire a {@link
* ValueChangeEvent} to be broadcast to all interested
* listeners.</li>
* </ul>
* <p>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
* <code>super.validate()</code> method to perform the standard
* processing described above.</p>
*
* @param context The {@link FacesContext} for the current request
* @throws NullPointerException if <code>context</code>
* 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) {
if (isRequired() && isSetAlwaysValidateRequired(context)) {
// continue as below
} else {
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(context, this, previous, newValue));
}
}
}
/*
* Respecting the fact that someone may have decorated FacesContextFactory
* and thus skipped our saving of this init param, look for the init
* param and return its value. The return is saved in a transient ivar
* to provide performance while not perturbing state saving.
*/
private boolean isSetAlwaysValidateRequired(FacesContext context) {
if (null != isSetAlwaysValidateRequired) {
return isSetAlwaysValidateRequired;
}
Boolean bool = (Boolean) context.getAttributes().get(ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE);
if (null != bool) {
isSetAlwaysValidateRequired = bool;
} else {
String val = context.getExternalContext().getInitParameter(ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE);
isSetAlwaysValidateRequired = Boolean.valueOf(val);
}
return isSetAlwaysValidateRequired;
}
/**
* <p>Convert the submitted value into a "local value" of the
* appropriate data type, if necessary. Employ the following
* algorithm to do so:</p>
* <ul>
* <li>If a <code>Renderer</code> is present, call
* <code>getConvertedValue()</code> to convert the submitted
* value.</li>
* <li>If no <code>Renderer</code> is present, and the submitted
* value is a String, locate a {@link Converter} as follows:
* <ul>
* <li>If <code>getConverter()</code> returns a non-null {@link Converter},
* use that instance.</li>
* <li>Otherwise, if a value binding for <code>value</code> exists,
* call <code>getType()</code> on it.
* <ul>
* <li>If this call returns <code>null</code>, assume the output
* type is <code>String</code> and perform no conversion.</li>
* <li>Otherwise, call
* <code>Application.createConverter(Class)</code>
* to locate any registered {@link Converter} capable of
* converting data values of the specified type.</li>
* </ul>
* </li>
* </ul>
* <li>If a {@link Converter} instance was located, call its
* <code>getAsObject()</code> method to perform the conversion.
* <span class="changed_modified_2_0_rev_a">If conversion fails, the
* <code>Converter</code> will have thrown
* a <code>ConverterException</code> which is declared as a checked exception
* on this method, and thus must be handled by the caller.</span></li>
* <li>Otherwise, use the submitted value without any conversion</li>
* </ul>
* <p>This method can be overridden by subclasses for more specific
* behavior.</p>
*
* @param context the Faces context.
* @param newSubmittedValue the new submitted value.
* @return the converted value.
*/
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;
}
/**
* <p><span class="changed_modified_2_0">Set</span> the "valid"
* property according to the below algorithm.</p>
* <ul>
* <li>
* <p>If the <code>valid</code> property on this component is
* still <code>true</code>, and the <code>required</code> property
* is also <code>true</code>, ensure that the local value is not
* empty (where "empty" is defined as <code>null</code> or a
* zero-length String). If the local value is empty:</p>
* <ul>
* <li><p>Enqueue an appropriate error message by calling the
* <code>addMessage()</code> method on the <code>FacesContext</code>
* instance for the current request. If the {@link
* #getRequiredMessage} returns non-<code>null</code>, use the value
* as the <code>summary</code> and <code>detail</code> in the {@link
* FacesMessage} that is enqueued on the <code>FacesContext</code>,
* otherwise use the message for the {@link #REQUIRED_MESSAGE_ID}.</li>
*
* <li>Set the <code>valid</code> property on this component
* to <code>false</code>.</li>
*
* <li><p class="changed_modified_2_0">If calling {@link
* ValidatorException#getFacesMessages} returns
* non-<code>null</code>, each message should be added to the
* <code>FacesContext</code>. Otherwise the single message returned
* from {@link ValidatorException#getFacesMessage} should be
* added.</p></li>
*
* </ul>
*
* </li>
*
* <li class="changed_added_2_0">
*
* <p>Otherwise, if the
* <code>valid</code> property on this component is still
* <code>true</code>, take the following action to determine if
* validation of this component should proceed.</p>
* <ul>
*
* <li><p>If the value is not empty, validation should proceed.</p></li>
* <li><p>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 <code>Map</code>
* from the <code>ExternalContext</code> 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
* the <code>ExternalContext</code>. If the value is
* <code>null</code> or equal to the string
* “<code>auto</code>” (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 “<code>true</code>” (without the
* quotes) validation should proceed. Otherwise, validation should
* not proceed.</p></li>
*
* </ul>
*
* <p>If the above determination indicates that validation should
* proceed, call the <code>validate()</code> method of each {@link
* Validator} registered for this {@link UIInput}, followed by the
* method pointed at by the <code>validatorBinding</code> 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
* <code>valid</code> property of this component to false.</li>
* </ul>
*
* @param context the Faces context.
* @param newValue the new value.
*/
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 or empty w/ validate empty fields enabled, call all validators
if (isValid() && (!isEmpty(newValue) || validateEmptyFields(context))) {
if (validators != null) {
Validator[] validators = this.validators.asArray(Validator.class);
for (Validator validator : 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 {
Collection<FacesMessage> messages = ve.getFacesMessages();
if (null != messages) {
message = null;
String cid = getClientId(context);
for (FacesMessage m : messages) {
context.addMessage(cid, m);
}
} else {
message = ve.getFacesMessage();
}
}
if (message != null) {
context.addMessage(getClientId(context), message);
}
}
}
}
}
}
/**
* <p>Return <code>true</code> if the new value is different from
* the previous value. First compare the two values by passing
* <em>value</em> to the <code>equals</code> method on argument
* <em>previous</em>. If that method returns <code>true</code>,
* return <code>true</code>. If that method returns
* <code>false</code>, and both arguments implement
* <code>java.lang.Comparable</code>, compare the two values by
* passing <em>value</em> to the <code>compareTo</code> method on
* argument <em>previous</em>. Return <code>true</code> if this
* method returns <code>0</code>, <code>false</code> otherwise.</p>
*
* @param previous old value of this component (if any)
* @param value new value of this component (if any)
* @return <code>true</code> if the new value is different from the
* previous value, <code>false</code> otherwise.
*/
protected boolean compareValues(Object previous, Object value) {
boolean result = true;
if (previous == null) {
result = (value != null);
} else if (value == null) {
result = true;
} else {
boolean previousEqualsValue = previous.equals(value);
if (!previousEqualsValue &&
previous instanceof Comparable &&
value instanceof Comparable) {
try {
result = !(0 == ((Comparable) previous).
compareTo((Comparable) value));
} catch (ClassCastException cce) {
// Comparable throws CCE if the types prevent a comparison
result = true;
}
} else {
result = !previousEqualsValue;
}
}
return result;
}
/**
* Executes validation logic.
*/
private void executeValidate(FacesContext context) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.validationFailed();
context.renderResponse();
}
}
/**
* <p class="changed_modified_2_3">
* Is the value denoting an empty value.
* </p>
*
* <p class="changed_modified_2_3">
* If the value is null, return true. If the value is a String and it is
* the empty string, return true. If the value is an array and the array
* length is 0, return true. If the value is a List and the List is empty,
* return true. If the value is a Collection and the Collection is empty,
* return true. If the value is a Map and the Map is empty, return true.
* In all other cases, return false.
* </p>
*
* @param value the value to check.
* @return true if it is, false otherwise.
*/
public 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);
}
} else if (value instanceof Collection) {
if (((Collection) value).isEmpty()) {
return (true);
}
} else if ((value instanceof Map) && (((Map) value).isEmpty())) {
return true;
}
return (false);
}
/**
* <p>The set of {@link Validator}s associated with this
* <code>UIComponent</code>.</p>
*/
AttachedObjectListHolder<Validator> validators;
/**
* <p>Add a {@link Validator} instance to the set associated with
* this {@link UIInput}.</p>
*
* @param validator The {@link Validator} to add
* @throws NullPointerException if <code>validator</code>
* is null
*/
@Override
public void addValidator(Validator validator) {
if (validator == null) {
throw new NullPointerException();
}
if (validators == null) {
validators = new AttachedObjectListHolder<>();
}
validators.add(validator);
}
/**
* <p>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.</p>
*/
@Override
public Validator[] getValidators() {
return ((validators != null) ? validators.asArray(Validator.class) : EMPTY_VALIDATOR);
}
/**
* <p>Remove a {@link Validator} instance from the set associated with
* this {@link UIInput}, if it was previously associated.
* Otherwise, do nothing.</p>
*
* @param validator The {@link Validator} to remove
*/
@Override
public void removeValidator(Validator validator) {
if (validator == null) {
return;
}
if (validators != null) {
validators.remove(validator);
}
}
// ------------------------------------------------ Event Processing Methods
/**
* <p>Add a new {@link ValueChangeListener} to the set of listeners
* interested in being notified when {@link ValueChangeEvent}s occur.</p>
*
* @param listener The {@link ValueChangeListener} to be added
* @throws NullPointerException if <code>listener</code>
* is <code>null</code>
*/
@Override
public void addValueChangeListener(ValueChangeListener listener) {
addFacesListener(listener);
}
/**
* <p>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.</p>
*/
@Override
public ValueChangeListener[] getValueChangeListeners() {
return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
}
/**
* <p>Remove an existing {@link ValueChangeListener} (if any) from the
* set of listeners interested in being notified when
* {@link ValueChangeEvent}s occur.</p>
*
* @param listener The {@link ValueChangeListener} to be removed
* @throws NullPointerException if <code>listener</code>
* is <code>null</code>
*/
@Override
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<>();
}
validators.restoreState(context, values[1]);
}
}
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) {
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);
}
private boolean considerEmptyStringNull(FacesContext ctx) {
if (emptyStringIsNull == null) {
String val = ctx.getExternalContext().getInitParameter(EMPTY_STRING_AS_NULL_PARAM_NAME);
emptyStringIsNull = Boolean.valueOf(val);
}
return emptyStringIsNull;
}
private boolean validateEmptyFields(FacesContext ctx) {
if (validateEmptyFields == null) {
ExternalContext extCtx = ctx.getExternalContext();
String val = extCtx.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
if (null == val) {
val = (String) extCtx.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
}
if (val == null || "auto".equals(val)) {
validateEmptyFields = isBeansValidationAvailable(ctx);
} else {
validateEmptyFields = Boolean.valueOf(val);
}
}
return validateEmptyFields;
}
private boolean isBeansValidationAvailable(FacesContext context) {
boolean result = false;
Map<String,Object> appMap = context.getExternalContext().getApplicationMap();
if (appMap.containsKey(BEANS_VALIDATION_AVAILABLE)) {
result = (Boolean) appMap.get(BEANS_VALIDATION_AVAILABLE);
} else {
try {
new BeanValidator();
appMap.put(BEANS_VALIDATION_AVAILABLE, result = true);
} catch (Throwable t) {
appMap.put(BEANS_VALIDATION_AVAILABLE, Boolean.FALSE);
}
}
return result;
}
}