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