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