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