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  import javax.faces.validator.Validator;
28  import javax.faces.validator.ValidatorException;
29  
30  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
31  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
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.Double</code> values.</p>
40   *
41   */
42  @JSFValidator(configExcluded=true)
43  public class DoubleRangeValidator extends javax.faces.validator.DoubleRangeValidator
44  {
45    
46    public static final String VALIDATOR_ID = "org.apache.myfaces.trinidad.DoubleRange";
47  
48    /**
49     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
50     * to be created if the maximum value 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 value.</p>
55     */
56    public static final String MAXIMUM_MESSAGE_ID =
57        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.MAXIMUM";
58  
59    /**
60     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
61     * to be created if the minimum value 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 value.</p>
65     */
66    public static final String MINIMUM_MESSAGE_ID =
67        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.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 value check fails, and both
73     * the maximum and minimum values 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 value and configured maximum value.</p>
78     */
79    public static final String NOT_IN_RANGE_MESSAGE_ID =
80        "org.apache.myfaces.trinidad.validator.DoubleRangeValidator.NOT_IN_RANGE";
81  
82    /**
83     * <p>The message identifier of the FacesMessage to be created if
84     * the value cannot be converted
85     */
86    public static final String CONVERT_MESSAGE_ID =
87        "org.apache.myfaces.trinidad.convert.DoubleConverter.CONVERT";
88    
89    /**
90     * Construct a {@link Validator} with no preconfigured limits.
91     */
92    public DoubleRangeValidator()
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 DoubleRangeValidator(double 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 DoubleRangeValidator(double maximum, double 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   @JSFProperty
127   @Override
128   public double getMaximum()
129   {
130     Object maxDouble = _facesBean.getProperty(_MAXIMUM_KEY);
131     if(maxDouble == null)
132       maxDouble = _MAXIMUM_KEY.getDefault();
133     return ComponentUtils.resolveDouble(maxDouble);
134   }
135 
136   /**
137    * Set the maximum value to be enforced by this {@link Validator}.
138    *
139    * @param maximum The new maximum value
140    *
141    */
142   @Override
143   public void setMaximum(double maximum)
144   {
145     _facesBean.setProperty(_MAXIMUM_KEY, Double.valueOf(maximum));
146   }
147 
148 
149   /**
150    * Return the minimum value to be enforced by this {@link
151    * Validator}, or null if it has not been
152    * set.
153    */
154   @JSFProperty
155   @Override
156   public double getMinimum()
157   {
158     Object minDouble = _facesBean.getProperty(_MINIMUM_KEY);
159     if(minDouble == null)
160       minDouble = _MINIMUM_KEY.getDefault();
161     return ComponentUtils.resolveDouble(minDouble);
162   }
163 
164   /**
165    * Set the minimum value to be enforced by this {@link Validator}.
166    *
167    * @param minimum The new minimum value
168    *
169    */
170   @Override
171   public void setMinimum(double minimum)
172   {
173     _facesBean.setProperty(_MINIMUM_KEY, Double.valueOf(minimum));
174   }
175 
176   /**
177    * <p>Custom error message to be used, for creating detail part of the
178    * {@link FacesMessage}, when input value exceeds the maximum value set.</p>
179    * Overrides detail message identified by message id {@link #MAXIMUM_MESSAGE_ID}
180    * @param maximumMessageDetail Custom error message.
181    */
182   public void setMessageDetailMaximum(String maximumMessageDetail)
183   {
184     _facesBean.setProperty(_MAXIMUM_MESSAGE_DETAIL_KEY, maximumMessageDetail);
185   }
186 
187   /**
188    *  <p>Return custom detail error message that was set for creating {@link FacesMessage},
189    *  for cases where input value exceeds the <code>maximum</code> value set.</p>
190    * @return Custom error message.
191    * @see #setMessageDetailMaximum(String)
192    */
193   @JSFProperty
194   public String getMessageDetailMaximum()
195   {
196     Object maxMsgDet = _facesBean.getProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
197     return ComponentUtils.resolveString(maxMsgDet);
198   }
199 
200   /**
201    * <p>Custom error message to be used, for creating detail part of the
202    * {@link FacesMessage}, when input value is less the set
203    * <code>minimum</code> value.</p>
204    * Overrides detail message identified by message id {@link #MINIMUM_MESSAGE_ID}
205    * @param minimumMessageDetail Custom error message.
206    */
207   public void setMessageDetailMinimum(String minimumMessageDetail)
208   {
209     _facesBean.setProperty(_MINIMUM_MESSAGE_DETAIL_KEY, minimumMessageDetail);
210   }
211 
212   /**
213    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
214    * for cases where, input value is less than the <code>minimum</code> value set.</p>
215    * @return Custom error message.
216    * @see #setMessageDetailMinimum(String)
217    */
218   @JSFProperty
219   public String getMessageDetailMinimum()
220   {
221     Object minMsgDet = _facesBean.getProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
222     return ComponentUtils.resolveString(minMsgDet);
223   }
224 
225   /**
226    * <p>Custom error message to be used, for creating detail part of the
227    * {@link FacesMessage}, when input value is not with in the range,
228    * when <code>minimum</code> and <code>maximum</code> is set.</p>
229    * Overrides detail message identified by message id {@link #NOT_IN_RANGE_MESSAGE_ID}
230    * @param notInRangeMessageDetail Custom error message.
231    */
232   public void setMessageDetailNotInRange(String notInRangeMessageDetail)
233   {
234     _facesBean.setProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY, notInRangeMessageDetail);
235   }
236 
237   /**
238    * <p>Return custom detail error message that was set for creating {@link FacesMessage},
239    * for cases where, input value exceeds the <code>maximum</code> value and is
240    * less than the <code>minimum</code> value set.</p>
241    * @return Custom error message.
242    * @see #setMessageDetailNotInRange(String)
243    */
244   @JSFProperty
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    * <p>Custom hint maximum message.</p>
253    * Overrides default hint message
254    * @param hintMaximum Custom hint message.
255    */
256   public void setHintMaximum(String hintMaximum)
257   {
258     _facesBean.setProperty(_HINT_MAXIMUM_KEY, hintMaximum);
259   }
260 
261   /**
262    * <p>Return custom hint maximum message.</p>
263    * @return Custom hint message.
264    * @see  #setHintMaximum(String)
265    */
266   @JSFProperty(tagExcluded=true)
267   public String getHintMaximum()
268   {
269     Object obj = _facesBean.getProperty(_HINT_MAXIMUM_KEY);
270     return ComponentUtils.resolveString(obj);
271   }
272 
273   /**
274    * <p>Custom hint minimum message.</p>
275    * Overrides default hint message
276    * @param hintMinimum Custom hint message.
277    */
278   @JSFProperty(tagExcluded=true)
279   public void setHintMinimum(String hintMinimum)
280   {
281     _facesBean.setProperty(_HINT_MINIMUM_KEY, hintMinimum);
282   }
283 
284   /**
285    * <p>Return custom hint minimum message.</p>
286    * @return Custom hint message.
287    * @see  #setHintMinimum(String)
288    */
289   public String getHintMinimum()
290   {
291     Object obj = _facesBean.getProperty(_HINT_MINIMUM_KEY);
292     return ComponentUtils.resolveString(obj);
293   }
294 
295   /**
296    * <p>Custom hint notInRange message.</p>
297    * Overrides default hint message
298    * @param hintNotInRange Custom hint message.
299    */
300   public void setHintNotInRange(String hintNotInRange)
301   {
302     _facesBean.setProperty(_HINT_NOT_IN_RANGE, hintNotInRange);
303   }
304 
305   /**
306    * <p>Return custom hint notInRange message.</p>
307    * @return Custom hint message.
308    * @see  #setHintNotInRange
309    */
310   @JSFProperty(tagExcluded=true)
311   public String getHintNotInRange()
312   {
313     Object obj = _facesBean.getProperty(_HINT_NOT_IN_RANGE);
314     return ComponentUtils.resolveString(obj);
315   }
316 
317   @Override
318   public void validate(
319     FacesContext context,
320     UIComponent component,
321     Object value
322     ) throws ValidatorException
323   {
324     if (isDisabled())
325       return;
326     
327     if ((context == null) || (component == null))
328     {
329       throw new NullPointerException();
330     }
331 
332     if (value == null)
333     {
334       return;
335     }
336     
337     try
338     {
339       double doubleValue = _convertValueToDouble(value); 
340       double min = getMinimum();
341       double max = getMaximum();
342 
343       if(isMaximumSet() && isMinimumSet())
344       {
345         if(doubleValue < min || doubleValue > max)
346         {
347           throw new ValidatorException(
348             _getNotInRangeMessage(context, component, value, min, max));
349         }
350       }
351       
352       if(isMaximumSet())
353       {
354         if (doubleValue > max)
355         {
356           throw new ValidatorException(
357             _getMaximumMessage(context, component, value, max));
358         }
359       }
360       
361       if(isMinimumSet())
362       {
363         if (doubleValue < min)
364         {
365           throw new ValidatorException(
366             _getMinimumMessage(context, component, value, min));
367         }
368       }
369     }
370     catch (NumberFormatException nfe)
371     {
372       FacesMessage msg = _getNotCorrectType(context);
373       throw new ValidatorException(msg);
374     }
375   }
376 
377   //  StateHolder Methods
378   @Override
379   public Object saveState(FacesContext context)
380   {
381     return _facesBean.saveState(context);
382   }
383 
384 
385   @Override
386   public void restoreState(FacesContext context, Object state)
387   {
388     _facesBean.restoreState(context, state);
389   }
390 
391   /**
392    * <p>Set the {@link ValueExpression} used to calculate the value for the
393    * specified attribute if any.</p>
394    *
395    * @param name Name of the attribute for which to set a {@link ValueExpression}
396    * @param expression The {@link ValueExpression} to set, or <code>null</code>
397    *  to remove any currently set {@link ValueExpression}
398    *
399    * @exception NullPointerException if <code>name</code>
400    *  is <code>null</code>
401    * @exception IllegalArgumentException if <code>name</code> is not a valid
402    *            attribute of this converter
403    */
404   public void setValueExpression(String name, ValueExpression expression)
405   {
406     ValidatorUtils.setValueExpression(_facesBean, name, expression) ;
407   }
408 
409 
410   /**
411    * <p>Return the {@link ValueExpression} used to calculate the value for the
412    * specified attribute name, if any.</p>
413    *
414    * @param name Name of the attribute or property for which to retrieve a
415    *  {@link ValueExpression}
416    *
417    * @exception NullPointerException if <code>name</code>
418    *  is <code>null</code>
419    * @exception IllegalArgumentException if <code>name</code> is not a valid
420    * attribute of this converter
421    */
422   public ValueExpression getValueExpression(String name)
423   {
424     return ValidatorUtils.getValueExpression(_facesBean, name);
425   }
426 
427 
428   /**
429    * <p>Set the {@link ValueBinding} used to calculate the value for the
430    * specified attribute if any.</p>
431    *
432    * @param name Name of the attribute for which to set a {@link ValueBinding}
433    * @param binding The {@link ValueBinding} to set, or <code>null</code>
434    *  to remove any currently set {@link ValueBinding}
435    *
436    * @exception NullPointerException if <code>name</code>
437    *  is <code>null</code>
438    * @exception IllegalArgumentException if <code>name</code> is not a valid
439    *            attribute of this validator
440    * @deprecated
441    */
442   public void setValueBinding(String name, ValueBinding binding)
443   {
444     ValidatorUtils.setValueBinding(_facesBean, name, binding) ;
445   }
446 
447   /**
448    * <p>Return the {@link ValueBinding} used to calculate the value for the
449    * specified attribute name, if any.</p>
450    *
451    * @param name Name of the attribute or property for which to retrieve a
452    *  {@link ValueBinding}
453    *
454    * @exception NullPointerException if <code>name</code>
455    *  is <code>null</code>
456    * @exception IllegalArgumentException if <code>name</code> is not a valid
457    * attribute of this validator
458    * @deprecated
459    */
460   public ValueBinding getValueBinding(String name)
461   {
462     return ValidatorUtils.getValueBinding(_facesBean, name);
463   }
464   
465   @JSFProperty(istransient=true, tagExcluded=true)
466   @Override
467   public boolean isTransient()
468   {
469     return (_transientValue);
470   }
471 
472   @Override
473   public void setTransient(boolean transientValue)
474   {
475     _transientValue = transientValue;
476   }
477 
478   @Override
479   public boolean equals(Object otherObj) {
480     if (!(otherObj instanceof DoubleRangeValidator)) 
481     {
482       return false;
483     }
484       
485     DoubleRangeValidator other = (DoubleRangeValidator) otherObj;
486     
487     return ((this.getMaximum() == other.getMaximum())
488             && (this.getMinimum() == other.getMinimum())
489             && (this.isMaximumSet() == other.isMaximumSet())
490             && (this.isMinimumSet() == other.isMinimumSet())
491             && (this.isDisabled() == other.isDisabled())
492             && (this.isTransient() == other.isTransient()));
493   }
494 
495   @Override
496   public int hashCode() {
497     int result = 17;
498     Object maxMsgDet        =  getMessageDetailMaximum();
499     Object minMsgDet        =  getMessageDetailMinimum();
500     Object notInRangeMsgDet =  getMessageDetailNotInRange();
501     
502     result = result * 37 + (isDisabled() ? 1 : 0);
503     result = result * 37 + (isTransient() ? 1 : 0);
504     result = result * 37 + Double.valueOf(this.getMinimum()).hashCode(); 
505     result = result * 37 + Double.valueOf(this.getMaximum()).hashCode();
506     result = result * 37 + Boolean.valueOf(isMinimumSet()).hashCode();
507     result = result * 37 + Boolean.valueOf(isMaximumSet()).hashCode();
508     result = result * 37 + ( maxMsgDet == null ? 0 : maxMsgDet.hashCode());
509     result = result * 37 + ( minMsgDet == null ? 0 : minMsgDet.hashCode());
510     result = result * 37 + ( notInRangeMsgDet == null ? 0 : notInRangeMsgDet.hashCode());
511     
512     return result;
513   }
514 
515   /**
516     * Return whether it is disabled.
517     * @return true if it's disabled and false if it's enabled. 
518     */ 
519   public void setDisabled(boolean isDisabled)
520   {
521     _facesBean.setProperty(_DISABLED_KEY, Boolean.valueOf(isDisabled));
522   }
523 
524   /**
525     * Return whether it is disabled.
526     * @return true if it's disabled and false if it's enabled. 
527     */  
528   public boolean isDisabled()
529   {
530     Boolean disabled = (Boolean) _facesBean.getProperty(_DISABLED_KEY);
531     
532     return (disabled != null) ? disabled.booleanValue() : false;
533   }  
534 
535   protected boolean isMaximumSet()
536   {
537     return _facesBean.getProperty(_MAXIMUM_KEY) != null;
538   }
539 
540   protected boolean isMinimumSet()
541   {
542     return _facesBean.getProperty(_MINIMUM_KEY) != null;
543   }
544 
545   private FacesMessage _getNotCorrectType(
546     FacesContext context)
547   {
548     return MessageFactory.getMessage(context, CONVERT_MESSAGE_ID);
549   }
550 
551   /**
552    * Try to call doubleValue() from java.lang.Number. Since not all
553    * "number" implement java.lang.Number, we try to call string
554    * and parse the String representation of the "number". If that fails
555    * we aren't working with a number so we throw a NumberFormatException  
556    * 
557    * @param value the number value
558    * @return parsed number value
559    * @throws NumberFormatException if the value is not a number
560    */
561   private double _convertValueToDouble(Object value) throws NumberFormatException
562   {
563     if(value instanceof Number)
564     {
565       return ((Number)value).doubleValue();
566     }
567     else
568     {
569       return Double.parseDouble(value.toString());
570     }
571   }
572 
573   private FacesMessage _getNotInRangeMessage(
574     FacesContext context,
575     UIComponent component,
576     Object value,
577     Object min,
578     Object max)
579   {
580       Object msg   = _getRawNotInRangeMessageDetail();
581       Object label = ValidatorUtils.getComponentLabel(component);
582 
583       Object[] params = {label, value, min, max};
584 
585       return MessageFactory.getMessage(context, NOT_IN_RANGE_MESSAGE_ID,
586                                         msg, params, component);
587   }
588 
589 
590     
591     private Object _getRawNotInRangeMessageDetail()
592     {
593       return _facesBean.getRawProperty(_NOT_IN_RANGE_MESSAGE_DETAIL_KEY);
594     }
595 
596 
597     private FacesMessage _getMaximumMessage(
598       FacesContext context,
599       UIComponent component,
600       Object value,
601       Object max)
602     {
603 
604       Object msg   = _getRawMaximumMessageDetail();
605       Object label = ValidatorUtils.getComponentLabel(component);
606 
607       Object[] params = {label, value, max};
608 
609       return MessageFactory.getMessage(context,
610                                        MAXIMUM_MESSAGE_ID,
611                                        msg,
612                                        params,
613                                        component);
614     }
615 
616     private Object _getRawMaximumMessageDetail()
617     {
618       return _facesBean.getRawProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
619     }
620 
621     private FacesMessage _getMinimumMessage(
622       FacesContext context,
623       UIComponent component,
624       Object value,
625       Object min)
626     {
627       Object msg      = _getRawMinimumMessageDetail();
628       Object label    = ValidatorUtils.getComponentLabel(component);
629 
630       Object[] params = {label, value, min};
631 
632       return MessageFactory.getMessage(context, MINIMUM_MESSAGE_ID,
633                                        msg, params, component);
634     }
635 
636     private Object _getRawMinimumMessageDetail()
637     {
638       return _facesBean.getRawProperty(_MINIMUM_MESSAGE_DETAIL_KEY);
639     }
640 
641   private static final FacesBean.Type _TYPE = new FacesBean.Type();
642 
643   private static final PropertyKey _MINIMUM_KEY =
644     _TYPE.registerKey("minimum", Double.class,
645                                  // Don't rely on autoboxing: there's a method overload
646                                  Double.valueOf(Double.MIN_VALUE));
647 
648   private static final PropertyKey _MAXIMUM_KEY =
649     _TYPE.registerKey("maximum", Double.class,
650                                  // Don't rely on autoboxing: there's a method overload
651                                  Double.valueOf(Double.MAX_VALUE));
652 
653   private static final PropertyKey _MAXIMUM_MESSAGE_DETAIL_KEY =
654     _TYPE.registerKey("messageDetailMaximum", String.class);
655 
656   private static final PropertyKey _MINIMUM_MESSAGE_DETAIL_KEY =
657     _TYPE.registerKey("messageDetailMinimum", String.class);
658 
659   private static final PropertyKey _NOT_IN_RANGE_MESSAGE_DETAIL_KEY =
660     _TYPE.registerKey("messageDetailNotInRange", String.class);
661 
662   private static final PropertyKey  _HINT_MAXIMUM_KEY =
663     _TYPE.registerKey("hintMaximum", String.class);
664 
665   private static final PropertyKey  _HINT_MINIMUM_KEY =
666     _TYPE.registerKey("hintMinimum", String.class);
667 
668   private static final PropertyKey  _HINT_NOT_IN_RANGE =
669     _TYPE.registerKey("hintNotInRange", String.class);
670   
671   // Default is false
672   private static final PropertyKey _DISABLED_KEY =
673     _TYPE.registerKey("disabled", Boolean.class, Boolean.FALSE);
674 
675   private FacesBean _facesBean = ValidatorUtils.getFacesBean(_TYPE);
676 
677   private boolean _transientValue = false;
678 }