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