View Javadoc

1   // WARNING: This file was automatically generated. Do not edit it directly,
2   //          or you will lose your changes.
3   
4   /*
5    * Licensed to the Apache Software Foundation (ASF) under one
6    * or more contributor license agreements.  See the NOTICE file
7    * distributed with this work for additional information
8    * regarding copyright ownership.  The ASF licenses this file
9    * to you under the Apache License, Version 2.0 (the
10   * "License"); you may not use this file except in compliance
11   * with the License.  You may obtain a copy of the License at
12   *
13   *   http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing,
16   * software distributed under the License is distributed on an
17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   * KIND, either express or implied.  See the License for the
19   * specific language governing permissions and limitations
20   * under the License.
21  */
22  package org.apache.myfaces.trinidad.component;
23  
24  import java.util.Iterator;
25  import javax.el.ValueExpression;
26  import javax.faces.FacesException;
27  import javax.faces.application.Application;
28  import javax.faces.application.FacesMessage;
29  import javax.faces.component.EditableValueHolder;
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIInput;
32  import javax.faces.context.ExternalContext;
33  import javax.faces.context.FacesContext;
34  import javax.faces.convert.Converter;
35  import javax.faces.convert.ConverterException;
36  import javax.faces.el.EvaluationException;
37  import javax.faces.el.MethodBinding;
38  import javax.faces.event.AbortProcessingException;
39  import javax.faces.event.FacesEvent;
40  import javax.faces.event.FacesListener;
41  import javax.faces.event.PostValidateEvent;
42  import javax.faces.event.PreValidateEvent;
43  import javax.faces.event.ValueChangeEvent;
44  import javax.faces.event.ValueChangeListener;
45  import javax.faces.render.Renderer;
46  import javax.faces.validator.Validator;
47  import javax.faces.validator.ValidatorException;
48  import javax.validation.Validation;
49  import org.apache.myfaces.trinidad.bean.FacesBean;
50  import org.apache.myfaces.trinidad.bean.PropertyKey;
51  import org.apache.myfaces.trinidad.context.RequestContext;
52  import org.apache.myfaces.trinidad.event.ValueUpdatedEvent;
53  import org.apache.myfaces.trinidad.event.ValueUpdatedListener;
54  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
55  import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
56  import org.apache.myfaces.trinidad.util.ComponentUtils;
57  import org.apache.myfaces.trinidad.util.LabeledFacesMessage;
58  import org.apache.myfaces.trinidad.util.MessageFactory;
59  import org.apache.myfaces.trinidad.util.Reportable;
60  
61  /**
62   *
63   * <h4>Events:</h4>
64   * <table border="1" width="100%" cellpadding="3" summary="">
65   * <tr bgcolor="#CCCCFF" class="TableHeadingColor">
66   * <th align="left">Type</th>
67   * <th align="left">Phases</th>
68   * <th align="left">Description</th>
69   * </tr>
70   * <tr class="TableRowColor">
71   * <td valign="top"><code>javax.faces.event.ValueChangeEvent</code></td>
72   * <td valign="top" nowrap>Process<br>Validations<br>Apply<br>Request<br>Values</td>
73   * <td valign="top">The valueChange event is delivered when the value
74                         attribute is changed.</td>
75   * </tr>
76   * <tr class="TableRowColor">
77   * <td valign="top"><code>org.apache.myfaces.trinidad.event.AttributeChangeEvent</code></td>
78   * <td valign="top" nowrap>Invoke<br>Application<br>Apply<br>Request<br>Values</td>
79   * <td valign="top">Event delivered to describe an attribute change.  Attribute change events are not delivered for any programmatic change to a property.  They are only delivered when a renderer changes a property without the application's specific request.  An example of an attribute change event might include the width of a column that supported client-side resizing.</td>
80   * </tr>
81   * </table>
82   */
83  abstract public class UIXEditableValue extends UIXValue
84                                         implements EditableValueHolder
85  {
86    static public final FacesBean.Type TYPE = new FacesBean.Type(
87      UIXValue.TYPE);
88    static public final PropertyKey IMMEDIATE_KEY =
89      TYPE.registerKey("immediate", Boolean.class, Boolean.FALSE);
90    static public final PropertyKey VALID_KEY =
91      TYPE.registerKey("valid", Boolean.class, Boolean.TRUE);
92    static public final PropertyKey REQUIRED_KEY =
93      TYPE.registerKey("required", Boolean.class, Boolean.FALSE);
94    static public final PropertyKey LOCAL_VALUE_SET_KEY =
95      TYPE.registerKey("localValueSet", Boolean.class, PropertyKey.CAP_NOT_BOUND);
96    static public final PropertyKey SUBMITTED_VALUE_KEY =
97      TYPE.registerKey("submittedValue", PropertyKey.CAP_NOT_BOUND);
98    static public final PropertyKey VALIDATOR_KEY =
99      TYPE.registerKey("validator", MethodBinding.class, PropertyKey.CAP_NOT_BOUND | PropertyKey.CAP_STATE_HOLDER);
100   static public final PropertyKey VALUE_CHANGE_LISTENER_KEY =
101     TYPE.registerKey("valueChangeListener", MethodBinding.class, PropertyKey.CAP_NOT_BOUND | PropertyKey.CAP_STATE_HOLDER);
102   static public final PropertyKey REQUIRED_MESSAGE_DETAIL_KEY =
103     TYPE.registerKey("requiredMessageDetail", String.class);
104 
105   static public final String COMPONENT_FAMILY =
106     "org.apache.myfaces.trinidad.EditableValue";
107   static public final String COMPONENT_TYPE =
108     "org.apache.myfaces.trinidad.EditableValue";
109   
110   static public final PropertyKey VALIDATORS_KEY =
111     TYPE.registerKey("validators", Validator[].class, PropertyKey.CAP_LIST);
112 
113   static public final String REQUIRED_MESSAGE_ID =
114     "org.apache.myfaces.trinidad.UIXEditableValue.REQUIRED";
115   static public final String CONVERSION_MESSAGE_ID =
116     "org.apache.myfaces.trinidad.UIXEditableValue.CONVERSION";
117   static public final String TRINIDAD_BEAN_VALIDATION_AVAILABLE =
118     "org.apache.myfaces.trinidad.UIXEditableValue.BEAN_VALIDATION_AVAILABLE";
119   static public final String VALIDATE_EMPTY_FIELDS_PARAM_NAME =
120     "org.apache.myfaces.trinidad.UIXEditableValue.VALIDATE_EMPTY_FIELDS";
121 
122   /** -=matzew=- According to http://wiki.java.net/bin/view/Projects/Jsf2MR1ChangeLog" target="alexandria_uri">http://wiki.java.net/bin/view/Projects/Jsf2MR1ChangeLog
123    * this constant will be made public on UIInput with JSF 2.1. For now we have to have
124    * it here as a private one...
125    **/
126   static private final String JSF_SPEC_EMPTY_VALUES_AS_NULL_PARAM_NAME =
127     "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
128 
129   // our own cache key...
130   static public final String TRINIDAD_EMPTY_VALUES_AS_NULL_PARAM_NAME =
131     "org.apache.myfaces.trinidad.UIXEditableValue.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
132 
133   /**
134    * Convenience method to reset this component's value to an
135    * uninitialized state, by resetting the local value and
136    * submitted values to null (ensuring that {@link #isLocalValueSet}
137    * is false), and setting "valid" to true.
138    */
139   public void resetValue()
140   {
141     setValue(null);
142     setSubmittedValue(null);
143     setLocalValueSet(false);
144     setValid(true);
145   }
146 
147 
148   // ----------------------------------------------------- Validators Methods
149 
150 
151 
152   public void addValidator(Validator validator)
153   {
154     if (validator == null)
155       throw new NullPointerException();
156 
157     getFacesBean().addEntry(VALIDATORS_KEY, validator);
158   }
159 
160 
161   public Validator[] getValidators()
162   {
163     return (Validator[]) getFacesBean().getEntries(VALIDATORS_KEY,
164                                                    Validator.class);
165   }
166 
167   public void removeValidator(Validator validator)
168   {
169     getFacesBean().removeEntry(VALIDATORS_KEY, validator);
170   }
171 
172 
173   /**
174    */
175   public void validate(FacesContext context)
176   {
177     if (context == null)
178       throw new NullPointerException();
179 
180     // Submitted value == null means "the component was not submitted
181     // at all";  validation should not continue
182 
183     Object submittedValue = getSubmittedValue();
184     if (submittedValue == null)
185       return;
186 
187     // From the SPEC:
188     // If the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL context parameter value is
189     // true (ignoring case), and getSubmittedValue() returns a zero-length String call
190     // setSubmittedValue(null) and continue processing using null as the current submitted value
191     //
192     // TODO: -> SPEC ISSUE (matzew)  setSubmittedValue(null) is wrong, so we do not follow the spec here...
193     if (shouldInterpretEmptyStringSubmittedValuesAsNull(context) && _isEmptyString(submittedValue))
194     {
195       submittedValue = null;
196     }
197 
198     Object newValue = null;
199     try
200     {
201       newValue = getConvertedValue(context, submittedValue);
202     }
203     catch (ConverterException ce)
204     {
205       _addConversionErrorMessage(context, ce, submittedValue);
206       setValid(false);
207     }
208 
209     validateValue(context, newValue);
210 
211     // If our value is valid, store the new value, erase the
212     // "submitted" value, and emit a ValueChangeEvent if appropriate
213     if (isValid())
214     {
215       Object previous = getValue();
216       setSubmittedValue(null);
217       if (compareValues(previous, newValue))
218       {
219         setValue(newValue);
220         queueEvent(new ValueChangeEvent(this, previous, newValue));
221       }
222     }
223   }
224 
225 
226   /**
227    * In addition to to the default
228    * {@link javax.faces.component.UIComponent#broadcast}
229    * processing, pass the {@link ValueChangeEvent} being broadcast to the
230    * method referenced by <code>valueChangeListener</code> (if any).
231    *
232    * @param event {@link FacesEvent} to be broadcast
233    *
234    * @exception AbortProcessingException Signal the JavaServer Faces
235    *  implementation that no further processing on the current event
236    *  should be performed
237    * @exception IllegalArgumentException if the implementation class
238    *  of this {@link FacesEvent} is not supported by this component
239    * @exception NullPointerException if <code>event</code> is
240    * <code>null</code>
241    */
242   @Override
243   public void broadcast(FacesEvent event)
244         throws AbortProcessingException
245   {
246     // Perform standard superclass processing
247     super.broadcast(event);
248 
249     if (event instanceof ValueChangeEvent)
250     {
251       broadcastToMethodBinding(event, getValueChangeListener());
252     }
253   }
254 
255 
256   /**
257    * In addition to the standard <code>processDecodes</code> behavior
258    * inherited from {@link UIXComponentBase}, calls
259    * <code>validate()</code> if the the <code>immediate</code>
260    * property is true.  Iif the component is invalid afterwards or
261    * a <code>RuntimeException</code> is thrown, calls
262    * {@link FacesContext#renderResponse}.
263    */
264   @Override
265   public void processDecodes(FacesContext context)
266   {
267     if (!isValid())
268     {
269       // An exception could occur during normal bean attribute level
270       // validation in update_model phase. When it happens, the component
271       // will have an invalid local value, and LOCAL_VALUE_SET remains
272       // true since we want the invalid value to be shown to end user
273       // to make corrections. But we don't want the invalid state affects
274       // the next request, so we clear the local value and LOCAL_VALUE_SET
275       // property here. While on the other hand, we should not clear the
276       // state when the component is valid, to avoid accidentally clearing
277       // data that other components might depend on.
278 
279       setValue(null);
280       setLocalValueSet(false);
281     }
282     setValid(true);
283 
284     // Skip processing if our rendered flag is false
285     if (!isRendered())
286       return;
287 
288     RequestContext requestContext = RequestContext.getCurrentInstance();
289     requestContext.pushCurrentComponent(context, this);
290     pushComponentToEL(context, this);
291     try
292     {
293       super.processDecodes(context);
294 
295       if (isImmediate())
296         _executeValidate(context);
297     }
298     finally
299     {
300       popComponentFromEL(context);
301       requestContext.popCurrentComponent(context, this);
302     }
303   }
304 
305   @Override
306   public void processUpdates(FacesContext context)
307   {
308     // Skip processing if our rendered flag is false
309     if (!isRendered())
310       return;
311 
312     RequestContext requestContext = RequestContext.getCurrentInstance();
313     requestContext.pushCurrentComponent(context, this);
314     pushComponentToEL(context, this);
315     try
316     {
317       super.processUpdates(context);
318 
319       // Process this component itself
320       updateModel(context);
321     }
322     finally
323     {
324       popComponentFromEL(context);
325       requestContext.popCurrentComponent(context, this);
326     }
327 
328     if (!isValid())
329     {
330       context.renderResponse();
331     }
332   }
333 
334   @Override
335   public void processValidators(FacesContext context)
336   {
337     // Skip processing if our rendered flag is false
338     if (!isRendered())
339       return;
340 
341     RequestContext requestContext = RequestContext.getCurrentInstance();
342     requestContext.pushCurrentComponent(context, this);
343     pushComponentToEL(context, this);
344     try
345     {
346       super.processValidators(context);
347 
348       if (!isImmediate())
349         _executeValidate(context);
350     }
351     finally
352     {
353       popComponentFromEL(context);
354       requestContext.popCurrentComponent(context, this);
355     }
356   }
357   /**
358    * Adds a ValueUpdatedListener to the component to handle ValueUpdatedEvent which is queued 
359    * within updateModel method.
360    */
361   public void addValueUpdatedListener(ValueUpdatedListener listener) 
362   {
363     super.addFacesListener(listener);
364   }
365   /**
366    * Removes a ValueUpdatedListener from the component.
367    */
368   public void removeValueUpdatedListener(ValueUpdatedListener listener) 
369   {
370     super.removeFacesListener(listener);
371   }
372 
373   // TODO Better error messages when update model fails.
374   public void updateModel(FacesContext context)
375   {
376     if (context == null)
377       throw new NullPointerException();
378 
379     if (!isValid() || !isLocalValueSet())
380       return;
381 
382     ValueExpression expression = getFacesBean().getValueExpression(VALUE_KEY);
383     if (expression == null)
384       return;
385 
386     try
387     {
388       Object localValue = getLocalValue();
389       expression.setValue(context.getELContext(), localValue);
390       setValue(null);
391       setLocalValueSet(false);
392       (new ValueUpdatedEvent(this)).queue();
393       if (_LOG.isFiner())
394       {
395         _LOG.finer("Wrote value {0} to model {1} in component {2}",
396                    new Object[]{localValue,
397                                 expression.getExpressionString(),
398                                 this});
399       }
400     }
401     catch (RuntimeException e)
402     {
403       // exceptions at this point can occur during normal
404       // bean attribute level validation:
405       if (_LOG.isFine())
406       {
407         _LOG.fine("Error updating expression ({0})",
408                     expression.getExpressionString());
409         _LOG.fine(e);
410       }
411 
412       setValid(false);
413 
414       // don't report the exception if the exception is a Reportable instance and tells so
415       boolean shouldReportMessage = (e instanceof Reportable) ?
416                                     ((Reportable) e).shouldReportMessage() :
417                                     true;
418 
419       if (shouldReportMessage)
420       {
421         FacesMessage message = MessageFactory.getMessage(e);
422         message = _wrapMessage(message);
423         context.addMessage(getClientId(context), message);
424       }
425     }
426   }
427 
428   /**
429    */
430   @SuppressWarnings("unchecked")
431   protected void validateValue(FacesContext context, Object newValue)
432   {
433     if (!isValid())
434       return;
435 
436     // If our value is empty, check the required property
437     boolean isEmpty = isEmpty(newValue);
438     if (isEmpty && isRequired())
439     {
440       FacesMessage message = _getRequiredFacesMessage(context);
441       context.addMessage(getClientId(context), message);
442       setValid(false);
443     }
444 
445     // If our value is not empty, OR we should do empty field validation, call all validators
446     if (!isEmpty || shouldValidateEmptyFields(context))
447     {
448       Iterator<Validator> validators = (Iterator<Validator>)getFacesBean().entries(VALIDATORS_KEY);
449       while (validators.hasNext())
450       {
451         Validator validator = validators.next();
452         try
453         {
454           validator.validate(context, this, newValue);
455         }
456         catch (ValidatorException ve)
457         {
458           // If the validator throws an exception, we're
459           // invalid, and we need to add a message
460           setValid(false);
461           FacesMessage message = ve.getFacesMessage();
462           if (message != null)
463           {
464             message.setSeverity(FacesMessage.SEVERITY_ERROR);
465             message = _wrapMessage(message);
466             context.addMessage(getClientId(context), message);
467           }
468         }
469       }
470 
471       MethodBinding validatorBinding = getValidator();
472       if (validatorBinding != null)
473       {
474         try
475         {
476           validatorBinding.invoke(context,
477                                   new Object[] { context, this, newValue});
478         }
479         catch (EvaluationException ee)
480         {
481           Throwable cause = ee.getCause();
482           if (cause instanceof ValidatorException)
483           {
484             ValidatorException ve = (ValidatorException) cause;
485 
486             // If the validator throws an exception, we're
487             // invalid, and we need to add a message
488             setValid(false);
489             FacesMessage message = ve.getFacesMessage();
490             if (message != null)
491             {
492               message.setSeverity(FacesMessage.SEVERITY_ERROR);
493               message = _wrapMessage(message);
494               context.addMessage(getClientId(context), message);
495             }
496           }
497           else
498           {
499             // Otherwise, rethrow the EvaluationException
500             throw ee;
501           }
502         }
503       }
504     }
505   }
506 
507 
508   protected String getRequiredMessageKey()
509   {
510     return REQUIRED_MESSAGE_ID;
511   }
512 
513   /**
514    *
515    */
516   protected Object getConvertedValue(
517     FacesContext context,
518     Object       submittedValue) throws ConverterException
519   {
520     Renderer renderer = getRenderer(context);
521     Object newValue = null;
522 
523     if (_LOG.isFine())
524     {
525       _LOG.fine("Converting from " + submittedValue + "(" +
526                 submittedValue.getClass() + ")");
527     }
528 
529     if (renderer != null)
530     {
531       newValue = renderer.getConvertedValue(context, this,
532                                             submittedValue);
533       if (_LOG.isFine())
534       {
535         _LOG.fine("Renderer " + renderer + " returned value " + newValue + "(" +
536                   ((newValue != null) ? newValue.getClass().getName() : "null") + ")");
537       }
538     }
539     else if (submittedValue instanceof String)
540     {
541       // If there's no Renderer, and we've got a String,
542       // run it through the Converter (if any)
543       Converter converter = _getConverterWithType(context);
544       if (converter != null)
545       {
546         newValue = converter.getAsObject(context, this,
547                                          (String) submittedValue);
548       }
549       else
550       {
551         newValue = submittedValue;
552       }
553     }
554     else
555     {
556       newValue = submittedValue;
557     }
558 
559     return newValue;
560   }
561 
562  /**
563    * <p>Return <code>true</code> if the new value is different from the
564    * previous value.</p>
565    *
566    * @param previous old value of this component (if any)
567    * @param value new value of this component (if any)
568    */
569   protected boolean compareValues(Object previous, Object value)
570   {
571     // handle cases where previous value was empty
572     if (previous == null || "".equals(previous)) // bug 4268807
573       return !(value == null || "".equals(value));
574 
575     boolean isNotEqual = !previous.equals(value);
576 
577     // Handle objects whose comparable() implementation is inconsistent with equals().
578     // if not equal we will also check compareTo if the data implements Comparable.
579     // An example of why we need this is for a case where the data is bigdecimal,
580     // because bigdecimal remembers formatting information like scale. So 2.0 is not equal to 2.00
581     // in bigdecimal, but when you use compareTo 2.0 and 2.00 are equal.
582     // See Issue TRINIDAD-1489 for test case
583     if (isNotEqual && value instanceof Comparable && previous.getClass().equals(value.getClass()))
584     {
585       int compareTo = ((Comparable)previous).compareTo(value);
586       isNotEqual = (compareTo != 0);
587     }
588 
589     return isNotEqual;
590   }
591 
592   /**
593    * <p>Return <code>true</code> if the value is empty.</p>
594    */
595   protected boolean isEmpty(Object value)
596   {
597     if (value == null)
598       return true;
599 
600     return ((value instanceof String) &&
601             (((String) value).trim().length() == 0));
602   }
603 
604   /**
605    * <p>Return <code>true</code> if the value is an empty <code>String</code>.</p>
606    */
607   private boolean _isEmptyString(Object value)
608   {
609     return ((value instanceof String) && (((String) value).length() == 0));
610   }
611 
612   /**
613    * Checks if the <code>validate()</code> should interpret an empty
614    * submitted value should be handle as <code>NULL</code>
615    *
616    * @return a (cached) boolean to identify the interpretation as null
617    */
618   public static boolean shouldInterpretEmptyStringSubmittedValuesAsNull(FacesContext context)
619   {
620     ExternalContext ec = context.getExternalContext();
621     Boolean interpretEmptyStringAsNull = (Boolean)ec.getApplicationMap().get(TRINIDAD_EMPTY_VALUES_AS_NULL_PARAM_NAME);
622 
623     // not yet cached...
624     if (interpretEmptyStringAsNull == null)
625     {
626       // parses the web.xml to get the "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL" value
627       String param = ec.getInitParameter(JSF_SPEC_EMPTY_VALUES_AS_NULL_PARAM_NAME);
628 
629       // evaluate the context parameter
630       interpretEmptyStringAsNull = "true".equalsIgnoreCase(param);
631 
632       // cache the parsed value
633       ec.getApplicationMap().put(TRINIDAD_EMPTY_VALUES_AS_NULL_PARAM_NAME, interpretEmptyStringAsNull);
634     }
635 
636     return interpretEmptyStringAsNull;
637   }
638 
639   /**
640    * Checks if the <code>validateValue()</code> should handle
641    * empty field validation (part of BeanValidation and JSF 2.0).
642    *
643    * @return a (cached) boolean to identify empty field validation
644    */
645   public static boolean shouldValidateEmptyFields(FacesContext context)
646   {
647     ExternalContext ec = context.getExternalContext();
648     Boolean shouldValidateEmptyFields = (Boolean)ec.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
649 
650     // not yet cached...
651     if (shouldValidateEmptyFields == null)
652     {
653       // From the JSF 2.0 specification:
654       // The implementation must obtain the init parameter Map  from the ExternalContext and inspect the value
655       // for the key given by the value of the symbolic constant VALIDATE_EMPTY_FIELDS_PARAM_NAME.
656       String param = ec.getInitParameter(UIInput.VALIDATE_EMPTY_FIELDS_PARAM_NAME);
657 
658       // If there is no value under that key, use the same key and look in the
659       // application map from the ExternalContext.
660       if (param == null)
661       {
662         param = (String) ec.getApplicationMap().get(UIInput.VALIDATE_EMPTY_FIELDS_PARAM_NAME);
663       }
664 
665       // null means the same as auto (see SPEC on page 11-5)
666       if (param == null)
667       {
668         param = "auto";
669       }
670       else
671       {
672         // The environment variables are case insensitive...
673         param = param.toLowerCase();
674       }
675 
676       if (param.equals("auto") && _isBeanValidationAvailable(context))
677       {
678         shouldValidateEmptyFields = Boolean.TRUE;
679       }
680       else
681       {
682         // "true".equalsIgnoreCase(param) is faster than Boolean.valueOf()
683         shouldValidateEmptyFields = "true".equalsIgnoreCase(param);
684       }
685 
686       // cache the parsed value
687       ec.getApplicationMap().put(VALIDATE_EMPTY_FIELDS_PARAM_NAME, shouldValidateEmptyFields);
688     }
689 
690     return shouldValidateEmptyFields;
691   }
692 
693   /**
694    * This boolean indicates if Bean Validation is present.
695    *
696    * @return a (cached) boolean to identify if bean validation is present
697    */
698   private static boolean _isBeanValidationAvailable(FacesContext context)
699   {
700     ExternalContext ec = context.getExternalContext();
701     Boolean couldLoadBeanValidationAPI = (Boolean) ec.getApplicationMap().get(TRINIDAD_BEAN_VALIDATION_AVAILABLE);
702 
703     // not yet cached...
704     if (couldLoadBeanValidationAPI == null)
705     {
706       try
707       {
708         couldLoadBeanValidationAPI = Boolean.valueOf(ClassLoaderUtils.loadClass("javax.validation.Validation") != null);
709 
710         if (couldLoadBeanValidationAPI)
711         {
712           try
713           {
714             // Trial-error approach to check for Bean Validation impl existence.
715             Validation.buildDefaultValidatorFactory().getValidator();
716           }
717           catch (Exception validationException)
718           {
719             // From section 3.5.6.2 of the spec 
720             // "If the BeanValidator is used an no ValidatorFactory can be retrieved, a FacesException is raised. " 
721             // Only BeanValidator needs to throw a FacesException, in this case just log the message
722             // and behave as if bean validation is disabled
723             _LOG.warning("VALIDATOR_FACTORY_UNAVAILABLE", validationException.getMessage());
724             couldLoadBeanValidationAPI = Boolean.FALSE;
725           }
726         }
727       }
728       catch (ClassNotFoundException cnfe)
729       {
730         // SPEC section 3.5.6.2:
731         // if a Bean Validation provider is not present, bean validation is disabled
732         // TODO need a better warning (i18n) here, which has more information
733         _LOG.warning("A Bean Validation provider is not present, therefore bean validation is disabled");
734         couldLoadBeanValidationAPI = Boolean.FALSE;
735       }
736 
737       // cache the parsed value
738       ec.getApplicationMap().put(TRINIDAD_BEAN_VALIDATION_AVAILABLE, couldLoadBeanValidationAPI);
739     }
740 
741     return couldLoadBeanValidationAPI;
742   }
743 
744   /**
745    * Executes validation logic.
746    */
747   private void _executeValidate(FacesContext context)
748   {
749     Application application = context.getApplication();
750     application.publishEvent(context, PreValidateEvent.class, UIComponent.class, this);
751     try
752     {
753       validate(context);
754     }
755     catch (RuntimeException e)
756     {
757       context.renderResponse();
758       throw e;
759     }
760     finally
761     {
762       application.publishEvent(context, PostValidateEvent.class, UIComponent.class, this);
763     }
764 
765     if (!isValid())
766     {
767       context.renderResponse();
768     }
769   }
770 
771 
772   // We currently use 'label' for the validation failed message
773   private Object _getLabel()
774   {
775     Object o = getAttributes().get("label");
776     if (o == null)
777       o = getValueExpression("label");
778 
779     return o;
780   }
781 
782   private Object _getRequiredMessageDetail()
783   {
784     Object o = getAttributes().get("requiredMessageDetail");
785       if (o == null)
786        o = getValueExpression("requiredMessageDetail");
787 
788     return o;
789   }
790 
791   private FacesMessage _getRequiredFacesMessage(FacesContext context)
792   {
793     Object customMessageDetail = _getRequiredMessageDetail();
794     FacesMessage message;
795     Object label = _getLabel();
796 
797     // if message is null then a custom message was not set.
798     message = MessageFactory.getMessage(context,
799                                         getRequiredMessageKey(),
800                                         customMessageDetail,
801                                         new Object[]{label},
802                                         label);
803     return message;
804   }
805 
806   private void _addConversionErrorMessage(
807      FacesContext       context,
808      ConverterException ce,
809      Object             value)
810   {
811     FacesMessage message = ce.getFacesMessage();
812 
813     if (message == null)
814     {
815       Object label = _getLabel();
816       message = MessageFactory.getMessage(context,
817                                           CONVERSION_MESSAGE_ID,
818                                           new Object[]{label, value,
819                                                        ce.getMessage()},
820                                           label);
821     }
822     else
823     {
824       message = _wrapMessage(message);
825     }
826 
827     context.addMessage(getClientId(context), message);
828   }
829 
830   private Converter _getConverterWithType(FacesContext context)
831   {
832     Converter converter = getConverter();
833     if (converter != null)
834     {
835       return converter;
836     }
837 
838     ValueExpression valueExpression = getValueExpression("value");
839     if (valueExpression == null)
840     {
841       return null;
842     }
843 
844     Class<?> converterType = valueExpression.getType(context.getELContext());
845     // if converterType is null, String, or Object, assume
846     // no conversion is needed
847     if (converterType == null ||
848         converterType == String.class ||
849         converterType == Object.class)
850     {
851       return null;
852     }
853 
854     // if getType returns a type for which we support a default
855     // conversion, acquire an appropriate converter instance.
856     try
857     {
858       Application application = context.getApplication();
859       return application.createConverter(converterType);
860     }
861     catch (Exception e)
862     {
863       return null;
864     }
865   }
866 
867   private FacesMessage _wrapMessage(FacesMessage original)
868   {
869     if (original instanceof LabeledFacesMessage)
870       return original;
871 
872     return new FacesMessageWrapper(original, _getLabel());
873   }
874 
875   static private final TrinidadLogger _LOG =
876     TrinidadLogger.createTrinidadLogger(UIXEditableValue.class);
877 
878   /**
879    * Gets whether the value is converted and validated immediately in the Apply Request Values phase, or is handled in the Process Validators phase, the default.  By default, values are converted and validated together in the Process Validators phase.  However, if you need access to the value of a component during Apply Request Values - for example, if you need to get the value from an actionListener on an immediate commandButton - then setting this to "immediate" makes that possible.
880    *
881    * @return  the new immediate value
882    */
883   final public boolean isImmediate()
884   {
885     return ComponentUtils.resolveBoolean(getProperty(IMMEDIATE_KEY), false);
886   }
887 
888   /**
889    * Sets whether the value is converted and validated immediately in the Apply Request Values phase, or is handled in the Process Validators phase, the default.  By default, values are converted and validated together in the Process Validators phase.  However, if you need access to the value of a component during Apply Request Values - for example, if you need to get the value from an actionListener on an immediate commandButton - then setting this to "immediate" makes that possible.
890    * 
891    * @param immediate  the new immediate value
892    */
893   final public void setImmediate(boolean immediate)
894   {
895     setProperty(IMMEDIATE_KEY, immediate ? Boolean.TRUE : Boolean.FALSE);
896   }
897 
898   /**
899    * Gets whether the component's value is currently valid
900    *
901    * @return  the new valid value
902    */
903   final public boolean isValid()
904   {
905     return ComponentUtils.resolveBoolean(getProperty(VALID_KEY), true);
906   }
907 
908   /**
909    * Sets whether the component's value is currently valid
910    * 
911    * @param valid  the new valid value
912    */
913   final public void setValid(boolean valid)
914   {
915     setProperty(VALID_KEY, valid ? Boolean.TRUE : Boolean.FALSE);
916   }
917 
918   /**
919    * Gets whether a non-null, non-empty value must be entered.  If false, validators will not be executed when the value is null or empty.
920    *
921    * @return  the new required value
922    */
923   final public boolean isRequired()
924   {
925     return ComponentUtils.resolveBoolean(getProperty(REQUIRED_KEY), false);
926   }
927 
928   /**
929    * Sets whether a non-null, non-empty value must be entered.  If false, validators will not be executed when the value is null or empty.
930    * 
931    * @param required  the new required value
932    */
933   final public void setRequired(boolean required)
934   {
935     setProperty(REQUIRED_KEY, required ? Boolean.TRUE : Boolean.FALSE);
936   }
937 
938   /**
939    * Gets whether a local value is currently set.  If false, values are being retrieved from any attached ValueBinding
940    *
941    * @return  the new localValueSet value
942    */
943   final public boolean isLocalValueSet()
944   {
945     return ComponentUtils.resolveBoolean(getProperty(LOCAL_VALUE_SET_KEY));
946   }
947 
948   /**
949    * Sets whether a local value is currently set.  If false, values are being retrieved from any attached ValueBinding
950    * 
951    * @param localValueSet  the new localValueSet value
952    */
953   final public void setLocalValueSet(boolean localValueSet)
954   {
955     setProperty(LOCAL_VALUE_SET_KEY, localValueSet ? Boolean.TRUE : Boolean.FALSE);
956   }
957 
958   /**
959    * Gets the current submitted value.  This value,
960    * if non-null, is set by the Renderer to store a possibly invalid value
961    * for later conversion or redisplay, and has not yet been converted
962    * into the proper type for this component instance.   This method
963    * should only be used by the decode() and validate() method
964    * of this component, or its corresponding Renderer;  however, user code
965    * may manually set it to null to erase any submitted value.
966    *
967    * @return  the new submittedValue value
968    */
969   final public Object getSubmittedValue()
970   {
971     return getProperty(SUBMITTED_VALUE_KEY);
972   }
973 
974   /**
975    * Sets the current submitted value.  This value,
976    * if non-null, is set by the Renderer to store a possibly invalid value
977    * for later conversion or redisplay, and has not yet been converted
978    * into the proper type for this component instance.   This method
979    * should only be used by the decode() and validate() method
980    * of this component, or its corresponding Renderer;  however, user code
981    * may manually set it to null to erase any submitted value.
982    * 
983    * @param submittedValue  the new submittedValue value
984    */
985   final public void setSubmittedValue(Object submittedValue)
986   {
987     setProperty(SUBMITTED_VALUE_KEY, (submittedValue));
988   }
989 
990   /**
991    * Gets a method reference to a validator method
992    *
993    * @return  the new validator value
994    */
995   final public MethodBinding getValidator()
996   {
997     return (MethodBinding)getProperty(VALIDATOR_KEY);
998   }
999 
1000   /**
1001    * Sets a method reference to a validator method
1002    * 
1003    * @param validator  the new validator value
1004    */
1005   final public void setValidator(MethodBinding validator)
1006   {
1007     setProperty(VALIDATOR_KEY, (validator));
1008   }
1009 
1010   /**
1011    * Gets a method reference to a value change listener
1012    *
1013    * @return  the new valueChangeListener value
1014    */
1015   final public MethodBinding getValueChangeListener()
1016   {
1017     return (MethodBinding)getProperty(VALUE_CHANGE_LISTENER_KEY);
1018   }
1019 
1020   /**
1021    * Sets a method reference to a value change listener
1022    * 
1023    * @param valueChangeListener  the new valueChangeListener value
1024    */
1025   final public void setValueChangeListener(MethodBinding valueChangeListener)
1026   {
1027     setProperty(VALUE_CHANGE_LISTENER_KEY, (valueChangeListener));
1028   }
1029 
1030   /**
1031    * Gets <html> the message to be displayed, if 'required' validation fails.
1032    *              <p>
1033    *               <strong>
1034    *                Parameters:
1035    *               </strong>
1036    *               <ul>
1037    *                <li>
1038    *                 {0} the label that identifies the component
1039    *                </li>
1040    *               </ul>
1041    *              </p></html>
1042    *
1043    * @return  the new requiredMessageDetail value
1044    */
1045   final public String getRequiredMessageDetail()
1046   {
1047     return ComponentUtils.resolveString(getProperty(REQUIRED_MESSAGE_DETAIL_KEY));
1048   }
1049 
1050   /**
1051    * Sets <html> the message to be displayed, if 'required' validation fails.
1052    *              <p>
1053    *               <strong>
1054    *                Parameters:
1055    *               </strong>
1056    *               <ul>
1057    *                <li>
1058    *                 {0} the label that identifies the component
1059    *                </li>
1060    *               </ul>
1061    *              </p></html>
1062    * 
1063    * @param requiredMessageDetail  the new requiredMessageDetail value
1064    */
1065   final public void setRequiredMessageDetail(String requiredMessageDetail)
1066   {
1067     setProperty(REQUIRED_MESSAGE_DETAIL_KEY, (requiredMessageDetail));
1068   }
1069 
1070   /**
1071    * Adds a valueChange listener.
1072    *
1073    * @param listener  the valueChange listener to add
1074    */
1075   final public void addValueChangeListener(
1076     ValueChangeListener listener)
1077   {
1078     addFacesListener(listener);
1079   }
1080 
1081   /**
1082    * Removes a valueChange listener.
1083    *
1084    * @param listener  the valueChange listener to remove
1085    */
1086   final public void removeValueChangeListener(
1087     ValueChangeListener listener)
1088   {
1089     removeFacesListener(listener);
1090   }
1091 
1092   /**
1093    * Returns an array of attached valueChange listeners.
1094    *
1095    * @return  an array of attached valueChange listeners.
1096    */
1097   final public ValueChangeListener[] getValueChangeListeners()
1098   {
1099     return (ValueChangeListener[])getFacesListeners(ValueChangeListener.class);
1100   }
1101 
1102   @Override
1103   public String getFamily()
1104   {
1105     return COMPONENT_FAMILY;
1106   }
1107 
1108   @Override
1109   protected FacesBean.Type getBeanType()
1110   {
1111     return TYPE;
1112   }
1113 
1114   /**
1115    * Construct an instance of the UIXEditableValue.
1116    */
1117   protected UIXEditableValue(
1118     String rendererType
1119     )
1120   {
1121     super(rendererType);
1122   }
1123 
1124   /**
1125    * Construct an instance of the UIXEditableValue.
1126    */
1127   protected UIXEditableValue()
1128   {
1129     this(null);
1130   }
1131 
1132   static
1133   {
1134     TYPE.lockAndRegister("org.apache.myfaces.trinidad.EditableValue","org.apache.myfaces.trinidad.EditableValue");
1135   }
1136 }