View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.component;
20  
21  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
22  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFListener;
23  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
24  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
25  
26  import javax.el.ValueExpression;
27  import javax.faces.application.FacesMessage;
28  import javax.faces.application.ProjectStage;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.convert.Converter;
32  import javax.faces.convert.ConverterException;
33  import javax.faces.el.EvaluationException;
34  import javax.faces.el.MethodBinding;
35  import javax.faces.event.AbortProcessingException;
36  import javax.faces.event.ExceptionQueuedEvent;
37  import javax.faces.event.ExceptionQueuedEventContext;
38  import javax.faces.event.FacesEvent;
39  import javax.faces.event.PhaseId;
40  import javax.faces.event.PostValidateEvent;
41  import javax.faces.event.PreValidateEvent;
42  import javax.faces.event.ValueChangeEvent;
43  import javax.faces.event.ValueChangeListener;
44  import javax.faces.render.Renderer;
45  import javax.faces.validator.Validator;
46  import javax.faces.webapp.FacesServlet;
47  import java.util.ArrayList;
48  import java.util.Arrays;
49  import java.util.Collection;
50  import java.util.HashMap;
51  import java.util.LinkedList;
52  import java.util.List;
53  import java.util.Map;
54  
55  /**
56   * UICommand is a base abstraction for components that implement ActionSource.
57   * <p>
58   * See the javadoc for this class in the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF
59   * Specification</a> for further details.
60   * <p>
61   */
62  @JSFComponent(defaultRendererType = "javax.faces.Text")
63  public class UIInput extends UIOutput implements EditableValueHolder
64  {
65      public static final String COMPONENT_TYPE = "javax.faces.Input";
66      public static final String COMPONENT_FAMILY = "javax.faces.Input";
67  
68      public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION";
69      public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED";
70      public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";
71  
72      /**
73       * Force validation on empty fields (By default is auto, which means it is only 
74       * enabled when Bean Validation binaries are available on the current classpath).
75       */
76      @JSFWebConfigParam(defaultValue="auto", expectedValues="auto, true, false", since="2.0", group="validation")
77      public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS";
78      
79      /** 
80       * Submitted values are decoded as null values instead empty strings.
81       * 
82       * <p>Note this param is ignored for components extending from UISelectOne/UISelectMany.</p>
83       **/
84      @JSFWebConfigParam(defaultValue="false", expectedValues="true, false", since="2.0", group="validation")
85      public static final String EMPTY_STRING_AS_NULL_PARAM_NAME
86              = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
87  
88      /** 
89       * When CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY is enabled, input fields will be cleared
90       * when null or empty values are submitted. When disabled, and INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL 
91       * is enabled, submitting null or empty values will cause the previous model value to be restored 
92       * to the input field.
93       **/
94      @JSFWebConfigParam(defaultValue="true", expectedValues="true, false", since="2.3.0", group="validation")
95      private static final String CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY_PARAM_NAME
96              = "org.apache.myfaces.CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY";
97  
98      /**
99       * If set to true, validation is always performed when required is true.
100      */
101     @JSFWebConfigParam(defaultValue="false", expectedValues="true, false", since="2.3", group="validation")
102     public static final String ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE 
103             = "javax.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE";
104     
105     // our own, cached key
106     private static final String MYFACES_EMPTY_VALUES_AS_NULL_PARAM_NAME =
107       "org.apache.myfaces.UIInput.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
108     
109     /**
110      * Extended debug info is stored under this key in the request
111      * map for every UIInput component when in Development mode.
112      * ATTENTION: this constant is duplicate in org.apache.myfaces.renderkit.ErrorPageWriter
113      */
114     private static final String DEBUG_INFO_KEY = "org.apache.myfaces.debug.DEBUG_INFO";
115     
116     private final static String BEAN_BEFORE_JSF_PROPERTY = "oam.beanBeforeJsf";
117 
118     private static final Validator[] EMPTY_VALIDATOR_ARRAY = new Validator[0];
119     
120     private _DeltaList<Validator> _validatorList;
121 
122     /**
123      * Construct an instance of the UIInput.
124      */
125     public UIInput()
126     {
127         setRendererType("javax.faces.Text");
128     }
129 
130     @Override
131     public String getFamily()
132     {
133         return COMPONENT_FAMILY;
134     }
135 
136     /**
137      * Store the specified object as the "local value" of this component. The value-binding named "value" (if any) is
138      * ignored; the object is only stored locally on this component. During the "update model" phase, if there is a
139      * value-binding named "value" then this local value will be stored via that value-binding and the "local value"
140      * reset to null.
141      */
142     @Override
143     public void setValue(Object value)
144     {
145         FacesContext facesContext = getFacesContext();
146         if (facesContext != null && facesContext.isProjectStage(ProjectStage.Development))
147         {
148             // extended debug-info when in Development mode
149             _createFieldDebugInfo(facesContext, "localValue",
150                     getLocalValue(), value, 1);
151         }
152         setLocalValueSet(true);
153         super.setValue(value);
154     }
155     
156     /**
157      * Return the current value of this component.
158      * <p>
159      * If a submitted value has been converted but not yet pushed into the
160      * model, then return that locally-cached value (see isLocalValueSet).
161      * <p>
162      * Otherwise, evaluate an EL expression to fetch a value from the model. 
163      */
164     public Object getValue()
165     {
166         if (isLocalValueSet())
167         {
168             return super.getLocalValue();
169         }
170         return super.getValue();
171     }
172 
173     /**
174      * Set the "submitted value" of this component from the relevant data in the current servlet request object.
175      * <p>
176      * If this component is not rendered, then do nothing; no output would have been sent to the client so no input is
177      * expected.
178      * <p>
179      * Invoke the inherited functionality, which typically invokes the renderer associated with this component to
180      * extract and set this component's "submitted value".
181      * <p>
182      * If this component is marked "immediate", then immediately apply validation to the submitted value found. On
183      * error, call context method "renderResponse" which will force processing to leap to the "render
184      * response" phase as soon as the "decode" step has completed for all other components.
185      */
186     @Override
187     public void processDecodes(FacesContext context)
188     {
189         if (context == null)
190         {
191             throw new NullPointerException("context");
192         }
193         try
194         {
195             setCachedFacesContext(context);
196             pushComponentToEL(context, this);
197             if (!isRendered())
198             {
199                 return;
200             }
201         }
202         finally
203         {
204             setCachedFacesContext(null);
205             popComponentFromEL(context);
206         }
207         super.processDecodes(context);
208         try
209         {
210             setCachedFacesContext(context);
211             pushComponentToEL(context, this);
212             if (isImmediate())
213             {
214                 //Pre validation event dispatch for component
215                 context.getApplication().publishEvent(context,  PreValidateEvent.class, getClass(), this);
216                 try
217                 {
218                     validate(context);
219                 }
220                 catch (RuntimeException e)
221                 {
222                     context.renderResponse();
223                     throw e;
224                 }
225                 finally
226                 {
227                     context.getApplication().publishEvent(context,  PostValidateEvent.class, getClass(), this);
228                 }
229                 if (!isValid())
230                 {
231                     context.renderResponse();
232                 }
233             }
234         }
235         finally
236         {
237             setCachedFacesContext(null);
238             popComponentFromEL(context);
239         }
240     }
241 
242     @Override
243     public void processValidators(FacesContext context)
244     {
245         if (context == null)
246         {
247             throw new NullPointerException("context");
248         }
249         try
250         {
251             setCachedFacesContext(context);
252             pushComponentToEL(context, this);
253             if (!isRendered())
254             {
255                 return;
256             }
257         }
258         finally
259         {
260             setCachedFacesContext(null);
261             popComponentFromEL(context);
262         }
263 
264         //super.processValidators(context);
265         
266         // Call the processValidators() method of all facets and children of this UIComponent, in the order
267         // determined by a call to getFacetsAndChildren().
268         int facetCount = getFacetCount();
269         if (facetCount > 0)
270         {
271             for (UIComponent facet : getFacets().values())
272             {
273                 facet.processValidators(context);
274             }
275         }
276 
277         for (int i = 0, childCount = getChildCount(); i < childCount; i++)
278         {
279             UIComponent child = getChildren().get(i);
280             child.processValidators(context);
281         }
282 
283         try
284         {
285             setCachedFacesContext(context);
286             pushComponentToEL(context, this);
287             if (!isImmediate())
288             {
289                 //Pre validation event dispatch for component
290                 context.getApplication().publishEvent(context,  PreValidateEvent.class, getClass(), this);
291                 try
292                 {
293                     validate(context);
294                 }
295                 catch (RuntimeException e)
296                 {
297                     context.renderResponse();
298                     throw e;
299                 }
300                 finally
301                 {
302                     context.getApplication().publishEvent(context,  PostValidateEvent.class, getClass(), this);
303                 }
304                 if (!isValid())
305                 {
306                     context.validationFailed();
307                     context.renderResponse();
308                 }
309             }
310         }
311         finally
312         {
313             setCachedFacesContext(null);
314             popComponentFromEL(context);
315         }
316     }
317 
318     @Override
319     public void processUpdates(FacesContext context)
320     {
321         if (context == null)
322         {
323             throw new NullPointerException("context");
324         }
325         try
326         {
327             setCachedFacesContext(context);
328             pushComponentToEL(context, this);
329             if (!isRendered())
330             {
331                 return;
332             }
333         }
334         finally
335         {
336             setCachedFacesContext(null);
337             popComponentFromEL(context);
338         }
339         super.processUpdates(context);
340 
341         try
342         {
343             setCachedFacesContext(context);
344             pushComponentToEL(context, this);
345             try
346             {
347                 updateModel(context);
348             }
349             catch (RuntimeException e)
350             {
351                 context.renderResponse();
352                 throw e;
353             }
354             if (!isValid())
355             {
356                 context.renderResponse();
357             }
358         }
359         finally
360         {
361             setCachedFacesContext(null);
362             popComponentFromEL(context);
363         }
364     }
365 
366     @Override
367     public void decode(FacesContext context)
368     {
369         // We (re)set to valid, so that component automatically gets (re)validated
370         setValid(true);
371         super.decode(context);
372     }
373 
374     @Override
375     public void broadcast(FacesEvent event) throws AbortProcessingException
376     {
377         // invoke standard listeners attached to this component first
378         super.broadcast(event);
379 
380         // Check if the event is applicable for ValueChangeListener
381         if (event instanceof ValueChangeEvent)
382         {
383             // invoke the single listener defined directly on the component
384             MethodBinding valueChangeListenerBinding = getValueChangeListener();
385             if (valueChangeListenerBinding != null)
386             {
387                 try
388                 {
389                     valueChangeListenerBinding.invoke(getFacesContext(), new Object[] { event });
390                 }
391                 catch (EvaluationException e)
392                 {
393                     Throwable cause = e.getCause();
394                     if (cause != null && cause instanceof AbortProcessingException)
395                     {
396                         throw (AbortProcessingException) cause;
397                     }
398                     else
399                     {
400                         throw e;
401                     }
402                 }
403             }
404         }
405     }
406 
407     public void updateModel(FacesContext context)
408     {
409         if (context == null)
410         {
411             throw new NullPointerException();
412         }
413         if (!isValid())
414         {
415             return;
416         }
417         if (!isLocalValueSet())
418         {
419             return;
420         }
421         ValueExpression expression = getValueExpression("value");
422         if (expression == null)
423         {
424             return;
425         }
426 
427         try
428         {
429             expression.setValue(context.getELContext(), getLocalValue());
430             setValue(null);
431             setLocalValueSet(false);
432         }
433         catch (Exception e)
434         {
435             // Enqueue an error message
436             //context.getExternalContext().log(e.getMessage(), e);
437             
438             // Create a FacesMessage with the id UPDATE_MESSAGE_ID
439             FacesMessage facesMessage = _MessageUtils.getMessage(context,
440                     context.getViewRoot().getLocale(), FacesMessage.SEVERITY_ERROR, UPDATE_MESSAGE_ID,
441                     new Object[] { _MessageUtils.getLabel(context, this) });
442             
443             // create an UpdateModelException and enqueue it since 
444             // we are not allowed to throw it directly here
445             // spec javadoc: The exception must not be re-thrown. This enables tree traversal to 
446             // continue for this lifecycle phase, as in all the other lifecycle phases.
447             UpdateModelException updateModelException = new UpdateModelException(facesMessage, e);
448             ExceptionQueuedEventContext exceptionQueuedContext 
449                     = new ExceptionQueuedEventContext(context, updateModelException, this, PhaseId.UPDATE_MODEL_VALUES);
450             
451             // spec javadoc says we should call context.getExceptionHandler().processEvent(exceptionQueuedContext),
452             // which is not just syntactically wrong, but also stupid!!
453             context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, exceptionQueuedContext);
454             
455             // Set the valid property of this UIInput to false
456             setValid(false);
457         }
458     }
459 
460     protected void validateValue(FacesContext context, Object convertedValue)
461     {
462         if (!isValid())
463         {
464             return;
465         }
466 
467         // If our value is empty, check the required property
468         boolean isEmpty = isEmpty(convertedValue); 
469 
470         if (isRequired() && isEmpty)
471         {
472             if (getRequiredMessage() != null)
473             {
474                 String requiredMessage = getRequiredMessage();
475                 context.addMessage(this.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
476                     requiredMessage, requiredMessage));
477             }
478             else
479             {
480                 _MessageUtils.addErrorMessage(context, this, REQUIRED_MESSAGE_ID,
481                     new Object[] { _MessageUtils.getLabel(context, this) });
482             }
483             setValid(false);
484             return;
485         }
486 
487         if (!isEmpty || shouldValidateEmptyFields(context))
488         {
489             _ComponentUtils.callValidators(context, this, convertedValue);
490         }
491     }
492     
493     /**
494      * Checks if the <code>validate()</code> should interpret an empty
495      * submitted value should be handle as <code>NULL</code>
496      * 
497      * @return a (cached) boolean to identify the interpretation as null
498      */
499     private boolean shouldInterpretEmptyStringSubmittedValuesAsNull(FacesContext context)
500     {
501         ExternalContext ec = context.getExternalContext();
502         Boolean interpretEmptyStringAsNull
503                 = (Boolean)ec.getApplicationMap().get(MYFACES_EMPTY_VALUES_AS_NULL_PARAM_NAME);
504 
505         // not yet cached...
506         if (interpretEmptyStringAsNull == null)
507         {
508             // parses the web.xml to get the "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL" value
509             String param = ec.getInitParameter(EMPTY_STRING_AS_NULL_PARAM_NAME);
510 
511             // evaluate the param
512             interpretEmptyStringAsNull = "true".equalsIgnoreCase(param);
513 
514             // cache the parsed value
515             ec.getApplicationMap().put(MYFACES_EMPTY_VALUES_AS_NULL_PARAM_NAME, interpretEmptyStringAsNull);
516         }
517 
518         return interpretEmptyStringAsNull;
519     }
520 
521     /**
522      * <p>Return <code>true</code> if the value is an empty <code>String</code>.</p>
523      */
524     private boolean isEmptyString(Object value)
525     {
526         return ((value instanceof String) && (((String) value).length() == 0));
527     }
528 
529 
530     private boolean shouldValidateEmptyFields(FacesContext context)
531     {
532         ExternalContext ec = context.getExternalContext();
533         Boolean validateEmptyFields = (Boolean) ec.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
534 
535         if (validateEmptyFields == null)
536         {
537              String param = ec.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
538 
539              // null means the same as auto.
540              if (param == null)
541              {
542                  param = "auto";
543              }
544              else
545              {
546                  // The environment variables are case insensitive.
547                  param = param.toLowerCase();
548              }
549 
550              if (param.equals("auto") && _ExternalSpecifications.isBeanValidationAvailable())
551              {
552                  validateEmptyFields = true;
553              }
554              else if (param.equals("true"))
555              {
556                  validateEmptyFields = true;
557              }
558              else
559              {
560                  validateEmptyFields = false;
561              }
562 
563              // cache the parsed value
564              ec.getApplicationMap().put(VALIDATE_EMPTY_FIELDS_PARAM_NAME, validateEmptyFields);
565         }
566 
567         return validateEmptyFields;
568     }
569 
570     /**
571      * Get the value of context parameter
572      * org.apache.myfaces.CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY from the web.xml
573      * 
574      * @return the value of the context parameter
575      */
576     private boolean shouldClearInputWhenSubmittedValueIsNullOrEmpty(FacesContext context)
577     {
578         ExternalContext ec = context.getExternalContext();
579         Boolean clearInputWhenSubmittedValueIsNullOrEmpty = 
580                         (Boolean) ec.getApplicationMap().get(CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY_PARAM_NAME);
581 
582         if (clearInputWhenSubmittedValueIsNullOrEmpty == null)
583         {
584             // parses the web.xml to get the
585             // "org.apache.myfaces.CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY" value
586             String param = ec.getInitParameter(CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY_PARAM_NAME);
587             
588             // evaluate the param
589             // NOTE: On JSF 2.3, this value will be set to true by default.
590             if (param == null)
591             {
592                 clearInputWhenSubmittedValueIsNullOrEmpty = true; // default
593             }
594             else if ("false".equalsIgnoreCase(param))
595             {
596                 clearInputWhenSubmittedValueIsNullOrEmpty = false;
597             }
598             else
599             {
600                 clearInputWhenSubmittedValueIsNullOrEmpty = true;
601             }
602             
603             // cache the parsed value
604             ec.getApplicationMap().put(CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY_PARAM_NAME, clearInputWhenSubmittedValueIsNullOrEmpty);
605         }
606         
607         return clearInputWhenSubmittedValueIsNullOrEmpty;
608     }
609     
610     private boolean shouldAlwaysPerformValidationWhenRequiredTrue(FacesContext context)
611     {
612         ExternalContext ec = context.getExternalContext();
613         Boolean alwaysPerformValidationWhenRequiredTrue = (Boolean) ec.getApplicationMap().get(
614                 ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE);
615 
616         if (alwaysPerformValidationWhenRequiredTrue == null)
617         {
618              String param = ec.getInitParameter(ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE);
619 
620              // null means the same as auto.
621              if (param == null)
622              {
623                  param = "false";
624              }
625              else
626              {
627                  // The environment variables are case insensitive.
628                  param = param.toLowerCase();
629              }
630 
631              if (param.equals("true"))
632              {
633                  alwaysPerformValidationWhenRequiredTrue = true;
634              }
635              else
636              {
637                  alwaysPerformValidationWhenRequiredTrue = false;
638              }
639 
640              // cache the parsed value
641              ec.getApplicationMap().put(ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE, 
642                      alwaysPerformValidationWhenRequiredTrue);
643         }
644 
645         return alwaysPerformValidationWhenRequiredTrue;
646     }
647 
648     /**
649      * Determine whether the new value is valid, and queue a ValueChangeEvent if necessary.
650      * <p>
651      * The "submitted value" is converted to the necessary type; conversion failure is reported as an error and
652      * validation processing terminates for this component. See documentation for method getConvertedValue for details
653      * on the conversion process.
654      * <p>
655      * Any validators attached to this component are then run, passing the converted value.
656      * <p>
657      * The old value of this component is then fetched (possibly involving the evaluation of a value-binding expression,
658      * ie invoking a method on a user object). The old value is compared to the new validated value, and if they are
659      * different then a ValueChangeEvent is queued for later processing.
660      * <p>
661      * On successful completion of this method:
662      * <ul>
663      * <li>isValid() is true
664      * <li>isLocalValueSet() is true
665      * <li>submittedValue is reset to null
666      * <li>a ValueChangeEvent is queued if the new value != old value
667      * </ul>
668      */
669     public void validate(FacesContext context)
670     {
671         if (context == null)
672         {
673             throw new NullPointerException("context");
674         }
675 
676         Object submittedValue = getSubmittedValue();
677         if (submittedValue == null)
678         {
679             if (isRequired() && shouldAlwaysPerformValidationWhenRequiredTrue(context))
680             {
681                 // continue
682             }
683             else
684             {
685                 return;
686             }
687         }
688 
689         // Begin new JSF 2.0 requirement (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
690         if (shouldInterpretEmptyStringSubmittedValuesAsNull(context) && isEmptyString(submittedValue))
691         {   
692             setSubmittedValue(null);
693 
694             if (!shouldClearInputWhenSubmittedValueIsNullOrEmpty(context))
695             {
696                 submittedValue = null;
697             }
698         }
699         // End new JSF 2.0 requirement (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
700 
701         Object convertedValue;
702         try
703         {
704             if (shouldClearInputWhenSubmittedValueIsNullOrEmpty(context))
705             {
706                 // don't use local variable submittedValue because of
707                 // (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
708                 convertedValue = getConvertedValue(context, getSubmittedValue());
709             }
710             else
711             {
712                 convertedValue = getConvertedValue(context, submittedValue);
713             }
714         }
715         catch (ConverterException e)
716         {
717             String converterMessage = getConverterMessage();
718             if (converterMessage != null)
719             {
720                 context.addMessage(getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
721                         converterMessage, converterMessage));
722             }
723             else
724             {
725                 FacesMessage facesMessage = e.getFacesMessage();
726                 if (facesMessage != null)
727                 {
728                     context.addMessage(getClientId(context), facesMessage);
729                 }
730                 else
731                 {
732                     _MessageUtils.addErrorMessage(context, this, CONVERSION_MESSAGE_ID,
733                             new Object[] { _MessageUtils.getLabel(context, this) });
734                 }
735             }
736             setValid(false);
737             if (shouldClearInputWhenSubmittedValueIsNullOrEmpty(context))
738             {
739                 // set submitted value again if is invalid
740                 // because of (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
741                 setSubmittedValue(submittedValue);
742             }
743             return;
744         }
745 
746         validateValue(context, convertedValue);
747 
748         if (!isValid())
749         {
750             if (shouldClearInputWhenSubmittedValueIsNullOrEmpty(context))
751             {
752                 // set submitted value again  if is invalid
753                 // because of (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
754                 setSubmittedValue(submittedValue);
755             }
756             return;
757         }
758 
759         Object previousValue = getValue();
760         setValue(convertedValue);
761         setSubmittedValue(null);
762         if (compareValues(previousValue, convertedValue))
763         {
764             queueEvent(new ValueChangeEvent(this, previousValue, convertedValue));
765         }
766     }
767 
768     /**
769      * Convert the provided object to the desired value.
770      * <p>
771      * If there is a renderer for this component, then call the renderer's getConvertedValue method. While this can of
772      * course be implemented in any way the renderer desires, it typically performs exactly the same processing that
773      * this method would have done anyway (ie that described below for the no-renderer case).
774      * <p>
775      * Otherwise:
776      * <ul>
777      * <li>If the submittedValue is not a String then just return the submittedValue unconverted.
778      * <li>If there is no "value" value-binding then just return the submittedValue unconverted.
779      * <li>Use introspection to determine the type of the target property specified by the value-binding, and then use
780      * Application.createConverter to find a converter that can map from String to the required type. Apply the
781      * converter to the submittedValue and return the result.
782      * </ul>
783      */
784     protected Object getConvertedValue(FacesContext context, Object submittedValue) throws ConverterException
785     {
786         Renderer renderer = getRenderer(context);
787         if (renderer != null)
788         {
789             return renderer.getConvertedValue(context, this, submittedValue);
790         }
791         else if (submittedValue instanceof String)
792         {
793             Converter converter = _SharedRendererUtils.findUIOutputConverter(context, this);
794             if (converter != null)
795             {
796                 return converter.getAsObject(context, this, (String) submittedValue);
797             }
798         }
799         return submittedValue;
800     }
801 
802     protected boolean compareValues(Object previous, Object value)
803     {
804         return previous == null ? (value != null) : (!previous.equals(value));
805     }
806 
807     /**
808      * @since 1.2
809      */
810     public void resetValue()
811     {
812         super.resetValue();
813         setSubmittedValue(null);
814         setLocalValueSet(false);
815         setValid(true);
816     }
817     
818     /**
819      * A boolean value that identifies the phase during which action events should fire.
820      * <p>
821      * During normal event processing, action methods and action listener methods are fired during the
822      * "invoke application" phase of request processing. If this attribute is set to "true", these methods are fired
823      * instead at the end of the "apply request values" phase.
824      * </p>
825      */
826     @JSFProperty
827     public boolean isImmediate()
828     {
829         return (Boolean) getStateHelper().eval(PropertyKeys.immediate, Boolean.FALSE);
830     }
831 
832     public void setImmediate(boolean immediate)
833     {
834         getStateHelper().put(PropertyKeys.immediate, immediate );
835     }
836 
837     /**
838      * A boolean value that indicates whether an input value is required.
839      * <p>
840      * If this value is true and no input value is provided by a postback operation, then the "requiredMessage" text is
841      * registered as a FacesMessage for the request, and validation fails.
842      * </p>
843      * <p>
844      * Default value: false.
845      * </p>
846      */
847     @JSFProperty(defaultValue = "false")
848     public boolean isRequired()
849     {
850         return (Boolean) getStateHelper().eval(PropertyKeys.required, Boolean.FALSE);
851     }
852 
853     public void setRequired(boolean required)
854     {
855         getStateHelper().put(PropertyKeys.required, required ); 
856     }
857 
858     /**
859      * Text to be displayed to the user as an error message when conversion of a submitted value to the target type
860      * fails.
861      * <p>
862      * </p>
863      */
864     @JSFProperty
865     public String getConverterMessage()
866     {
867         return (String) getStateHelper().eval(PropertyKeys.converterMessage);
868     }
869 
870     public void setConverterMessage(String converterMessage)
871     {
872         getStateHelper().put(PropertyKeys.converterMessage, converterMessage );
873     }
874 
875     /**
876      * Text to be displayed to the user as an error message when this component is marked as "required" but no input
877      * data is present during a postback (ie the user left the required field blank).
878      */
879     @JSFProperty
880     public String getRequiredMessage()
881     {
882         return (String) getStateHelper().eval(PropertyKeys.requiredMessage);
883     }
884 
885     public void setRequiredMessage(String requiredMessage)
886     {
887         getStateHelper().put(PropertyKeys.requiredMessage, requiredMessage );
888     }
889 
890     /**
891      * A method-binding EL expression which is invoked during the validation phase for this component.
892      * <p>
893      * The invoked method is expected to check the submitted value for this component, and if not acceptable then report
894      * a validation error for the component.
895      * </p>
896      * <p>
897      * The method is expected to have the prototype
898      * </p>
899      * <code>public void aMethod(FacesContext, UIComponent,Object)</code>
900      * 
901      * @deprecated
902      */
903     @SuppressWarnings("dep-ann")
904     @JSFProperty(stateHolder=true, returnSignature = "void",
905             methodSignature = "javax.faces.context.FacesContext,javax.faces.component.UIComponent,java.lang.Object")
906     public MethodBinding getValidator()
907     {
908         return (MethodBinding) getStateHelper().eval(PropertyKeys.validator);
909     }
910 
911     /** See getValidator.
912      *  
913      * @deprecated 
914      */
915     public void setValidator(MethodBinding validator)
916     {
917         getStateHelper().put(PropertyKeys.validator, validator);
918     }
919 
920     /** See getValidator. */
921     public void addValidator(Validator validator)
922     {
923         if (validator == null)
924         {
925             throw new NullPointerException("validator");
926         }
927         
928         if (_validatorList == null)
929         {
930             //normally add user 0-3 validators: 
931             _validatorList = new _DeltaList<Validator>(3);
932         }
933 
934         _validatorList.add(validator);
935         
936         // The argument validator must be inspected for the presence of the ResourceDependency annotation.
937         //_handleAnnotations(FacesContext.getCurrentInstance(), validator);
938     }
939 
940     /** See getValidator. */
941     public void removeValidator(Validator validator)
942     {
943         if (validator == null || _validatorList == null)
944         {
945             return;
946         }
947 
948         _validatorList.remove(validator);
949     }
950 
951     /** See getValidator. */
952     public Validator[] getValidators()
953     {
954         if (_ExternalSpecifications.isBeanValidationAvailable() &&
955             Boolean.TRUE.equals(this.getAttributes().containsKey(BEAN_BEFORE_JSF_PROPERTY)))
956         {
957             int bvIndex = -1;
958             for (int i = 0; i < _validatorList.size(); i++)
959             {
960                 Validator v = _validatorList.get(i);
961                 if (_BeanValidationUtils.isBeanValidator(v))
962                 {
963                     bvIndex = i;
964                     break;
965                 }
966             }
967             if (bvIndex != -1)
968             {
969                 Validator[] array = new Validator[_validatorList.size()];
970                 for (int i = 0; i < _validatorList.size(); i++)
971                 {
972                     if (i == bvIndex)
973                     {
974                         array[0] = _validatorList.get(i);
975                         bvIndex = -1;
976                     }
977                     else
978                     {
979                         array[i+1] = _validatorList.get(i);
980                     }
981                 }
982                 return array;
983             }
984             else
985             {
986                 return _validatorList == null ? EMPTY_VALIDATOR_ARRAY
987                         : _validatorList.toArray(new Validator[_validatorList.size()]);
988             }
989         }
990         else
991         {
992             return _validatorList == null ? EMPTY_VALIDATOR_ARRAY
993                     : _validatorList.toArray(new Validator[_validatorList.size()]);
994         }
995     }
996 
997     /**
998      * Text which will be shown if validation fails.
999      */
1000     @JSFProperty
1001     public String getValidatorMessage()
1002     {
1003         return (String) getStateHelper().eval(PropertyKeys.validatorMessage);
1004     }
1005 
1006     public void setValidatorMessage(String validatorMessage)
1007     {
1008         getStateHelper().put(PropertyKeys.validatorMessage, validatorMessage );
1009     }
1010 
1011     /**
1012      * A method which is invoked during postback processing for the current view if the submitted value for this
1013      * component is not equal to the value which the "value" expression for this component returns.
1014      * <p>
1015      * The phase in which this method is invoked can be controlled via the immediate attribute.
1016      * </p>
1017      * 
1018      * @deprecated
1019      */
1020     @JSFProperty(stateHolder=true, returnSignature = "void",
1021                  methodSignature = "javax.faces.event.ValueChangeEvent", clientEvent="valueChange")
1022     public MethodBinding getValueChangeListener()
1023     {
1024         return (MethodBinding) getStateHelper().eval(PropertyKeys.valueChangeListener);
1025     }
1026 
1027     /**
1028      * See getValueChangeListener.
1029      * 
1030      * @deprecated
1031      */
1032     public void setValueChangeListener(MethodBinding valueChangeListener)
1033     {
1034         getStateHelper().put(PropertyKeys.valueChangeListener, valueChangeListener);
1035     }
1036 
1037     /**
1038      * Specifies whether the component's value is currently valid, ie whether the validators attached to this component
1039      * have allowed it.
1040      */
1041     @JSFProperty(defaultValue = "true", tagExcluded = true)
1042     public boolean isValid()
1043     {
1044         Object value = getStateHelper().get(PropertyKeys.valid);
1045         if (value != null)
1046         {
1047             return (Boolean) value;        
1048         }
1049         return true; 
1050     }
1051 
1052     public void setValid(boolean valid)
1053     {
1054         // default value for valid is true, so if the intention is to save the default
1055         // value when nothing else was set before, don't do it. This is done in order to
1056         // reduce the size of the saved state of the state helper. Default values won't be
1057         // included in the saved state. 
1058         if (getStateHelper().get(PropertyKeys.valid) != null || !valid)
1059         {
1060             getStateHelper().put(PropertyKeys.valid, valid );
1061         }
1062     }
1063 
1064     /**
1065      * Specifies whether a local value is currently set.
1066      * <p>
1067      * If false, values are being retrieved from any attached ValueBinding.
1068      */
1069     @JSFProperty(defaultValue = "false", tagExcluded = true)
1070     public boolean isLocalValueSet()
1071     {
1072         Object value = getStateHelper().get(PropertyKeys.localValueSet);
1073         if (value != null)
1074         {
1075             return (Boolean) value;        
1076         }
1077         return false;
1078     }
1079 
1080     public void setLocalValueSet(boolean localValueSet)
1081     {
1082         // default value for localValueSet is false, so if the intention is to save the default
1083         // value when nothing else was set before, don't do it. This is done in order to
1084         // reduce the size of the saved state of the state helper. Default values won't be
1085         // included in the saved state.
1086         if (getStateHelper().get(PropertyKeys.localValueSet) != null || localValueSet)
1087         {
1088             getStateHelper().put(PropertyKeys.localValueSet, localValueSet );
1089         }
1090     }
1091 
1092     /**
1093      * Gets the current submitted value. This value, if non-null, is set by the Renderer to store a possibly invalid
1094      * value for later conversion or redisplay, and has not yet been converted into the proper type for this component
1095      * instance. This method should only be used by the decode() and validate() method of this component, or its
1096      * corresponding Renderer; however, user code may manually set it to null to erase any submitted value.
1097      */
1098     @JSFProperty(tagExcluded = true)
1099     public Object getSubmittedValue()
1100     {
1101         return  getStateHelper().get(PropertyKeys.submittedValue);
1102     }
1103 
1104     public void setSubmittedValue(Object submittedValue)
1105     {
1106         FacesContext facesContext = getFacesContext();
1107         if (facesContext != null && facesContext.isProjectStage(ProjectStage.Development))
1108         {
1109             // extended debug-info when in Development mode
1110             _createFieldDebugInfo(facesContext, "submittedValue",
1111                     getStateHelper().get(PropertyKeys.submittedValue), submittedValue, 1);
1112         }
1113         getStateHelper().put(PropertyKeys.submittedValue, submittedValue );
1114     }
1115 
1116     public void addValueChangeListener(ValueChangeListener listener)
1117     {
1118         addFacesListener(listener);
1119     }
1120 
1121     public void removeValueChangeListener(ValueChangeListener listener)
1122     {
1123         removeFacesListener(listener);
1124     }
1125 
1126     /**
1127      * The valueChange event is delivered when the value attribute
1128      * is changed.
1129      */
1130     @JSFListener(event="javax.faces.event.ValueChangeEvent")
1131     public ValueChangeListener[] getValueChangeListeners()
1132     {
1133         return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
1134     }
1135 
1136     enum PropertyKeys
1137     {
1138          immediate
1139         , required
1140         , converterMessage
1141         , requiredMessage
1142         , validator
1143         , validatorListSet
1144         , validatorMessage
1145         , valueChangeListener
1146         , valid
1147         , localValueSet
1148         , submittedValue
1149     }
1150     
1151     private static final Object[] INITIAL_STATE_PROPERTIES = new
1152             Object[]{
1153                 UIOutput.PropertyKeys.value,
1154                 null,
1155                 UIInput.PropertyKeys.localValueSet,
1156                 false,
1157                 UIInput.PropertyKeys.submittedValue,
1158                 null,
1159                 UIInput.PropertyKeys.valid,
1160                 true
1161             };
1162     
1163     public void markInitialState()
1164     {
1165         StateHelper helper = getStateHelper(false);
1166         if (helper != null && helper instanceof _DeltaStateHelper)
1167         {
1168             ((_DeltaStateHelper)helper).markPropertyInInitialState(INITIAL_STATE_PROPERTIES);
1169         }
1170         super.markInitialState();
1171         if (_validatorList != null)
1172         {
1173             _validatorList.markInitialState();
1174         }
1175     }
1176     
1177     public void clearInitialState()
1178     {
1179         if (initialStateMarked())
1180         {
1181             super.clearInitialState();
1182             if (_validatorList != null)
1183             {
1184                 _validatorList.clearInitialState();
1185             }
1186         }
1187     }    
1188 
1189     @Override
1190     public Object saveState(FacesContext facesContext)
1191     {
1192         if (initialStateMarked())
1193         {
1194             Object parentSaved = super.saveState(facesContext);
1195             Object validatorListSaved = saveValidatorList(facesContext);
1196             if (parentSaved == null && validatorListSaved == null)
1197             {
1198                 //No values
1199                 return null;
1200             }
1201             
1202             Object[] values = new Object[2];
1203             values[0] = parentSaved;
1204             values[1] = validatorListSaved;
1205             return values;
1206         }
1207         else
1208         {
1209             Object[] values = new Object[2];
1210             values[0] = super.saveState(facesContext);
1211             values[1] = saveValidatorList(facesContext);
1212             return values;
1213         }
1214     }
1215 
1216     @SuppressWarnings("unchecked")
1217     @Override
1218     public void restoreState(FacesContext facesContext, Object state)
1219     {
1220         if (state == null)
1221         {
1222             return;
1223         }
1224         
1225         Object[] values = (Object[])state;
1226         super.restoreState(facesContext,values[0]);
1227         if (values[1] instanceof _AttachedDeltaWrapper)
1228         {
1229             //Delta
1230             if (_validatorList != null)
1231             {
1232                 ((StateHolder)_validatorList).restoreState(facesContext,
1233                         ((_AttachedDeltaWrapper) values[1]).getWrappedStateObject());
1234             }
1235         }
1236         else if (values[1] != null || !initialStateMarked())
1237         {
1238             //Full
1239             _validatorList = (_DeltaList<Validator>)
1240                 restoreAttachedState(facesContext,values[1]);
1241         }
1242     }
1243     
1244     private Object saveValidatorList(FacesContext facesContext)
1245     {
1246         PartialStateHolder holder = (PartialStateHolder) _validatorList;
1247         if (initialStateMarked() && _validatorList != null && holder.initialStateMarked())
1248         {                
1249             Object attachedState = holder.saveState(facesContext);
1250             if (attachedState != null)
1251             {
1252                 return new _AttachedDeltaWrapper(_validatorList.getClass(),
1253                         attachedState);
1254             }
1255             //_validatorList instances once is created never changes, we can return null
1256             return null;
1257         }
1258         else
1259         {
1260             return saveAttachedState(facesContext,_validatorList);
1261         }            
1262     }
1263     
1264     /**
1265      * Returns the debug-info Map for this component.
1266      * @return
1267      */
1268     @SuppressWarnings("unchecked")
1269     private Map<String, List<Object[]>> _getDebugInfoMap()
1270     {
1271         Map<String, Object> requestMap = getFacesContext()
1272                 .getExternalContext().getRequestMap();
1273         Map<String, List<Object[]>> debugInfo = (Map<String, List<Object[]>>) 
1274                 requestMap.get(DEBUG_INFO_KEY + getClientId());
1275         if (debugInfo == null)
1276         {
1277             // no debug info available yet, create one and put it on the attributes map
1278             debugInfo = new HashMap<String, List<Object[]>>();
1279             requestMap.put(DEBUG_INFO_KEY + getClientId(), debugInfo);
1280         }
1281         return debugInfo;
1282     }
1283     
1284     /**
1285      * Returns the field's debug-infos from the component's debug-info Map.
1286      * @param field
1287      * @return
1288      */
1289     private List<Object[]> _getFieldDebugInfos(final String field)
1290     {
1291         Map<String, List<Object[]>> debugInfo = _getDebugInfoMap();
1292         List<Object[]> fieldDebugInfo = debugInfo.get(field);
1293         if (fieldDebugInfo == null)
1294         {
1295             // no field debug-infos yet, create them and store it in the Map
1296             fieldDebugInfo = new ArrayList<Object[]>();
1297             debugInfo.put(field, fieldDebugInfo);
1298         }
1299         return fieldDebugInfo;
1300     }
1301     
1302     /**
1303      * Creates the field debug-info for the given field, which changed
1304      * from oldValue to newValue.
1305      * 
1306      * @param facesContext
1307      * @param field
1308      * @param oldValue
1309      * @param newValue
1310      * @param skipStackTaceElements How many StackTraceElements should be skipped
1311      *                              when the calling function will be determined.
1312      */
1313     private void _createFieldDebugInfo(FacesContext facesContext,
1314             final String field, Object oldValue, 
1315             Object newValue, final int skipStackTaceElements)
1316     {
1317         if (oldValue == null && newValue == null)
1318         {
1319             // both values are null, not interesting and can
1320             // happen a lot in UIData with saving and restoring state
1321             return;
1322         }
1323         
1324         if (facesContext.getViewRoot() == null)
1325         {
1326             // No viewRoot set, it is creating component, 
1327             // so it is not possible to calculate the clientId, 
1328             // abort processing because the interesting part will
1329             // happen later.
1330             return;
1331         }
1332         
1333         if (getParent() == null || !isInView())
1334         {
1335             //Skip if no parent or is not in view
1336             return;
1337         }
1338         
1339         // convert Array values into a more readable format
1340         if (oldValue != null && oldValue.getClass().isArray() && Object[].class.isAssignableFrom(oldValue.getClass()))
1341         {
1342             oldValue = Arrays.deepToString((Object[]) oldValue);
1343         }
1344         if (newValue != null && newValue.getClass().isArray() && Object[].class.isAssignableFrom(newValue.getClass()))
1345         {
1346             newValue = Arrays.deepToString((Object[]) newValue);
1347         }
1348         
1349         // use Throwable to get the current call stack
1350         Throwable throwableHelper = new Throwable();
1351         StackTraceElement[] stackTraceElements = throwableHelper.getStackTrace();
1352         List<StackTraceElement> debugStackTraceElements = new LinkedList<StackTraceElement>();
1353         
1354         // + 1 because this method should also be skipped
1355         for (int i = skipStackTaceElements + 1; i < stackTraceElements.length; i++)
1356         {
1357             debugStackTraceElements.add(stackTraceElements[i]);
1358             
1359             if (FacesServlet.class.getCanonicalName()
1360                     .equals(stackTraceElements[i].getClassName()))
1361             {
1362                 // stop after the FacesServlet
1363                 break;
1364             }
1365         }
1366         
1367         // create the debug-info array
1368         // structure:
1369         //     - 0: phase
1370         //     - 1: old value
1371         //     - 2: new value
1372         //     - 3: StackTraceElement List
1373         // NOTE that we cannot create a class here to encapsulate this data,
1374         // because this is not on the spec and the class would not be available in impl.
1375         Object[] debugInfo = new Object[4];
1376         debugInfo[0] = facesContext.getCurrentPhaseId();
1377         debugInfo[1] = oldValue;
1378         debugInfo[2] = newValue;
1379         debugInfo[3] = debugStackTraceElements;
1380         
1381         // add the debug info
1382         _getFieldDebugInfos(field).add(debugInfo);
1383     }
1384     
1385     /**
1386      * Check if a value is empty or not. Since we don't know the class of
1387      * value we have to check and deal with it properly.
1388      * 
1389      * @since 2.0
1390      * @param value
1391      * @return
1392      */
1393     public static boolean isEmpty(Object value)
1394     {
1395         if (value == null)
1396         {
1397             return true;
1398         }
1399         else if (value instanceof String)
1400         {
1401             if ( ((String)value).trim().length() <= 0 )
1402             {
1403                 return true;
1404             }
1405         }
1406         else if (value instanceof Collection)
1407         {
1408             if ( ((Collection)value).isEmpty())
1409             {
1410                 return true;
1411             }
1412         }
1413         else if (value.getClass().isArray())
1414         {
1415             if (java.lang.reflect.Array.getLength(value) <= 0)
1416             {
1417                 return true;
1418             }
1419         }
1420         else if (value instanceof Map)
1421         {
1422             if ( ((Map)value).isEmpty())
1423             {
1424                 return true;
1425             }
1426         }
1427         return false;
1428     }
1429 
1430 }