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