All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.UIInput Maven / Gradle / Ivy

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.0
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

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 { 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 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.

*/ public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS"; /** *

The name of a context parameter * that indicates how empty strings need to be interpreted.

*/ public static final String EMPTY_STRING_AS_NULL_PARAM_NAME = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL"; /** *

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}.

*/ 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 { /** *

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 @Override 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}.

*/ @Override public Object getSubmittedValue() { if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) { // JAVASERVERFACES_SPEC_PUBLIC-671 return ""; } else { return 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 */ @Override 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. */ @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); } /** *

Return the "required field" state for this component.

*/ @Override 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. * * @return the required message. */ 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. * * @return the converter message. */ 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. * * @return the validator message. */ 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); } @Override public boolean isValid() { return (Boolean) getStateHelper().eval(PropertyKeys.valid, true); } @Override 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 */ @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); } /** *

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. */ @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; } /** *

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}. */ @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 /** *

* 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} */ @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); } } /** *

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}. * 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. * If a RuntimeException is thrown during * validation processing, calls {@link FacesContext#renderResponse} * and re-throw the exception. *

* * @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 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} */ @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); } /** *

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, 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), 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.
  • *
  • Convert the submitted value into a "local value" of the * appropriate data type by calling {@link #getConvertedValue}.
  • *
  • 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
    • *
    *
  • *
  • 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 * 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.

* * @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". 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); if (submittedValue == null) { setSubmittedValue(""); } } 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)); } } else { if (submittedValue == null) { setSubmittedValue(""); } } } /* * 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; } /** *

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, the * Converter will have thrown * a ConverterException 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.

* * @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; } /** *

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 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 the ExternalContext 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 ExternalContext. 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 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.

  • *
* * @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 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); } } } } } } /** *

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.

* * @param previous old value of this component (if any) * @param value new value of this component (if any) * @return true if the new value is different from the * previous value, false 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(); } } /** *

* Is the value denoting an empty value. *

* *

* 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. *

* * @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); } /** *

The set of {@link Validator}s associated with this * UIComponent.

*/ AttachedObjectListHolder validators; /** *

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 */ @Override public void addValidator(Validator validator) { if (validator == null) { throw new NullPointerException(); } if (validators == null) { validators = new AttachedObjectListHolder<>(); } 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.

*/ @Override 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 */ @Override 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 if listener * is null */ @Override 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.

*/ @Override 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 */ @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 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy