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