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

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

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

There is a newer version: 2.2.20
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.faces.component;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import java.util.Map;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.MethodBinding;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.PhaseId;
import javax.faces.event.PostValidateEvent;
import javax.faces.event.PreValidateEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
import javax.faces.render.Renderer;
import javax.faces.validator.BeanValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

/**
 * 

UIInput is a {@link * UIComponent} that represents a component that both displays output to * the user (like {@link UIOutput} components do) and processes request * parameters on the subsequent request that need to be decoded. There * are no restrictions on the data type of the local value, or the * object referenced by the value binding expression (if any); however, * individual {@link javax.faces.render.Renderer}s will generally impose * restrictions on the type of data they know how to display.

* *

During the Apply Request Values phase * of the request processing lifecycle, the decoded value of this * component, usually but not necessarily a String, must be stored - but * not yet converted - using setSubmittedValue(). If the * component wishes to indicate that no particular value was submitted, * it can either do nothing, or set the submitted value to * null.

*

By default, during the Process Validators phase of the * request processing lifecycle, the submitted value will be converted * to a typesafe object, and, if validation succeeds, stored as a local * value using setValue(). However, if the * immediate property is set to true, this * processing will occur instead at the end of the Apply Request * Values phase.

*

During the Render Response phase of the request * processing lifecycle, conversion for output occurs as for {@link * UIOutput}.

*

When the validate() method of this {@link UIInput} * detects that a value change has actually occurred, and that all * validations have been successfully passed, it will queue a {@link * ValueChangeEvent}. Later on, the broadcast() method * will ensure that this event is broadcast to all interested listeners. * This event will be delivered by default in the Process * Validators phase, but can be delivered instead during Apply * Request Values if the immediate property is set to * true. If the validation * fails, the implementation must call {@link * FacesContext#validationFailed}.

*

By default, the rendererType property must be set to * "Text". This value can be changed by calling the * setRendererType() method.

*/ public class UIInput extends UIOutput implements EditableValueHolder { /* PENDING_2_1 (edburns,rogerk) this should be exposed as public constant */ private static final String EMPTY_STRING_AS_NULL = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL"; private static final String BEANS_VALIDATION_AVAILABLE = "javax.faces.private.BEANS_VALIDATION_AVAILABLE"; // ------------------------------------------------------ Manifest Constants /** *

The standard component type for this component.

*/ public static final String COMPONENT_TYPE = "javax.faces.Input"; /** *

The standard component family for this component.

*/ public static final String COMPONENT_FAMILY = "javax.faces.Input"; /** *

The message identifier of the * {@link javax.faces.application.FacesMessage} to be created if * a conversion error occurs, and neither the page author nor * the {@link ConverterException} provides a message.

*/ public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION"; /** *

The message identifier of the * {@link javax.faces.application.FacesMessage} to be created if * a required check fails.

*/ public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED"; /** *

The message identifier of the * {@link javax.faces.application.FacesMessage} to be created if * a model update error occurs, and the thrown exception has * no message.

*/ public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE"; /** *

The name of an application parameter * that indicates how empty values should be handled with respect to * validation. See {@link #validateValue} for the allowable values * and specification of how they should be interpreted.

*/ public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS"; private static final Validator[] EMPTY_VALIDATOR = new Validator[0]; private transient Boolean emptyStringIsNull; private transient Boolean validateEmptyFields; enum PropertyKeys { /** *

The "localValueSet" state for this component. */ localValueSet, /** *

If the input is required or not.

*/ required, /** *

Custom message to be displayed if input is required but non was submitted.

*/ requiredMessage, /** *

Custom message to be displayed when conversion fails.

*/ converterMessage, /** *

Custom message to be displayed when validation fails.

*/ validatorMessage, /** *

Flag indicating whether or not this component is valid.

*/ valid, /** *

Flag indicating when conversion/validation should occur.

*/ immediate, } // ------------------------------------------------------------ Constructors /** *

Create a new {@link UIInput} instance with default property * values.

*/ public UIInput() { super(); setRendererType("javax.faces.Text"); } // -------------------------------------------------------------- Properties public String getFamily() { return (COMPONENT_FAMILY); } /** *

The submittedValue value of this {@link UIInput} component.

*/ private transient Object submittedValue = null; /** *

Return the submittedValue value of this {@link UIInput} component. * This method should only be used by the decode() and * validate() method of this component, or * its corresponding {@link Renderer}.

*/ public Object getSubmittedValue() { return (this.submittedValue); } /** *

Set the submittedValue value of this {@link UIInput} component. * This method should only be used by the decode() and * validate() method of this component, or * its corresponding {@link Renderer}.

* * @param submittedValue The new submitted value */ public void setSubmittedValue(Object submittedValue) { this.submittedValue = submittedValue; } /** *

If there is a local value, return it, * otherwise return the result of calling {@code super.getVaue()}.

* * @since 2.2 */ @Override public Object getValue() { return isLocalValueSet() ? getLocalValue() : super.getValue(); } @Override public void setValue(Object value) { super.setValue(value); // Mark the local value as set. setLocalValueSet(true); } /** *

Convenience method to reset * this component's value to the * un-initialized state. This method does the following:

*

*

Call {@link UIOutput#setValue}.

*

*

Call {@link #setSubmittedValue} passing null.

*

*

Clear state for property localValueSet.

*

*

Clear state for property valid.

*

*

Upon return from this call if the instance had a * ValueBinding associated with it for the "value" * property, this binding is evaluated when {@link * UIOutput#getValue} is called. Otherwise, null is * returned from getValue().

*/ @Override public void resetValue() { super.resetValue(); this.setSubmittedValue(null); getStateHelper().remove(PropertyKeys.localValueSet); getStateHelper().remove(PropertyKeys.valid); } /** * Return the "local value set" state for this component. * Calls to setValue() automatically reset * this property to true. */ public boolean isLocalValueSet() { return (Boolean) getStateHelper().eval(PropertyKeys.localValueSet, false); } /** * Sets the "local value set" state for this component. */ public void setLocalValueSet(boolean localValueSet) { getStateHelper().put(PropertyKeys.localValueSet, localValueSet); } /** *

Return the "required field" state for this component.

*/ public boolean isRequired() { return (Boolean) getStateHelper().eval(PropertyKeys.required, false); } /** *

If there has been a call to {@link #setRequiredMessage} on this * instance, return the message. Otherwise, call {@link #getValueExpression} * passing the key "requiredMessage", get the result of the expression, and return it. * Any {@link ELException}s thrown during the call to getValue() * must be wrapped in a {@link FacesException} and rethrown. */ public String getRequiredMessage() { return (String) getStateHelper().eval(PropertyKeys.requiredMessage); } /** *

Override any {@link ValueExpression} set for the "requiredMessage" * with the literal argument provided to this method. Subsequent calls * to {@link #getRequiredMessage} will return this value;

* * @param message the literal message value to be displayed in the event * the user hasn't supplied a value and one is required. */ public void setRequiredMessage(String message) { getStateHelper().put(PropertyKeys.requiredMessage, message); } /** *

If there has been a call to {@link #setConverterMessage} on this * instance, return the message. Otherwise, call {@link #getValueExpression} * passing the key "converterMessage", get the result of the expression, and return it. * Any {@link ELException}s thrown during the call to getValue() * must be wrapped in a {@link FacesException} and rethrown. */ public String getConverterMessage() { return (String) getStateHelper().eval(PropertyKeys.converterMessage); } /** *

Override any {@link ValueExpression} set for the "converterMessage" * with the literal argument provided to this method. Subsequent calls * to {@link #getConverterMessage} will return this value;

* * @param message the literal message value to be displayed in the event * conversion fails. */ public void setConverterMessage(String message) { getStateHelper().put(PropertyKeys.converterMessage, message); } /** *

If there has been a call to {@link #setValidatorMessage} on this * instance, return the message. Otherwise, call {@link #getValueExpression} * passing the key "validatorMessage", get the result of the expression, and return it. * Any {@link ELException}s thrown during the call to getValue() * must be wrapped in a {@link FacesException} and rethrown. */ public String getValidatorMessage() { return (String) getStateHelper().eval(PropertyKeys.validatorMessage); } /** *

Override any {@link ValueExpression} set for the "validatorMessage" * with the literal argument provided to this method. Subsequent calls * to {@link #getValidatorMessage} will return this value;

* * @param message the literal message value to be displayed in the event * validation fails. */ public void setValidatorMessage(String message) { getStateHelper().put(PropertyKeys.validatorMessage, message); } public boolean isValid() { return (Boolean) getStateHelper().eval(PropertyKeys.valid, true); } public void setValid(boolean valid) { getStateHelper().put(PropertyKeys.valid, valid); } /** *

Set the "required field" state for this component.

* * @param required The new "required field" state */ public void setRequired(boolean required) { getStateHelper().put(PropertyKeys.required, required); } public boolean isImmediate() { return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false); } public void setImmediate(boolean immediate) { getStateHelper().put(PropertyKeys.immediate, immediate); } /** *

Return a MethodBinding pointing at a * method that will be called during Process Validations * phase of the request processing lifecycle, to validate the current * value of this component.

* * @deprecated {@link #getValidators} should be used instead. */ public MethodBinding getValidator() { MethodBinding result = null; Validator[] curValidators = getValidators(); // go through our lisetners list and find the one and only // MethodBindingValidator instance, if present. if (null != curValidators) { for (int i = 0; i < curValidators.length; i++) { // We are guaranteed to have at most one instance of // MethodBindingValidator in the curValidators list. if (MethodBindingValidator.class == curValidators[i].getClass()) { result = ((MethodBindingValidator) curValidators[i]). getWrapped(); break; } } } return result; } /** *

Set a MethodBinding pointing at a * method that will be called during Process Validations * phase of the request processing lifecycle, to validate the current * value of this component.

*

*

Any method referenced by such an expression must be public, with * a return type of void, and accept parameters of type * {@link FacesContext}, {@link UIComponent}, and Object.

* * @param validatorBinding The new MethodBinding instance * @deprecated Use {@link #addValidator} instead, obtaining the * argument {@link Validator} by creating an instance of {@link * javax.faces.validator.MethodExpressionValidator}. */ public void setValidator(MethodBinding validatorBinding) { Validator[] curValidators = getValidators(); // see if we need to null-out, or replace an existing validator if (null != curValidators) { for (int i = 0; i < curValidators.length; i++) { // if we want to remove the validatorBinding if (null == validatorBinding) { // We are guaranteed to have at most one instance of // MethodBindingValidator in the curValidators // list. if (MethodBindingValidator.class == curValidators[i].getClass()) { removeValidator(curValidators[i]); return; } } // if we want to replace the validatorBinding else //noinspection ObjectEquality if (validatorBinding == curValidators[i]) { removeValidator(curValidators[i]); break; } } } addValidator(new MethodBindingValidator(validatorBinding)); } public MethodBinding getValueChangeListener() { MethodBinding result = null; ValueChangeListener[] curListeners = getValueChangeListeners(); // go through our lisetners list and find the one and only // MethodBindingValueChangeListener instance, if present. if (null != curListeners) { for (int i = 0; i < curListeners.length; i++) { // We are guaranteed to have at most one instance of // MethodBindingValueChangeListener in the curListeners list. if (MethodBindingValueChangeListener.class == curListeners[i].getClass()) { result = ((MethodBindingValueChangeListener) curListeners[i]). getWrapped(); break; } } } return result; } /** * {@inheritDoc} * * @deprecated Use {@link #addValueChangeListener} instead, obtaining the * argument {@link ValueChangeListener} by creating an instance of {@link * javax.faces.event.MethodExpressionValueChangeListener}. */ public void setValueChangeListener(MethodBinding valueChangeListener) { ValueChangeListener[] curListeners = getValueChangeListeners(); // see if we need to null-out, or replace an existing listener if (null != curListeners) { for (int i = 0; i < curListeners.length; i++) { // if we want to remove the valueChangeListener if (null == valueChangeListener) { // We are guaranteed to have at most one instance of // MethodBindingValueChangeListener in the curListeners // list. if (MethodBindingValueChangeListener.class == curListeners[i].getClass()) { removeFacesListener(curListeners[i]); return; } } // if we want to replace the valueChangeListener else //noinspection ObjectEquality if (valueChangeListener == curListeners[i]) { removeFacesListener(curListeners[i]); break; } } } addValueChangeListener(new MethodBindingValueChangeListener(valueChangeListener)); } // ----------------------------------------------------- UIComponent Methods /** *

* In addition to the actions taken in {@link UIOutput} * when {@link PartialStateHolder#markInitialState()} is called, * check if any of the installed {@link Validator}s are PartialStateHolders and * if so, call {@link javax.faces.component.PartialStateHolder#markInitialState()} * as appropriate. *

*/ @Override public void markInitialState() { super.markInitialState(); if (validators != null) { validators.markInitialState(); } } @Override public void clearInitialState() { if (initialStateMarked()) { super.clearInitialState(); if (validators != null) { validators.clearInitialState(); } } } /** *

Specialized decode behavior on top of that provided by the * superclass. In addition to the standard * processDecodes behavior inherited from {@link * UIComponentBase}, calls validate() if the the * immediate property is true; if the component is * invalid afterwards or a RuntimeException is thrown, * calls {@link FacesContext#renderResponse}.

* * @throws NullPointerException {@inheritDoc} */ public void processDecodes(FacesContext context) { if (context == null) { throw new NullPointerException(); } // Skip processing if our rendered flag is false if (!isRendered()) { return; } super.processDecodes(context); if (isImmediate()) { executeValidate(context); } } /** *

In addition to the standard processValidators behavior * inherited from {@link UIComponentBase}, calls validate() * if the immediate property is false (which is the * default); if the component is invalid afterwards, calls * {@link FacesContext#renderResponse}. * If a RuntimeException is thrown during * validation processing, calls {@link FacesContext#renderResponse} * and re-throw the exception. *

* * @throws NullPointerException {@inheritDoc} */ public void processValidators(FacesContext context) { if (context == null) { throw new NullPointerException(); } // Skip processing if our rendered flag is false if (!isRendered()) { return; } pushComponentToEL(context, this); if (!isImmediate()) { Application application = context.getApplication(); application.publishEvent(context, PreValidateEvent.class, this); executeValidate(context); application.publishEvent(context, PostValidateEvent.class, this); } for (Iterator i = getFacetsAndChildren(); i.hasNext(); ) { i.next().processValidators(context); } popComponentFromEL(context); } /** *

In addition to the standard processUpdates behavior * inherited from {@link UIComponentBase}, calls * updateModel(). * If the component is invalid afterwards, calls * {@link FacesContext#renderResponse}. * If a RuntimeException is thrown during * update processing, calls {@link FacesContext#renderResponse} * and re-throw the exception. *

* * @throws NullPointerException {@inheritDoc} */ public void processUpdates(FacesContext context) { if (context == null) { throw new NullPointerException(); } // Skip processing if our rendered flag is false if (!isRendered()) { return; } super.processUpdates(context); try { updateModel(context); } catch (RuntimeException e) { context.renderResponse(); throw e; } if (!isValid()) { context.renderResponse(); } } /** * @throws NullPointerException {@inheritDoc} */ public void decode(FacesContext context) { if (context == null) { throw new NullPointerException(); } // Force validity back to "true" setValid(true); super.decode(context); } /** *

Perform * the following algorithm to update the model data * associated with this {@link UIInput}, if any, as appropriate.

*
    *
  • If the valid property of this component is * false, take no further action.
  • *
  • If the localValueSet property of this component is * false, take no further action.
  • *
  • If no {@link ValueExpression} for value exists, * take no further action.
  • *
  • Call setValue() method of the {@link ValueExpression} * to update the value that the {@link ValueExpression} points at.
  • *
  • If the setValue() method returns successfully: *
      *
    • Clear the local value of this {@link UIInput}.
    • *
    • Set the localValueSet property of this * {@link UIInput} to false.
    • *
  • *
  • If the setValue() method throws an Exception: *
      *
    • Enqueue an error message. Create a * {@link FacesMessage} with the id {@link #UPDATE_MESSAGE_ID}. Create a * {@link UpdateModelException}, passing the FacesMessage and * the caught exception to the constructor. Create an * {@link ExceptionQueuedEventContext}, passing the FacesContext, * the UpdateModelException, this component instance, and * {@link PhaseId#UPDATE_MODEL_VALUES} to its constructor. Call * {@link FacesContext#getExceptionHandler} and then call * {@link ExceptionHandler#processEvent}, passing the * ExceptionQueuedEventContext. *
    • *
    • Set the valid property of this {@link UIInput} * to false.
    • *
  • * The exception must not be re-thrown. This enables tree traversal * to continue for this lifecycle phase, as in all the other lifecycle * phases. *
* * @param context {@link FacesContext} for the request we are processing * @throws NullPointerException if context * is null */ public void updateModel(FacesContext context) { if (context == null) { throw new NullPointerException(); } if (!isValid() || !isLocalValueSet()) { return; } ValueExpression ve = getValueExpression("value"); if (ve != null) { Throwable caught = null; FacesMessage message = null; try { ve.setValue(context.getELContext(), getLocalValue()); resetValue(); } catch (ELException e) { caught = e; String messageStr = e.getMessage(); Throwable result = e.getCause(); while (null != result && result.getClass().isAssignableFrom(ELException.class)) { messageStr = result.getMessage(); result = result.getCause(); } if (null == messageStr) { message = MessageFactory.getMessage(context, UPDATE_MESSAGE_ID, MessageFactory.getLabel( context, this)); } else { message = new FacesMessage(FacesMessage.SEVERITY_ERROR, messageStr, messageStr); } setValid(false); } catch (Exception e) { caught = e; message = MessageFactory.getMessage(context, UPDATE_MESSAGE_ID, MessageFactory.getLabel( context, this)); setValid(false); } if (caught != null) { assert(message != null); // PENDING(edburns): verify this is in the spec. @SuppressWarnings({"ThrowableInstanceNeverThrown"}) UpdateModelException toQueue = new UpdateModelException(message, caught); ExceptionQueuedEventContext eventContext = new ExceptionQueuedEventContext(context, toQueue, this, PhaseId.UPDATE_MODEL_VALUES); context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, eventContext); } } } // ------------------------------------------------------ Validation Methods /** *

Perform the following algorithm to * validate the local value of this {@link UIInput}.

*
    *
  • Retrieve the submitted value with {@link #getSubmittedValue}. * If this returns null, exit without further * processing. (This indicates that no value was submitted for this * component.)
  • *

    *

  • If the * javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL * context parameter value is true (ignoring case), and * getSubmittedValue() returns a zero-length * String call {@link #setSubmittedValue}, * passing null as the argument and continue processing * using null as the current submitted * value.
  • *

    *

  • 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) { 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(this, previous, newValue)); } } else { if (submittedValue == null) { setSubmittedValue(""); } } } /** *

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.

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

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

    */ public Validator[] getValidators() { return ((validators != null) ? validators.asArray(Validator.class) : EMPTY_VALIDATOR); } /** *

    Remove a {@link Validator} instance from the set associated with * this {@link UIInput}, if it was previously associated. * Otherwise, do nothing.

    * * @param validator The {@link Validator} to remove */ public void removeValidator(Validator validator) { if (validator == null) { return; } if (validators != null) { validators.remove(validator); } } // ------------------------------------------------ Event Processing Methods /** *

    Add a new {@link ValueChangeListener} to the set of listeners * interested in being notified when {@link ValueChangeEvent}s occur.

    * * @param listener The {@link ValueChangeListener} to be added * @throws NullPointerException if listener * is null */ public void addValueChangeListener(ValueChangeListener listener) { addFacesListener(listener); } /** *

    Return the set of registered {@link ValueChangeListener}s for this * {@link UIInput} instance. If there are no registered listeners, * a zero-length array is returned.

    */ public ValueChangeListener[] getValueChangeListeners() { return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class); } /** *

    Remove an existing {@link ValueChangeListener} (if any) from the * set of listeners interested in being notified when * {@link ValueChangeEvent}s occur.

    * * @param listener The {@link ValueChangeListener} to be removed * @throws NullPointerException if listener * is null */ public void removeValueChangeListener(ValueChangeListener listener) { removeFacesListener(listener); } // ----------------------------------------------------- StateHolder Methods @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); 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