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 org.apache.myfaces.trinidad.validator;
20  
21  import javax.el.ValueExpression;
22  
23  import javax.faces.application.FacesMessage;
24  import javax.faces.component.UIComponent;
25  import javax.faces.context.FacesContext;
26  import javax.faces.el.ValueBinding;
27  
28  import javax.faces.validator.Validator;
29  import javax.faces.validator.ValidatorException;
30  
31  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
32  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
33  import org.apache.myfaces.trinidad.bean.FacesBean;
34  import org.apache.myfaces.trinidad.bean.PropertyKey;
35  import org.apache.myfaces.trinidad.util.ComponentUtils;
36  import org.apache.myfaces.trinidad.util.MessageFactory;
37  
38  /**
39   * <p>Implementation for length of <code>java.lang.String</code> values.</p>
40   *
41   */
42  @JSFValidator(configExcluded=true)
43  public class LengthValidator extends javax.faces.validator.LengthValidator
44  {
45    
46    public static final String VALIDATOR_ID = "org.apache.myfaces.trinidad.Length";
47  
48    /**
49     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
50     * to be created if the maximum length check fails.  The message format
51     * string for this message may optionally include <code>{0}</code>,
52     * <code>{1}</code> and <code>{3}</code> placeholders,
53     * which will be replaced by user input, component label and configured
54     * maximum length.</p>
55     */
56    public static final String MAXIMUM_MESSAGE_ID =
57        "org.apache.myfaces.trinidad.validator.LengthValidator.MAXIMUM";
58  
59    /**
60     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
61     * to be created if the minimum length check fails.  The message format
62     * string for this message may optionally include <code>{0}</code>,
63     * <code>{1}</code> and <code>{2}</code> placeholders, which will be replaced
64     * by user input, component label and configured minimum length.</p>
65     */
66    public static final String MINIMUM_MESSAGE_ID =
67        "org.apache.myfaces.trinidad.validator.LengthValidator.MINIMUM";
68  
69  
70    /**
71     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
72     * to be created if the maximum or minimum length check fails, and both
73     * the maximum and minimum lengths for this validator have been set.
74     * The message format string for this message may optionally include
75     * <code>{0}</code>, <code>{1}</code>, <code>{2}</code> and <code>{3}</code>
76     * placeholders, which will be replaced by user input, component label,
77     * configured minimum length and configured maximum length.</p>
78     */
79    public static final String NOT_IN_RANGE_MESSAGE_ID =
80        "org.apache.myfaces.trinidad.validator.LengthValidator.NOT_IN_RANGE";
81  
82    /**
83     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
84     * to be created if the maximum and minimum lengths are the same,
85     * and the length check fails, and both
86     * the maximum and minimum values for this validator have been set.
87     * The message format string for this message may optionally include
88     * <code>{0}</code>, <code>{1}</code>, <code>{2}</code>
89     * placeholders, which will be replaced by user input, component label,
90     * configured minimum/maximum length.</p>
91     */
92    public static final String EXACT_MESSAGE_ID =
93        "org.apache.myfaces.trinidad.validator.LengthValidator.EXACT";
94  
95    
96    /**
97     * Construct a {@link Validator} with no preconfigured limits.
98     */
99    public LengthValidator()
100   {
101     super();
102   }
103 
104   /**
105    * Construct a {@link Validator} with the specified preconfigured
106    * limit.
107    *
108    * @param maximum Maximum length to allow
109    */
110   public LengthValidator(int maximum)
111   {
112     super(maximum);
113   }
114 
115   /**
116    * Construct a {@link Validator} with the specified preconfigured
117    * limits.
118    *
119    * @param maximum Maximum length to allow
120    * @param minimum Minimum length to allow
121    *
122    */
123   public LengthValidator(int maximum, int minimum)
124   {
125     super(maximum, minimum);
126   }
127   
128   /**
129    * Return the maximum length to be enforced by this {@link
130    * Validator} or null if it has not been
131    * set.
132    */
133   @JSFProperty
134   @Override
135   public int getMaximum()
136   {
137     Object maxInt = _facesBean.getProperty(_MAXIMUM_KEY);
138     return ComponentUtils.resolveInteger(maxInt);
139   }
140 
141   /**
142    * Set the maximum length to be enforced by this {@link Validator}.
143    *
144    * @param maximum The new maximum length
145    *
146    */
147   @Override
148   public void setMaximum(int maximum)
149   {
150     _facesBean.setProperty(_MAXIMUM_KEY, Integer.valueOf(maximum));
151   }
152 
153 
154   /**
155    * Return the minimum length to be enforced by this {@link
156    * Validator}, or null if it has not been
157    * set.
158    */
159   @JSFProperty
160   @Override
161   public int getMinimum()
162   {
163     Object minInt = _facesBean.getProperty(_MINIMUM_KEY);
164     return ComponentUtils.resolveInteger(minInt);
165   }
166 
167   /**
168    * Set the minimum length to be enforced by this {@link Validator}.
169    *
170    * @param minimum The new minimum length
171    *
172    */
173   @Override
174   public void setMinimum(int minimum)
175   {
176     _facesBean.setProperty(_MINIMUM_KEY, Integer.valueOf(minimum));
177   }
178 
179   /**
180    * <p>Custom error message to be used, for creating detail part of the
181    * {@link FacesMessage}, when input length exceeds the maximum length set.</p>
182    * Overrides detail message identified by message id {@link #MAXIMUM_MESSAGE_ID}
183    * @param maximumMessageDetail Custom error message.
184    */
185   public void setMessageDetailMaximum(String maximumMessageDetail)
186   {
187     _facesBean.setProperty(_MAXIMUM_MESSAGE_DETAIL_KEY, maximumMessageDetail);
188   }
189 
190   /**
191    *  <p>Return custom detail error message that was set for creating {@link FacesMessage},
192    *  for cases where input length exceeds the <code>maximum</code> length set.</p>
193    * @return Custom error message.
194    * @see #setMessageDetailMaximum(String)
195    */
196   @JSFProperty
197   public String getMessageDetailMaximum()
198   {
199     Object maxMsgDet = _facesBean.getProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
200     return ComponentUtils.resolveString(maxMsgDet);
201   }
202 
203   /**
204    * <p>Custom error message to be used, for creating detail part of the
205    * {@link FacesMessage}, when input length is less the set
206    * <code>minimum</code> length.</p>
207    * Overrides detail message identified by message id {@link #MINIMUM_MESSAGE_ID}
208    * @param minimumMessageDetail Custom error message.
209    */
210   public void setMessageDetailMinimum(String minimumMessageDetail)
211   {
212     _facesBean.setProperty(_MINIMUM_MESSAGE_DETAIL_KEY, minimumMessageDetail);
213   }
214 
215   /**
216    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
217    * for cases where, input length is less than the <code>minimum</code> length set.</p>
218    * @return Custom error message.
219    * @see #setMessageDetailMinimum(String)
220    */
221   @JSFProperty
222   public String getMessageDetailMinimum()
223   {
224     Object minMsgDet = _facesBean.getProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
225     return ComponentUtils.resolveString(minMsgDet);
226   }
227 
228   /**
229    * <p>Custom error message to be used, for creating detail part of the
230    * {@link FacesMessage}, when input length is not with in the range,
231    * when <code>minimum</code> and <code>maximum</code> is set.</p>
232    * Overrides detail message identified by message id {@link #NOT_IN_RANGE_MESSAGE_ID}
233    * @param notInRangeMessageDetail Custom error message.
234    */
235   public void setMessageDetailNotInRange(String notInRangeMessageDetail)
236   {
237     _facesBean.setProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY, notInRangeMessageDetail);
238   }
239 
240   /**
241    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
242    * for cases where, input length exceeds the <code>maximum</code> length and is
243    * less than the <code>minimum</code> length set.</p>
244    * @return Custom error message.
245    * @see #setMessageDetailNotInRange(String)
246    */
247   @JSFProperty
248   public String getMessageDetailNotInRange()
249   {
250     Object notInRngMsg = _facesBean.getProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY);
251     return ComponentUtils.resolveString(notInRngMsg);
252   }
253 
254 
255   /**
256    * <p>Custom error message to be used, for creating detail part of the
257    * {@link FacesMessage}, 
258    * for cases where the maximum and minimum lengths are the same, and
259    * the input length does not match.
260    * Overrides detail message identified by message id {@link #EXACT_MESSAGE_ID}
261    * @param exactMessageDetail Custom error message.
262    */
263   public void setMessageDetailExact(String exactMessageDetail)
264   {
265     _facesBean.setProperty(_EXACT_MESSAGE_DETAIL_KEY, exactMessageDetail);
266   }
267 
268   /**
269    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
270    * for cases where the maximum and minimum lengths are the same, and
271    * the input length does not match.</p>
272    * @return Custom error message.
273    * @see #setMessageDetailExact(String)
274    */
275   @JSFProperty
276   public String getMessageDetailExact()
277   {
278     Object msg = _facesBean.getProperty(_EXACT_MESSAGE_DETAIL_KEY);
279     return ComponentUtils.resolveString(msg);
280   }
281 
282   
283   /**
284    * <p>Custom hint maximum message.</p>
285    * Overrides default hint message
286    * @param hintMaximum Custom hint message.
287    */
288   public void setHintMaximum(String hintMaximum)
289   {
290     _facesBean.setProperty(_HINT_MAXIMUM_KEY, hintMaximum);
291   }
292 
293   /**
294    * <p>Return custom hint maximum message.</p>
295    * @return Custom hint message.
296    * @see  #setHintMaximum(String)
297    */
298   @JSFProperty(tagExcluded=true)
299   public String getHintMaximum()
300   {
301     Object obj = _facesBean.getProperty(_HINT_MAXIMUM_KEY);
302     return ComponentUtils.resolveString(obj);
303   }
304 
305   /**
306    * <p>Custom hint minimum message.</p>
307    * Overrides default hint message
308    * @param hintMinimum Custom hint message.
309    */
310   public void setHintMinimum(String hintMinimum)
311   {
312     _facesBean.setProperty(_HINT_MINIMUM_KEY, hintMinimum);
313   }
314 
315   /**
316    * <p>Return custom hint minimum message.</p>
317    * @return Custom hint message.
318    * @see  #setHintMinimum(String)
319    */
320   @JSFProperty(tagExcluded=true)
321   public String getHintMinimum()
322   {
323     Object obj = _facesBean.getProperty(_HINT_MINIMUM_KEY);
324     return ComponentUtils.resolveString(obj);
325   }
326 
327   /**
328    * <p>Custom hint notInRange message.</p>
329    * Overrides default hint message
330    * @param hintNotInRange Custom hint message.
331    */
332   public void setHintNotInRange(String hintNotInRange)
333   {
334     _facesBean.setProperty(_HINT_NOT_IN_RANGE, hintNotInRange);
335   }
336 
337   /**
338    * <p>Return custom hint notInRange message.</p>
339    * @return Custom hint message.
340    * @see  #setHintNotInRange
341    */
342   @JSFProperty(tagExcluded=true)
343   public String getHintNotInRange()
344   {
345     Object obj = _facesBean.getProperty(_HINT_NOT_IN_RANGE);
346     return ComponentUtils.resolveString(obj);
347   }
348 
349 
350   /**
351    * <p>Custom hint exact message.</p>
352    * Overrides default hint message
353    * @param hintExact Custom hint message.
354    */
355   public void setHintExact(String hintExact)
356   {
357     _facesBean.setProperty(_HINT_EXACT, hintExact);
358   }
359 
360   /**
361    * <p>Return custom hint exact message.</p>
362    * @return Custom hint message.
363    * @see  #setHintExact
364    */
365   @JSFProperty(tagExcluded=true)
366   public String getHintExact()
367   {
368     Object obj = _facesBean.getProperty(_HINT_EXACT);
369     return ComponentUtils.resolveString(obj);
370   }
371   
372   @Override
373   public void validate(
374     FacesContext context,
375     UIComponent component,
376     Object value
377     ) throws ValidatorException
378   {   
379     if (isDisabled())
380       return;
381     
382     if ((context == null) || (component == null))
383     {
384       throw new NullPointerException();
385     }
386 
387     if(value != null)
388     {
389       int max = getMaximum();
390       int min = getMinimum();
391       int length = value instanceof String ?
392         ((String)value).length() : value.toString().length();
393 
394       // range validation
395       if(isMaximumSet() && isMinimumSet())
396       {
397         if(length<min || length>max)
398         {
399           throw new ValidatorException(
400             _getNotInRangeMessage(context, component, value, min, max));
401         }
402       }
403       // too short
404       if(isMinimumSet())
405       {
406         if (length < min)
407         {
408           throw new ValidatorException(
409             _getMinimumMessage(context, component, value, min));
410         }
411       }
412       // too long
413       if(isMaximumSet())
414       {
415         if (length > max)
416         {
417           throw new ValidatorException(
418             _getMaximumMessage(context, component, value, max));
419         }
420       }
421     }
422   }
423 
424   //  StateHolder Methods
425   @Override
426   public Object saveState(FacesContext context)
427   {
428     return _facesBean.saveState(context);
429   }
430 
431 
432   @Override
433   public void restoreState(FacesContext context, Object state)
434   {
435     _facesBean.restoreState(context, state);
436   }
437 
438   /**
439    * <p>Set the {@link ValueExpression} used to calculate the value for the
440    * specified attribute if any.</p>
441    *
442    * @param name Name of the attribute for which to set a {@link ValueExpression}
443    * @param expression The {@link ValueExpression} to set, or <code>null</code>
444    *  to remove any currently set {@link ValueExpression}
445    *
446    * @exception NullPointerException if <code>name</code>
447    *  is <code>null</code>
448    * @exception IllegalArgumentException if <code>name</code> is not a valid
449    *            attribute of this converter
450    */
451   public void setValueExpression(String name, ValueExpression expression)
452   {
453     ValidatorUtils.setValueExpression(_facesBean, name, expression) ;
454   }
455 
456 
457   /**
458    * <p>Return the {@link ValueExpression} used to calculate the value for the
459    * specified attribute name, if any.</p>
460    *
461    * @param name Name of the attribute or property for which to retrieve a
462    *  {@link ValueExpression}
463    *
464    * @exception NullPointerException if <code>name</code>
465    *  is <code>null</code>
466    * @exception IllegalArgumentException if <code>name</code> is not a valid
467    * attribute of this converter
468    */
469   public ValueExpression getValueExpression(String name)
470   {
471     return ValidatorUtils.getValueExpression(_facesBean, name);
472   }
473 
474 
475   /**
476    * <p>Set the {@link ValueBinding} used to calculate the value for the
477    * specified attribute if any.</p>
478    *
479    * @param name Name of the attribute for which to set a {@link ValueBinding}
480    * @param binding The {@link ValueBinding} to set, or <code>null</code>
481    *  to remove any currently set {@link ValueBinding}
482    *
483    * @exception NullPointerException if <code>name</code>
484    *  is <code>null</code>
485    * @exception IllegalArgumentException if <code>name</code> is not a valid
486    *            attribute of this validator
487    * @deprecated
488    */
489   public void setValueBinding(String name, ValueBinding binding)
490   {
491     ValidatorUtils.setValueBinding(_facesBean, name, binding) ;
492   }
493 
494   /**
495    * <p>Return the {@link ValueBinding} used to calculate the value for the
496    * specified attribute name, if any.</p>
497    *
498    * @param name Name of the attribute or property for which to retrieve a
499    *  {@link ValueBinding}
500    *
501    * @exception NullPointerException if <code>name</code>
502    *  is <code>null</code>
503    * @exception IllegalArgumentException if <code>name</code> is not a valid
504    * attribute of this validator
505    * @deprecated
506    */
507   public ValueBinding getValueBinding(String name)
508   {
509     return ValidatorUtils.getValueBinding(_facesBean, name);
510   }
511   
512   @JSFProperty(istransient=true, tagExcluded=true)
513   @Override
514   public boolean isTransient()
515   {
516     return (_transientValue);
517   }
518 
519 
520   @Override
521   public void setTransient(boolean transientValue)
522   {
523     _transientValue = transientValue;
524   }
525 
526   @Override
527   public boolean equals(Object otherObj) 
528   {
529     if (!(otherObj instanceof LengthValidator)) 
530     {
531       return false;
532     }
533     
534     LengthValidator other = (LengthValidator) otherObj;
535     
536     return ((this.getMaximum() == other.getMaximum())
537             && (this.getMinimum() == other.getMinimum())
538             && (this.isMinimumSet() == other.isMinimumSet())
539             && (this.isMaximumSet() == other.isMaximumSet())
540             && (this.isDisabled() == other.isDisabled())
541             && (this.isTransient() == other.isTransient()));
542   }
543 
544   @Override
545   public int hashCode() 
546   {
547     int result = 17;
548     Object msgDetExact      =  getMessageDetailExact();
549     Object maxMsgDet        =  getMessageDetailMaximum();
550     Object minMsgDet        =  getMessageDetailMinimum();
551     Object notInRangeMsgDet =  getMessageDetailNotInRange();
552     
553     result = 37 * result + (isDisabled() ? 1 : 0);    
554     result = 37 * result + (isTransient() ? 0 : 1);
555     result = 37 * result + (isMaximumSet() ? 0 : 1);
556     result = 37 * result + (isMinimumSet() ? 0 : 1);
557     result = 37 * result + Integer.valueOf(getMinimum()).hashCode();
558     result = 37 * result + Integer.valueOf(getMaximum()).hashCode();
559     result = 37 * result + ( msgDetExact == null ? 0 : msgDetExact.hashCode());
560     result = 37 * result + ( maxMsgDet == null ? 0 : maxMsgDet.hashCode());
561     result = 37 * result + ( minMsgDet == null ? 0 : minMsgDet.hashCode());
562     result = 37 * result + ( notInRangeMsgDet == null ? 0 : notInRangeMsgDet.hashCode());
563     
564     return result;
565   }
566 
567   /**
568     * Return whether it is disabled.
569     * @return true if it's disabled and false if it's enabled. 
570     */ 
571   public void setDisabled(boolean isDisabled)
572   {
573     _facesBean.setProperty(_DISABLED_KEY, Boolean.valueOf(isDisabled));
574   }
575 
576   /**
577     * Return whether it is disabled.
578     * @return true if it's disabled and false if it's enabled. 
579     */  
580   public boolean isDisabled()
581   {
582     Boolean disabled = (Boolean) _facesBean.getProperty(_DISABLED_KEY);
583     
584     return (disabled != null) ? disabled.booleanValue() : false;
585   }  
586 
587   protected boolean isMaximumSet()
588   {
589     return _facesBean.getProperty(_MAXIMUM_KEY) != null;
590   }
591 
592   protected boolean isMinimumSet()
593   {
594     return _facesBean.getProperty(_MINIMUM_KEY) != null;
595   }
596 
597   private FacesMessage _getNotInRangeMessage(
598       FacesContext context,
599       UIComponent component,
600       Object value,
601       Object min,
602       Object max)
603   {
604     if (min.equals(max))
605       return _getExactMessage(context, component, value, min);
606 
607     Object msg   = _getRawNotInRangeMessageDetail();
608     Object label = ValidatorUtils.getComponentLabel(component);
609     
610     Object[] params = {label, value, min, max};
611     
612     return MessageFactory.getMessage(context, NOT_IN_RANGE_MESSAGE_ID,
613                                      msg, params, component);
614   }
615 
616   
617   private Object _getRawNotInRangeMessageDetail()
618   {
619     return _facesBean.getRawProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY);
620   }
621 
622 
623   private FacesMessage _getExactMessage(
624       FacesContext context,
625       UIComponent component,
626       Object value,
627       Object minMax)
628   {
629     Object msg   = _getRawExactMessageDetail();
630     Object label = ValidatorUtils.getComponentLabel(component);
631     
632     Object[] params = {label, value, minMax};
633     
634     return MessageFactory.getMessage(context, EXACT_MESSAGE_ID,
635                                      msg, params, component);
636   }
637   
638 
639   private Object _getRawExactMessageDetail()
640   {
641     return _facesBean.getRawProperty(_EXACT_MESSAGE_DETAIL_KEY);
642   }
643   
644   
645   private FacesMessage _getMaximumMessage(
646     FacesContext context,
647     UIComponent component,
648     Object value,
649     Object max)
650   {
651     
652     Object msg   = _getRawMaximumMessageDetail();
653     Object label = ValidatorUtils.getComponentLabel(component);
654     
655     Object[] params = {label, value, max};
656     
657     return MessageFactory.getMessage(context,
658                                      MAXIMUM_MESSAGE_ID,
659                                      msg,
660                                      params,
661                                      component);
662   }
663   
664   private Object _getRawMaximumMessageDetail()
665   {
666     return _facesBean.getRawProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
667   }
668   
669   private FacesMessage _getMinimumMessage(
670     FacesContext context,
671     UIComponent component,
672     Object value,
673     Object min)
674   {
675     Object msg      = _getRawMinimumMessageDetail();
676     Object label    = ValidatorUtils.getComponentLabel(component);
677     
678     Object[] params = {label, value, min};
679     
680     return MessageFactory.getMessage(context, MINIMUM_MESSAGE_ID,
681                                      msg, params, component);
682   }
683   
684   private Object _getRawMinimumMessageDetail()
685   {
686     return _facesBean.getRawProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
687   }
688   
689   private static final FacesBean.Type _TYPE = new FacesBean.Type();
690  
691   // Default is false
692   private static final PropertyKey _DISABLED_KEY =
693     _TYPE.registerKey("disabled",
694                       Boolean.class,
695                       Boolean.FALSE);
696   
697   // Default is zero, not MIN_VALUE
698   private static final PropertyKey _MINIMUM_KEY =
699     _TYPE.registerKey("minimum",
700                       Integer.class,
701                       // Don't rely on autoboxing: there's a method overload
702                       Integer.valueOf(0));
703 
704   // FIXME: the default of the superclass is 0, not MAX_VALUE
705   private static final PropertyKey _MAXIMUM_KEY =
706     _TYPE.registerKey("maximum", Integer.class,
707                       // Don't rely on autoboxing: there's a method overload
708                       Integer.valueOf(0));
709 
710   private static final PropertyKey _MAXIMUM_MESSAGE_DETAIL_KEY =
711     _TYPE.registerKey("messageDetailMaximum", String.class);
712 
713   private static final PropertyKey _MINIMUM_MESSAGE_DETAIL_KEY =
714     _TYPE.registerKey("messageDetailMinimum", String.class);
715 
716   private static final PropertyKey _NOT_IN_RANGE_MESSAGE_DETAIL_KEY =
717     _TYPE.registerKey("messageDetailNotInRange", String.class);
718 
719   private static final PropertyKey _EXACT_MESSAGE_DETAIL_KEY =
720     _TYPE.registerKey("messageDetailExact", String.class);
721 
722   private static final PropertyKey  _HINT_MAXIMUM_KEY =
723     _TYPE.registerKey("hintMaximum", String.class);
724 
725   private static final PropertyKey  _HINT_MINIMUM_KEY =
726     _TYPE.registerKey("hintMinimum", String.class);
727 
728   private static final PropertyKey  _HINT_NOT_IN_RANGE =
729     _TYPE.registerKey("hintNotInRange", String.class);
730 
731   private static final PropertyKey  _HINT_EXACT =
732     _TYPE.registerKey("hintExact", String.class);
733 
734   private FacesBean _facesBean = ValidatorUtils.getFacesBean(_TYPE);
735 
736   private boolean _transientValue = false;
737 }