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 javax.faces.application.FacesMessage;
22  import javax.faces.context.FacesContext;
23  import javax.faces.convert.Converter;
24  import javax.faces.convert.ConverterException;
25  import javax.faces.el.EvaluationException;
26  import javax.faces.el.MethodBinding;
27  import javax.faces.el.ValueBinding;
28  import javax.faces.event.AbortProcessingException;
29  import javax.faces.event.FacesEvent;
30  import javax.faces.event.ValueChangeEvent;
31  import javax.faces.event.ValueChangeListener;
32  import javax.faces.render.Renderer;
33  import javax.faces.validator.Validator;
34  import javax.faces.FacesException;
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  /**
39   * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a>
40   *
41   * @JSFComponent
42   *   type = "javax.faces.Input"
43   *   family = "javax.faces.Input"
44   *   desc = "UIInput"
45   *   
46   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
47   * @version $Revision: 949325 $ $Date: 2010-05-28 19:38:36 -0500 (Fri, 28 May 2010) $
48   */
49  public class UIInput extends UIOutput implements EditableValueHolder
50  {
51      public static final String COMPONENT_TYPE = "javax.faces.Input";
52      public static final String COMPONENT_FAMILY = "javax.faces.Input";
53      private static final String DEFAULT_RENDERER_TYPE = "javax.faces.Text";
54      private static final boolean DEFAULT_IMMEDIATE = false;
55      private static final boolean DEFAULT_REQUIRED = false;
56  
57      public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION";
58      public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED";
59      private static final String ERROR_HANDLING_EXCEPTION_LIST = "org.apache.myfaces.errorHandling.exceptionList";
60  
61      private static final Validator[] EMPTY_VALIDATOR_ARRAY = new Validator[0];
62  
63      private Boolean _immediate = null;
64      private Boolean _required = null;
65  
66      private Object _submittedValue = null;
67      private boolean _localValueSet = false;
68      private boolean _valid = true;
69      private MethodBinding _validator = null;
70      private MethodBinding _valueChangeListener = null;
71      private List _validatorList = null;
72  
73      // use javadoc inherited from EditableValueHolder
74      /**
75       * @JSFProperty
76       *   tagExcluded = "true"
77       */
78      public Object getSubmittedValue()
79      {
80          return _submittedValue;
81      }
82  
83      // use javadoc inherited from EditableValueHolder
84      public void setSubmittedValue(Object submittedValue)
85      {
86          _submittedValue = submittedValue;
87      }
88  
89      /**
90       * Store the specified object as the "local value" of this component.
91       * The value-binding named "value" (if any) is ignored; the object is
92       * only stored locally on this component. During the "update model"
93       * phase, if there is a value-binding named "value" then this local
94       * value will be stored via that value-binding and the "local value"
95       * reset to null.
96       */
97      public void setValue(Object value)
98      {
99          setLocalValueSet(true);
100         super.setValue(value);
101     }
102 
103     /**
104      * Return the current value of this component.
105      * <p>
106      * If a submitted value has been converted but not yet pushed into the
107      * model, then return that locally-cached value (see isLocalValueSet).
108      * <p>
109      * Otherwise, evaluate an EL expression to fetch a value from the model. 
110      */
111     public Object getValue()
112     {
113         if (isLocalValueSet()) return super.getLocalValue();
114         return super.getValue();
115     }
116 
117     // use javadoc inherited from EditableValueHolder
118     /**
119      * @JSFProperty
120      *   tagExcluded = "true"
121      */
122     public boolean isLocalValueSet()
123     {
124         return _localValueSet;
125     }
126 
127     // use javadoc inherited from EditableValueHolder
128     public void setLocalValueSet(boolean localValueSet)
129     {
130         _localValueSet = localValueSet;
131     }
132 
133     // use javadoc inherited from EditableValueHolder
134     /**
135      * @JSFProperty
136      *   tagExcluded = "true"
137      */
138     public boolean isValid()
139     {
140         return _valid;
141     }
142 
143     // use javadoc inherited from EditableValueHolder
144     public void setValid(boolean valid)
145     {
146         _valid = valid;
147     }
148 
149     // use javadoc inherited from EditableValueHolder    
150     /**
151      * A method binding EL expression, accepting FacesContext, UIComponent,
152      * and Object parameters, and returning void, that validates the
153      * component's local value.
154      * 
155      * @JSFProperty
156      *   stateHolder="true"
157      *   returnSignature="void"
158      *   methodSignature="javax.faces.context.FacesContext,javax.faces.component.UIComponent,java.lang.Object"
159      */
160     public MethodBinding getValidator()
161     {
162         return _validator;
163     }
164 
165     // use javadoc inherited from EditableValueHolder
166     public void setValidator(MethodBinding validator)
167     {
168         _validator = validator;
169     }
170 
171     // use javadoc inherited from EditableValueHolder
172     /**
173      * A method binding EL expression, accepting a ValueChangeEvent parameter
174      * and returning void. The specified method is invoked if this component
175      * is modified. The phase that this handler is fired in can be controlled
176      * via the immediate attribute.
177      * 
178      * @JSFProperty
179      *   stateHolder="true"
180      *   returnSignature="void"
181      *   methodSignature="javax.faces.event.ValueChangeEvent"
182      */
183     public MethodBinding getValueChangeListener()
184     {
185         return _valueChangeListener;
186     }
187 
188     // use javadoc inherited from EditableValueHolder
189     public void setValueChangeListener(MethodBinding valueChangeListener)
190     {
191         _valueChangeListener = valueChangeListener;
192     }
193 
194     /**
195      * Set the "submitted value" of this component from the relevant data
196      * in the current servlet request object.
197      * <p>
198      * If this component is not rendered, then do nothing; no output would
199      * have been sent to the client so no input is expected.
200      * <p>
201      * Invoke the inherited functionality, which typically invokes the
202      * renderer associated with this component to extract and set this
203      * component's "submitted value".
204      * <p>
205      * If this component is marked "immediate", then immediately apply
206      * validation to the submitted value found. On error, call context
207      * method "renderResponse" which will force processing to leap to
208      * the "render response" phase as soon as the "decode" step has
209      * completed for all other components.
210      */
211     public void processDecodes(FacesContext context)
212     {
213         if (context == null)
214         {
215             throw new NullPointerException("context");
216         }
217         try
218         {
219             setCachedFacesContext(context);
220             if (!isRendered())
221             {
222                 return;
223             }
224         }
225         finally
226         {
227             setCachedFacesContext(null);
228         }
229         super.processDecodes(context);
230         try
231         {
232             setCachedFacesContext(context);
233             if (isImmediate())
234             {
235                 try
236                 {
237                     validate(context);
238                 }
239                 catch (RuntimeException e)
240                 {
241                     context.renderResponse();
242                     throw e;
243                 }
244                 if (!isValid())
245                 {
246                     context.renderResponse();
247                 }
248             }
249         }
250         finally
251         {
252             setCachedFacesContext(null);
253         }
254     }
255 
256     public void processValidators(FacesContext context)
257     {
258         if (context == null)
259         {
260             throw new NullPointerException("context");
261         }
262         try
263         {
264             setCachedFacesContext(context);
265             if (!isRendered())
266             {
267                 return;
268             }
269         }
270         finally
271         {
272             setCachedFacesContext(null);
273         }
274         
275         super.processValidators(context);
276 
277         try
278         {
279             setCachedFacesContext(context);
280             if (!isImmediate())
281             {
282                 try
283                 {
284                     validate(context);
285                 }
286                 catch (RuntimeException e)
287                 {
288                     context.renderResponse();
289                     throw e;
290                 }
291                 if (!isValid())
292                 {
293                     context.renderResponse();
294                 }
295             }
296         }
297         finally
298         {
299             setCachedFacesContext(null);
300         }
301     }
302 
303     public void processUpdates(FacesContext context)
304     {
305         if (context == null)
306         {
307             throw new NullPointerException("context");
308         }
309         try
310         {
311             setCachedFacesContext(context);
312             if (!isRendered())
313             {
314                 return;
315             }
316         }
317         finally
318         {
319             setCachedFacesContext(null);
320         }
321         super.processUpdates(context);
322 
323         try
324         {
325             setCachedFacesContext(context);
326             try
327             {
328                 updateModel(context);
329             }
330             catch (RuntimeException e)
331             {
332                 context.renderResponse();
333                 throw e;
334             }
335             if (!isValid())
336             {
337                 context.renderResponse();
338             }
339         }
340         finally
341         {
342             setCachedFacesContext(null);
343         }
344     }
345 
346     public void decode(FacesContext context)
347     {
348         //We (re)set to valid, so that component automatically gets (re)validated
349         setValid(true);
350         super.decode(context);
351     }
352 
353     public void broadcast(FacesEvent event)
354             throws AbortProcessingException
355     {
356         // invoke standard listeners attached to this component first
357         super.broadcast(event);
358 
359         //Check if the event is applicable for ValueChangeListener
360         if (event instanceof ValueChangeEvent){
361             // invoke the single listener defined directly on the component
362             MethodBinding valueChangeListenerBinding = getValueChangeListener();
363             if (valueChangeListenerBinding != null)
364             {
365                 try
366                 {
367                     valueChangeListenerBinding.invoke(getFacesContext(),
368                                                       new Object[]{event});
369                 }
370                 catch (EvaluationException e)
371                 {
372                     Throwable cause = e.getCause();
373                     if (cause != null && cause instanceof AbortProcessingException)
374                     {
375                         throw (AbortProcessingException)cause;
376                     }
377                     else
378                     {
379                         throw e;
380                     }
381                 }
382             }
383         }
384     }
385 
386     public void updateModel(FacesContext context)
387     {
388         if (!isValid()) return;
389         if (!isLocalValueSet()) return;
390         ValueBinding vb = getValueBinding("value");
391         if (vb == null) return;
392         try
393         {
394             vb.setValue(context, getLocalValue());
395             setValue(null);
396             setLocalValueSet(false);
397         }
398          catch (Exception e)
399         {
400             //Object[] args = {getId()};
401             context.getExternalContext().log(e.getMessage(), e);
402             _MessageUtils.addErrorMessage(context, this,CONVERSION_MESSAGE_ID,new Object[]{getId()});
403             setValid(false);
404 
405             /* we are not allowed to throw exceptions here - we still need the full stack-trace later on
406              * to process it later in our error-handler
407              */
408             queueExceptionInRequest(context, vb, e);
409         }
410     }
411 
412     /**
413      * For development and production, we want to offer a single point
414      * to which error-handlers can attach. So we queue up all ocurring
415      * exceptions and later pass them to the configured error-handler.
416      */
417     private void queueExceptionInRequest(FacesContext context, ValueBinding binding, Exception e) {
418         List li = (List) context.getExternalContext().getRequestMap().get(ERROR_HANDLING_EXCEPTION_LIST);
419         if(null==li) {
420             li = new ArrayList();
421             context.getExternalContext().getRequestMap().put(ERROR_HANDLING_EXCEPTION_LIST, li);
422         }
423         li.add(new FacesException("Exception while setting value for expression : "+
424             binding.getExpressionString()+" of component with path : "
425             + _ComponentUtils.getPathToComponent(this),e));
426     }
427 
428     protected void validateValue(FacesContext context,Object convertedValue)
429     {
430         boolean empty = convertedValue == null ||
431                         (convertedValue instanceof String
432                          && ((String)convertedValue).length() == 0);
433 
434         if (isRequired() && empty)
435         {
436             _MessageUtils.addErrorMessage(context, this, REQUIRED_MESSAGE_ID,new Object[]{getId()});
437             setValid(false);
438             return;
439         }
440 
441         if (!empty)
442         {
443             _ComponentUtils.callValidators(context, this, convertedValue);
444         }
445 
446     }
447 
448     /**
449      * Determine whether the new value is valid, and queue a ValueChangeEvent
450      * if necessary.
451      * <p>
452      * The "submitted value" is converted to the necessary type; conversion
453      * failure is reported as an error and validation processing terminates
454      * for this component. See documentation for method getConvertedValue
455      * for details on the conversion process.
456      * <p>
457      * Any validators attached to this component are then run, passing
458      * the converted value.
459      * <p>
460      * The old value of this component is then fetched (possibly involving
461      * the evaluation of a value-binding expression, ie invoking a method
462      * on a user object). The old value is compared to the new validated
463      * value, and if they are different then a ValueChangeEvent is queued
464      * for later processing.
465      * <p>
466      * On successful completion of this method:
467      * <ul>
468      * <li> isValid() is true
469      * <li> isLocalValueSet() is true
470      * <li> getValue() will return the converted value, NOT evaluate the
471      * EL expression bound to the "value" attribute. Note that the getValue
472      * method will return to normal behaviour (evaluating its EL expression)
473      * after a successful update-model phase, ie after pushing this converted
474      * value into the model.
475      * <li> submittedValue is reset to null
476      * <li> a ValueChangeEvent is queued if the new value != old value
477      * </ul> 
478      * 
479      */
480     public void validate(FacesContext context)
481     {
482         if (context == null) throw new NullPointerException("context");
483 
484         try {
485 
486             Object submittedValue = getSubmittedValue();
487             if (submittedValue == null) return;
488 
489             Object convertedValue = getConvertedValue(context, submittedValue);
490 
491             if (!isValid()) return;
492 
493             validateValue(context, convertedValue);
494 
495             if (!isValid()) return;
496 
497             Object previousValue = getValue();
498             setValue(convertedValue);
499             setSubmittedValue(null);
500             if (compareValues(previousValue, convertedValue))
501             {
502                 queueEvent(new ValueChangeEvent(this, previousValue, convertedValue));
503             }
504         }
505         catch (Exception ex)
506         {
507             throw new FacesException("Exception while validating component with path : "+_ComponentUtils.getPathToComponent(this),ex);
508         }
509     }
510 
511     /**
512      * Convert the provided object to the desired value.
513      * <p>
514      * If there is a renderer for this component, then call the renderer's
515      * getConvertedValue method. While this can of course be implemented in
516      * any way the renderer desires, it typically performs exactly the same
517      * processing that this method would have done anyway (ie that described
518      * below for the no-renderer case).
519      * <p>
520      * Otherwise:
521      * <ul>
522      * <li>If the submittedValue is not a String then just return the
523      *   submittedValue unconverted.
524      * <li>If there is no "value" value-binding then just return the
525      *   submittedValue unconverted.
526      * <li>Use introspection to determine the type of the target 
527      *   property specified by the value-binding, and then use
528      *   Application.createConverter to find a converter that can
529      *   map from String to the required type. Apply the converter
530      *   to the submittedValue and return the result. 
531      * </ul>
532      */
533     protected Object getConvertedValue(FacesContext context, Object submittedValue)
534     {
535         try
536         {
537             Renderer renderer = getRenderer(context);
538             if (renderer != null)
539             {
540                 return renderer.getConvertedValue(context, this, submittedValue);
541             }
542             else if (submittedValue instanceof String)
543             {
544                 Converter converter = _SharedRendererUtils.findUIOutputConverter(context, this);
545                 if (converter != null)
546                 {
547                     return converter.getAsObject(context, this, (String)submittedValue);
548                 }
549             }
550         }
551         catch (ConverterException e)
552         {
553             FacesMessage facesMessage = e.getFacesMessage();
554             if (facesMessage != null)
555             {
556                 context.addMessage(getClientId(context), facesMessage);
557             }
558             else
559             {
560                 _MessageUtils.addErrorMessage(context, this, CONVERSION_MESSAGE_ID,new Object[]{getId()});
561             }
562             setValid(false);
563         }
564         return submittedValue;
565     }
566 
567 
568 
569     protected boolean compareValues(Object previous,
570                                       Object value)
571     {
572         return previous==null?(value!=null):(!previous.equals(value));
573     }
574 
575     public void addValidator(Validator validator)
576     {
577         if (validator == null) throw new NullPointerException("validator");
578         if (_validatorList == null)
579         {
580             _validatorList = new ArrayList();
581         }
582         _validatorList.add(validator);
583     }
584 
585     public Validator[] getValidators()
586     {
587         return _validatorList != null ?
588                (Validator[])_validatorList.toArray(new Validator[_validatorList.size()]) :
589                EMPTY_VALIDATOR_ARRAY;
590     }
591 
592     public void removeValidator(Validator validator)
593     {
594         if (validator == null) throw new NullPointerException("validator");
595         if (_validatorList != null)
596         {
597             _validatorList.remove(validator);
598         }
599     }
600 
601     public void addValueChangeListener(ValueChangeListener listener)
602     {
603         addFacesListener(listener);
604     }
605 
606     public ValueChangeListener[] getValueChangeListeners()
607     {
608         return (ValueChangeListener[])getFacesListeners(ValueChangeListener.class);
609     }
610 
611     public void removeValueChangeListener(ValueChangeListener listener)
612     {
613         removeFacesListener(listener);
614     }
615 
616     public Object saveState(FacesContext context)
617     {
618         Object values[] = new Object[9];
619         values[0] = super.saveState(context);
620         values[1] = _immediate;
621         values[2] = Boolean.valueOf(_localValueSet);
622         values[3] = _required;
623         values[4] = _submittedValue;
624         values[5] = Boolean.valueOf(_valid);
625         values[6] = saveAttachedState(context, _validator);
626         values[7] = saveAttachedState(context, _valueChangeListener);
627         values[8] = saveAttachedState(context, _validatorList);
628         return values;
629     }
630 
631     public void restoreState(FacesContext context, Object state)
632     {
633         Object values[] = (Object[])state;
634         super.restoreState(context, values[0]);
635         _immediate = (Boolean)values[1];
636         _localValueSet = ((Boolean)values[2]).booleanValue();
637         _required = (Boolean)values[3];
638         _submittedValue = values[4];
639         _valid = ((Boolean)values[5]).booleanValue();
640         _validator = (MethodBinding)restoreAttachedState(context, values[6]);
641         _valueChangeListener = (MethodBinding)restoreAttachedState(context, values[7]);
642         _validatorList = (List)restoreAttachedState(context, values[8]);
643 
644     }
645 
646     public UIInput()
647     {
648         setRendererType(DEFAULT_RENDERER_TYPE);
649     }
650 
651     public String getFamily()
652     {
653         return COMPONENT_FAMILY;
654     }
655 
656     public void setImmediate(boolean immediate)
657     {
658         _immediate = Boolean.valueOf(immediate);
659     }
660 
661     /**
662      * A boolean value that identifies the phase during which value change
663      * events should fire. During normal event processing, value change
664      * events are fired during the "process validations" phase of request
665      * processing. If this attribute is set to "true", these methods are
666      * fired instead at the end of the "apply request values" phase.
667      * 
668      * @JSFProperty
669      */
670     public boolean isImmediate()
671     {
672         if (_immediate != null) return _immediate.booleanValue();
673         ValueBinding vb = getValueBinding("immediate");
674         Boolean v = vb != null ? (Boolean)vb.getValue(getFacesContext()) : null;
675         return v != null ? v.booleanValue() : DEFAULT_IMMEDIATE;
676     }
677 
678     public void setRequired(boolean required)
679     {
680         _required = Boolean.valueOf(required);
681     }
682 
683     /**
684      * A boolean value that indicates whether an input value is required.
685      * If this value is true, and no input value is provided, the error
686      * message javax.faces.component.UIInput.REQUIRED is posted.
687      * 
688      * @JSFProperty
689      */
690     public boolean isRequired()
691     {
692         if (_required != null) return _required.booleanValue();
693         ValueBinding vb = getValueBinding("required");
694         Boolean v = vb != null ? (Boolean)vb.getValue(getFacesContext()) : null;
695         return v != null ? v.booleanValue() : DEFAULT_REQUIRED;
696     }
697 }