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