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