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