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  import javax.faces.validator.Validator;
26  import javax.faces.validator.ValidatorException;
27  
28  import org.apache.myfaces.trinidad.bean.FacesBean;
29  import org.apache.myfaces.trinidad.bean.PropertyKey;
30  import org.apache.myfaces.trinidad.util.ComponentUtils;
31  import org.apache.myfaces.trinidad.util.MessageFactory;
32  
33  /**
34   * <p>Implementation for <code>java.lang.Double</code> values.</p>
35   *
36   */
37  public class DoubleRangeValidator extends javax.faces.validator.DoubleRangeValidator
38  {
39    
40    public static final String VALIDATOR_ID = "org.apache.myfaces.trinidad.DoubleRange";
41  
42    /**
43     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
44     * to be created if the maximum value check fails.  The message format
45     * string for this message may optionally include <code>{0}</code>,
46     * <code>{1}</code> and <code>{3}</code> placeholders,
47     * which will be replaced by user input, component label and configured
48     * maximum value.</p>
49     */
50    public static final String MAXIMUM_MESSAGE_ID =
51        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.MAXIMUM";
52  
53    /**
54     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
55     * to be created if the minimum value check fails.  The message format
56     * string for this message may optionally include <code>{0}</code>,
57     * <code>{1}</code> and <code>{2}</code> placeholders, which will be replaced
58     * by user input, component label and configured minimum value.</p>
59     */
60    public static final String MINIMUM_MESSAGE_ID =
61        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.MINIMUM";
62  
63  
64    /**
65     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
66     * to be created if the maximum or minimum value check fails, and both
67     * the maximum and minimum values for this validator have been set.
68     * The message format string for this message may optionally include
69     * <code>{0}</code>, <code>{1}</code>, <code>{2}</code> and <code>{3}</code>
70     * placeholders, which will be replaced by user input, component label,
71     * configured minimum value and configured maximum value.</p>
72     */
73    public static final String NOT_IN_RANGE_MESSAGE_ID =
74        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.NOT_IN_RANGE";
75  
76    
77    /**
78     * Construct a {@link Validator} with no preconfigured limits.
79     */
80    public DoubleRangeValidator()
81    {
82      super();
83    }
84  
85    /**
86     * Construct a {@link Validator} with the specified preconfigured
87     * limit.
88     *
89     * @param maximum Maximum value to allow
90     */
91    public DoubleRangeValidator(double maximum) 
92    {
93      super(maximum);
94    }
95  
96    /**
97     * Construct a {@link Validator} with the specified preconfigured
98     * limits.
99     *
100    * @param maximum Maximum value to allow
101    * @param minimum Minimum value to allow
102    *
103    */
104   public DoubleRangeValidator(double maximum, double minimum)
105   {
106     super(maximum, minimum);
107   }
108   
109   /**
110    * Return the maximum value to be enforced by this {@link
111    * Validator} or null if it has not been
112    * set.
113    */
114   @Override
115   public double getMaximum()
116   {
117     Object maxDouble = _facesBean.getProperty(_MAXIMUM_KEY);
118     if(maxDouble == null)
119       maxDouble = Double.MAX_VALUE;
120     return ComponentUtils.resolveDouble(maxDouble);
121   }
122 
123   /**
124    * Set the maximum value to be enforced by this {@link Validator}.
125    *
126    * @param maximum The new maximum value
127    *
128    */
129   @Override
130   public void setMaximum(double maximum)
131   {
132     super.setMaximum(maximum);
133     _facesBean.setProperty(_MAXIMUM_KEY, Double.valueOf(maximum));
134   }
135 
136 
137   /**
138    * Return the minimum value to be enforced by this {@link
139    * Validator}, or null if it has not been
140    * set.
141    */
142   @Override
143   public double getMinimum()
144   {
145     Object minDouble = _facesBean.getProperty(_MINIMUM_KEY);
146     if(minDouble == null)
147       minDouble = Double.MIN_VALUE;
148     return ComponentUtils.resolveDouble(minDouble);
149   }
150 
151   /**
152    * Set the minimum value to be enforced by this {@link Validator}.
153    *
154    * @param minimum The new minimum value
155    *
156    */
157   @Override
158   public void setMinimum(double minimum)
159   {
160     super.setMinimum(minimum);
161     _facesBean.setProperty(_MINIMUM_KEY, Double.valueOf(minimum));
162   }
163 
164   /**
165    * <p>Custom error message to be used, for creating detail part of the
166    * {@link FacesMessage}, when input value exceeds the maximum value set.</p>
167    * Overrides detail message identified by message id {@link #MAXIMUM_MESSAGE_ID}
168    * @param maximumMessageDetail Custom error message.
169    */
170   public void setMessageDetailMaximum(String maximumMessageDetail)
171   {
172     _facesBean.setProperty(_MAXIMUM_MESSAGE_DETAIL_KEY, maximumMessageDetail);
173   }
174 
175   /**
176    *  <p>Return custom detail error message that was set for creating {@link FacesMessage},
177    *  for cases where input value exceeds the <code>maximum</code> value set.</p>
178    * @return Custom error message.
179    * @see #setMessageDetailMaximum(String)
180    */
181   public String getMessageDetailMaximum()
182   {
183     Object maxMsgDet = _facesBean.getProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
184     return ComponentUtils.resolveString(maxMsgDet);
185   }
186 
187   /**
188    * <p>Custom error message to be used, for creating detail part of the
189    * {@link FacesMessage}, when input value is less the set
190    * <code>minimum</code> value.</p>
191    * Overrides detail message identified by message id {@link #MINIMUM_MESSAGE_ID}
192    * @param minimumMessageDetail Custom error message.
193    */
194   public void setMessageDetailMinimum(String minimumMessageDetail)
195   {
196     _facesBean.setProperty(_MINIMUM_MESSAGE_DETAIL_KEY, minimumMessageDetail);
197   }
198 
199   /**
200    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
201    * for cases where, input value is less than the <code>minimum</code> value set.</p>
202    * @return Custom error message.
203    * @see #setMessageDetailMinimum(String)
204    */
205   public String getMessageDetailMinimum()
206   {
207     Object minMsgDet = _facesBean.getProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
208     return ComponentUtils.resolveString(minMsgDet);
209   }
210 
211   /**
212    * <p>Custom error message to be used, for creating detail part of the
213    * {@link FacesMessage}, when input value is not with in the range,
214    * when <code>minimum</code> and <code>maximum</code> is set.</p>
215    * Overrides detail message identified by message id {@link #NOT_IN_RANGE_MESSAGE_ID}
216    * @param notInRangeMessageDetail Custom error message.
217    */
218   public void setMessageDetailNotInRange(String notInRangeMessageDetail)
219   {
220     _facesBean.setProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY, notInRangeMessageDetail);
221   }
222 
223   /**
224    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
225    * for cases where, input value exceeds the <code>maximum</code> value and is
226    * less than the <code>minimum</code> value set.</p>
227    * @return Custom error message.
228    * @see #setMessageDetailNotInRange(String)
229    */
230   public String getMessageDetailNotInRange()
231   {
232     Object notInRngMsg = _facesBean.getProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY);
233     return ComponentUtils.resolveString(notInRngMsg);
234   }
235 
236   /**
237    * <p>Custom hint maximum message.</p>
238    * Overrides default hint message
239    * @param hintMaximum Custom hint message.
240    */
241   public void setHintMaximum(String hintMaximum)
242   {
243     _facesBean.setProperty(_HINT_MAXIMUM_KEY, hintMaximum);
244   }
245 
246   /**
247    * <p>Return custom hint maximum message.</p>
248    * @return Custom hint message.
249    * @see  #setHintMaximum(String)
250    */
251   public String getHintMaximum()
252   {
253     Object obj = _facesBean.getProperty(_HINT_MAXIMUM_KEY);
254     return ComponentUtils.resolveString(obj);
255   }
256 
257   /**
258    * <p>Custom hint minimum message.</p>
259    * Overrides default hint message
260    * @param hintMinimum Custom hint message.
261    */
262   public void setHintMinimum(String hintMinimum)
263   {
264     _facesBean.setProperty(_HINT_MINIMUM_KEY, hintMinimum);
265   }
266 
267   /**
268    * <p>Return custom hint minimum message.</p>
269    * @return Custom hint message.
270    * @see  #setHintMinimum(String)
271    */
272   public String getHintMinimum()
273   {
274     Object obj = _facesBean.getProperty(_HINT_MINIMUM_KEY);
275     return ComponentUtils.resolveString(obj);
276   }
277 
278   /**
279    * <p>Custom hint notInRange message.</p>
280    * Overrides default hint message
281    * @param hintNotInRange Custom hint message.
282    */
283   public void setHintNotInRange(String hintNotInRange)
284   {
285     _facesBean.setProperty(_HINT_NOT_IN_RANGE, hintNotInRange);
286   }
287 
288   /**
289    * <p>Return custom hint notInRange message.</p>
290    * @return Custom hint message.
291    * @see  #setHintNotInRange
292    */
293   public String getHintNotInRange()
294   {
295     Object obj = _facesBean.getProperty(_HINT_NOT_IN_RANGE);
296     return ComponentUtils.resolveString(obj);
297   }
298 
299   @Override
300   public void validate(
301     FacesContext context,
302     UIComponent component,
303     Object value
304     ) throws ValidatorException
305   {
306     try
307     {
308       super.validate(context, component, value);
309     }
310     catch (ValidatorException ve)
311     {
312          
313       if (value != null && value instanceof Number)
314       {
315         double doubleValue = ((Number)value).doubleValue(); 
316         
317         double min = getMinimum();
318         double max = getMaximum();
319         
320         if (doubleValue > max)
321         {
322           if (min != Double.MIN_VALUE)//the default...
323           {
324              throw new ValidatorException
325                         (_getNotInRangeMessage(context, component, value, min, max));
326           }
327           else
328           {
329              throw new ValidatorException
330                         (_getMaximumMessage(context, component, value, max));
331           }
332         }
333 
334         if (doubleValue < min)
335         {
336           if (max != Double.MAX_VALUE)//the default...
337           {
338             throw new ValidatorException
339                         (_getNotInRangeMessage(context, component, value, min, max));
340           }
341           else
342           {
343             FacesMessage msg = _getMinimumMessage(context, component, value, min);
344             throw new ValidatorException(msg);
345           }
346         }
347       }
348       else
349       {
350         throw ve;
351       }
352     }     
353   }
354 
355   //  StateHolder Methods
356   @Override
357   public Object saveState(FacesContext context)
358   {
359     return _facesBean.saveState(context);
360   }
361 
362 
363   @Override
364   public void restoreState(FacesContext context, Object state)
365   {
366     _facesBean.restoreState(context, state);
367   }
368 
369   /**
370    * <p>Set the {@link ValueBinding} used to calculate the value for the
371    * specified attribute if any.</p>
372    *
373    * @param name Name of the attribute for which to set a {@link ValueBinding}
374    * @param binding The {@link ValueBinding} to set, or <code>null</code>
375    *  to remove any currently set {@link ValueBinding}
376    *
377    * @exception NullPointerException if <code>name</code>
378    *  is <code>null</code>
379    * @exception IllegalArgumentException if <code>name</code> is not a valid
380    *            attribute of this validator
381    */
382   public void setValueBinding(String name, ValueBinding binding)
383   {
384     ValidatorUtils.setValueBinding(_facesBean, name, binding) ;
385   }
386 
387   /**
388    * <p>Return the {@link ValueBinding} used to calculate the value for the
389    * specified attribute name, if any.</p>
390    *
391    * @param name Name of the attribute or property for which to retrieve a
392    *  {@link ValueBinding}
393    *
394    * @exception NullPointerException if <code>name</code>
395    *  is <code>null</code>
396    * @exception IllegalArgumentException if <code>name</code> is not a valid
397    * attribute of this validator
398    */
399   public ValueBinding getValueBinding(String name)
400   {
401     return ValidatorUtils.getValueBinding(_facesBean, name);
402   }
403   
404   @Override
405   public boolean isTransient()
406   {
407     return (_transientValue);
408   }
409 
410 
411   @Override
412   public void setTransient(boolean transientValue)
413   {
414     _transientValue = transientValue;
415   }
416 
417   private FacesMessage _getNotInRangeMessage(
418       FacesContext context,
419       UIComponent component,
420       Object value,
421       Object min,
422       Object max)
423     {
424       Object msg   = _getRawNotInRangeMessageDetail();
425       Object label = ValidatorUtils.getComponentLabel(component);
426 
427       Object[] params = {label, value, min, max};
428 
429       return MessageFactory.getMessage(context, NOT_IN_RANGE_MESSAGE_ID,
430                                         msg, params, component);
431     }
432 
433 
434     
435     private Object _getRawNotInRangeMessageDetail()
436     {
437       return _facesBean.getRawProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY);
438     }
439 
440 
441     private FacesMessage _getMaximumMessage(
442       FacesContext context,
443       UIComponent component,
444       Object value,
445       Object max)
446     {
447 
448       Object msg   = _getRawMaximumMessageDetail();
449       Object label = ValidatorUtils.getComponentLabel(component);
450 
451       Object[] params = {label, value, max};
452 
453       return MessageFactory.getMessage(context,
454                                        MAXIMUM_MESSAGE_ID,
455                                        msg,
456                                        params,
457                                        component);
458     }
459 
460     private Object _getRawMaximumMessageDetail()
461     {
462       return _facesBean.getRawProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
463     }
464 
465     private FacesMessage _getMinimumMessage(
466       FacesContext context,
467       UIComponent component,
468       Object value,
469       Object min)
470     {
471       Object msg      = _getRawMinimumMessageDetail();
472       Object label    = ValidatorUtils.getComponentLabel(component);
473 
474       Object[] params = {label, value, min};
475 
476       return MessageFactory.getMessage(context, MINIMUM_MESSAGE_ID,
477                                        msg, params, component);
478     }
479 
480     private Object _getRawMinimumMessageDetail()
481     {
482       return _facesBean.getRawProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
483     }
484 
485   private static final FacesBean.Type _TYPE = new FacesBean.Type();
486 
487   private static final PropertyKey _MINIMUM_KEY =
488     _TYPE.registerKey("minimum", Double.class);
489 
490   private static final PropertyKey _MAXIMUM_KEY =
491     _TYPE.registerKey("maximum", Double.class);
492 
493   private static final PropertyKey _MAXIMUM_MESSAGE_DETAIL_KEY =
494     _TYPE.registerKey("messageDetailMaximum", String.class);
495 
496   private static final PropertyKey _MINIMUM_MESSAGE_DETAIL_KEY =
497     _TYPE.registerKey("messageDetailMinimum", String.class);
498 
499   private static final PropertyKey _NOT_IN_RANGE_MESSAGE_DETAIL_KEY =
500     _TYPE.registerKey("messageDetailNotInRange", String.class);
501 
502   private static final PropertyKey  _HINT_MAXIMUM_KEY =
503     _TYPE.registerKey("hintMaximum", String.class);
504 
505   private static final PropertyKey  _HINT_MINIMUM_KEY =
506     _TYPE.registerKey("hintMinimum", String.class);
507 
508   private static final PropertyKey  _HINT_NOT_IN_RANGE =
509     _TYPE.registerKey("hintNotInRange", String.class);
510 
511   private FacesBean _facesBean = ValidatorUtils.getFacesBean(_TYPE);
512 
513   private boolean _transientValue = false;
514 }