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 java.util.Calendar;
22  import java.util.Date;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.TimeZone;
29  
30  import javax.el.ValueExpression;
31  
32  import javax.faces.application.FacesMessage;
33  import javax.faces.component.StateHolder;
34  import javax.faces.component.UIComponent;
35  import javax.faces.component.ValueHolder;
36  import javax.faces.context.FacesContext;
37  import javax.faces.convert.Converter;
38  import javax.faces.convert.DateTimeConverter;
39  import javax.faces.el.ValueBinding;
40  import javax.faces.validator.Validator;
41  import javax.faces.validator.ValidatorException;
42  
43  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
44  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
45  import org.apache.myfaces.trinidad.bean.FacesBean;
46  import org.apache.myfaces.trinidad.bean.PropertyKey;
47  import org.apache.myfaces.trinidad.context.RequestContext;
48  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
49  import org.apache.myfaces.trinidad.model.DateListProvider;
50  import org.apache.myfaces.trinidad.util.ComponentUtils;
51  import org.apache.myfaces.trinidad.util.MessageFactory;
52  
53  @JSFValidator(configExcluded=true)
54  public class DateRestrictionValidator implements Validator, StateHolder {
55  
56  
57    public static final String VALIDATOR_ID = "org.apache.myfaces.trinidad.DateRestriction";
58    
59    /**
60     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
61     * to be created if the valid days  value check fails.  The message format
62     * string for this message may optionally include <code>{0}</code>,
63     * <code>{1}</code> and <code>{3}</code> placeholders,
64     * which will be replaced by user input, component label and configured
65     * days value.</p>
66     */
67    public static final String DAY_MESSAGE_ID =
68        "org.apache.myfaces.trinidad.validator.DateRestrictionValidator.DAY";
69  
70    /**
71     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
72     * to be created if the valid month value check fails.  The message format
73     * string for this message may optionally include <code>{0}</code>,
74     * <code>{1}</code> and <code>{3}</code> placeholders,
75     * which will be replaced by user input, component label and configured
76     * month value.</p>
77     */
78    public static final String MONTH_MESSAGE_ID =
79        "org.apache.myfaces.trinidad.validator.DateRestrictionValidator.MONTH";
80  
81    /**
82     * <p>The message identifier of the {@link javax.faces.application.FacesMessage}
83     * to be created if the valid weekdays value check fails.  The message format
84     * string for this message may optionally include <code>{0}</code>,
85     * <code>{1}</code> and <code>{3}</code> placeholders,
86     * which will be replaced by user input, component label and configured
87     * weekdays value.</p>
88     */
89    public static final String WEEKDAY_MESSAGE_ID =
90        "org.apache.myfaces.trinidad.validator.DateRestrictionValidator.WEEKDAY";
91  
92    /**
93     * Construct a {@link Validator} with no preconfigured limits.
94     */
95    public DateRestrictionValidator()
96    {
97      super();
98      _initMaps();
99    }
100 
101   @JSFProperty
102   public final String[] getInvalidMonths()
103   {
104     return (String[]) _facesBean.getProperty(_INVALID_MONTHS);
105   }
106 
107   public final void setInvalidMonths(String[] invalidMonths)
108   {
109     _facesBean.setProperty(_INVALID_MONTHS, invalidMonths);
110   }
111   
112   @JSFProperty
113   public final String[] getInvalidDaysOfWeek()
114   {
115     return (String[]) _facesBean.getProperty(_INVALID_DAYS_OF_WEEK);  }
116 
117   public final void setInvalidDaysOfWeek(String[] invalidDaysOfWeek)
118   {
119     _facesBean.setProperty(_INVALID_DAYS_OF_WEEK, invalidDaysOfWeek);
120   }
121   
122   @JSFProperty
123   public final DateListProvider getInvalidDays()
124   {
125     return (DateListProvider)_facesBean.getProperty(_INVALID_DAYS);
126   }
127 
128   public final void setInvalidDays(DateListProvider invalidDays)
129   {
130     _facesBean.setProperty(_INVALID_DAYS, invalidDays);
131   }
132   
133   @JSFProperty
134   public final String getMessageDetailInvalidMonths()
135   {
136     Object messageDetailInvalidMonths = _facesBean.getProperty(_INVALID_MONTHS_MESSAGE_DETAIL_KEY);
137     return ComponentUtils.resolveString(messageDetailInvalidMonths);
138   }
139 
140   public final void setMessageDetailInvalidMonths(String invalidMonths)
141   {
142     _facesBean.setProperty(_INVALID_MONTHS_MESSAGE_DETAIL_KEY, invalidMonths);
143   }
144   
145   @JSFProperty
146   public final String getMessageDetailInvalidDaysOfWeek()
147   {
148     Object messageDetailInvalidDaysOfWeek = _facesBean.getProperty(_INVALID_DAYS_OF_WEEK_MESSAGE_DETAIL_KEY);
149     return ComponentUtils.resolveString(messageDetailInvalidDaysOfWeek);
150   }
151 
152   public final void setMessageDetailInvalidDaysOfWeek(String invalidDaysOfWeek)
153   {
154     _facesBean.setProperty(_INVALID_DAYS_OF_WEEK_MESSAGE_DETAIL_KEY, invalidDaysOfWeek);
155   }
156   
157   @JSFProperty
158   public final String getMessageDetailInvalidDays()
159   {
160     Object messageDetailInvalidDays = _facesBean.getProperty(_INVALID_DAYS_MESSAGE_DETAIL_KEY);
161     return ComponentUtils.resolveString(messageDetailInvalidDays);
162   }
163 
164   public final void setMessageDetailInvalidDays(String invalidDays)
165   {
166     _facesBean.setProperty(_INVALID_DAYS_MESSAGE_DETAIL_KEY, invalidDays);
167   }
168 
169   /**
170    * <p>Custom hint invalidDaysOfWeek message.</p>
171    * Overrides default hint message
172    * @param hintWeek Custom hint message.
173    */
174   public void setHintInvalidDaysOfWeek(String hintWeek)
175   {
176     _facesBean.setProperty(_HINT_WEEK_KEY, hintWeek);
177   }
178 
179   /**
180    * <p>Return custom hint invalidDaysOfWeek message.</p>
181    * @return Custom hint message.
182    * @see  #setHintInvalidDaysOfWeek(String)
183    */
184   @JSFProperty(tagExcluded=true)
185   public String getHintInvalidDaysOfWeek()
186   {
187     Object obj = _facesBean.getProperty(_HINT_WEEK_KEY);
188     return ComponentUtils.resolveString(obj);
189   }
190 
191   /**
192    * <p>Custom hint invalidMonths message.</p>
193    * Overrides default hint message
194    * @param hintMonth Custom hint message.
195    */
196   public void setHintInvalidMonths(String hintMonth)
197   {
198     _facesBean.setProperty(_HINT_MONTH_KEY, hintMonth);
199   }
200 
201   /**
202    * <p>Return custom hint invalidMonths message.</p>
203    * @return Custom hint message.
204    * @see  #setHintInvalidMonths(String)
205    */
206   @JSFProperty(tagExcluded=true)
207   public String getHintInvalidMonths()
208   {
209     Object obj = _facesBean.getProperty(_HINT_MONTH_KEY);
210     return ComponentUtils.resolveString(obj);
211   }
212 
213   /**
214    * @exception IllegalArgumentException if <code>value</code> is not of type
215    * {@link java.util.Date}
216    */
217   public void validate(
218     FacesContext context,
219     UIComponent  component,
220     Object       value) throws ValidatorException
221   {
222     if (isDisabled())
223       return;
224     
225     if ((context == null) || (component == null))
226     {
227       throw new NullPointerException(_LOG.getMessage(
228         "NULL_FACESCONTEXT_OR_UICOMPONENT"));
229     }
230 
231     if (value != null)
232     {
233       Calendar calendar = getCalendar();
234       calendar.setTime(getDateValue(value));
235       Date convertedDate = calendar.getTime();
236       
237       String weekday = _dayMap.get(calendar.get(Calendar.DAY_OF_WEEK));
238       if (_getInvalidDaysOfWeek().contains(weekday))
239       {
240         throw new ValidatorException(
241             _getWrongWeekDayMessage(context, component, value, weekday));
242       }
243       
244       String month = _monthMap.get(calendar.get(Calendar.MONTH));
245       if ( _getInvalidMonths().contains(month))
246       {
247         throw new ValidatorException(
248             _getWrongMonthMessage(context, component, value, month));
249       }
250       
251       DateListProvider dlp = getInvalidDays();
252       List<Date> dates = null;
253       if (dlp != null)
254       {
255         dates = dlp.getDateList(context, calendar, calendar.getTime(), calendar.getTime());
256       }
257       
258       if(dates!=null)
259       {
260         for (Date date : dates)
261         {
262           //range is only one submitted day...
263           if(!date.before(convertedDate) && !date.after(convertedDate))
264           {
265             throw new ValidatorException(
266                 _getWrongDayMessage(context, component, value, date));
267           }
268         }
269       }
270     }
271   }
272 
273   //  StateHolder Methods
274   public Object saveState(FacesContext context)
275   {
276     return _facesBean.saveState(context);
277   }
278 
279 
280   public void restoreState(FacesContext context, Object state)
281   {
282     _facesBean.restoreState(context, state);
283   }
284 
285   @JSFProperty(istransient=true, tagExcluded=true)
286   public boolean isTransient()
287   {
288     return (_transientValue);
289   }
290 
291 
292   public void setTransient(boolean transientValue)
293   {
294     _transientValue = transientValue;
295   }
296   //  End of StateHolder Methods
297 
298 
299   /**
300    * <p>Set the {@link ValueExpression} used to calculate the value for the
301    * specified attribute if any.</p>
302    *
303    * @param name Name of the attribute for which to set a {@link ValueExpression}
304    * @param expression The {@link ValueExpression} to set, or <code>null</code>
305    *  to remove any currently set {@link ValueExpression}
306    *
307    * @exception NullPointerException if <code>name</code>
308    *  is <code>null</code>
309    * @exception IllegalArgumentException if <code>name</code> is not a valid
310    *            attribute of this converter
311    */
312   public void setValueExpression(String name, ValueExpression expression)
313   {
314     ValidatorUtils.setValueExpression(_facesBean, name, expression) ;
315   }
316 
317 
318   /**
319    * <p>Return the {@link ValueExpression} used to calculate the value for the
320    * specified attribute name, if any.</p>
321    *
322    * @param name Name of the attribute or property for which to retrieve a
323    *  {@link ValueExpression}
324    *
325    * @exception NullPointerException if <code>name</code>
326    *  is <code>null</code>
327    * @exception IllegalArgumentException if <code>name</code> is not a valid
328    * attribute of this converter
329    */
330   public ValueExpression getValueExpression(String name)
331   {
332     return ValidatorUtils.getValueExpression(_facesBean, name);
333   }
334 
335 
336   /**
337    * <p>Set the {@link ValueBinding} used to calculate the value for the
338    * specified attribute if any.</p>
339    *
340    * @param name Name of the attribute for which to set a {@link ValueBinding}
341    * @param binding The {@link ValueBinding} to set, or <code>null</code>
342    *  to remove any currently set {@link ValueBinding}
343    *
344    * @exception NullPointerException if <code>name</code>
345    *  is <code>null</code>
346    * @exception IllegalArgumentException if <code>name</code> is not a valid
347    *            attribute of this validator
348    * @deprecated
349    */
350   public void setValueBinding(String name, ValueBinding binding)
351   {
352     ValidatorUtils.setValueBinding(_facesBean, name, binding) ;
353   }
354 
355   /**
356    * <p>Return the {@link ValueBinding} used to calculate the value for the
357    * specified attribute name, if any.</p>
358    *
359    * @param name Name of the attribute or property for which to retrieve a
360    *  {@link ValueBinding}
361    *
362    * @exception NullPointerException if <code>name</code>
363    *  is <code>null</code>
364    * @exception IllegalArgumentException if <code>name</code> is not a valid
365    * attribute of this validator
366    * @deprecated
367    */
368   public ValueBinding getValueBinding(String name)
369   {
370     return ValidatorUtils.getValueBinding(_facesBean, name);
371   }
372 
373   @Override
374   public boolean equals(Object o)
375   {
376     if ( o instanceof DateRestrictionValidator)
377     {
378       DateRestrictionValidator that = (DateRestrictionValidator)o;
379 
380       if ( _transientValue == that._transientValue &&
381            isDisabled() == that.isDisabled() &&
382            (ValidatorUtils.equals(getInvalidDays(), that.getInvalidDays())) &&
383            (ValidatorUtils.equals(getInvalidDaysOfWeek(), that.getInvalidDaysOfWeek())) &&
384            (ValidatorUtils.equals(getInvalidMonths(), that.getInvalidMonths())) &&
385            (ValidatorUtils.equals(getMessageDetailInvalidDays(),
386                                    that.getMessageDetailInvalidDays())) &&
387            (ValidatorUtils.equals(getMessageDetailInvalidDaysOfWeek(),
388                                    that.getMessageDetailInvalidDaysOfWeek())) &&
389            (ValidatorUtils.equals(getMessageDetailInvalidMonths(),
390                                    that.getMessageDetailInvalidMonths()))
391           )
392       {
393         return true;
394       }
395     }
396     return false;
397   }
398 
399   @Override
400   public int hashCode()
401   {
402     int result = 17;
403     Object days = getInvalidDays();
404     Object daysOfWeek = getInvalidDaysOfWeek();
405     Object month = getInvalidMonths();
406     Object msgDetDays = getMessageDetailInvalidDays();
407     Object msgDetDaysOfWeek = getMessageDetailInvalidDaysOfWeek();
408     Object msgDetMonth = getMessageDetailInvalidMonths();
409 
410     result = 37 * result + ( days == null ? 0 : days.hashCode());
411     result = 37 * result + ( daysOfWeek == null ? 0 : daysOfWeek.hashCode());
412     result = 37 * result + ( month == null ? 0 : month.hashCode());
413     result = 37 * result + ( _transientValue ? 0 : 1);
414     result = 37 * result + (isDisabled() ? 1 : 0);
415     result = 37 * result + ( msgDetDays == null ? 0: msgDetDays.hashCode());
416     result = 37 * result + ( msgDetDaysOfWeek == null ? 0: msgDetDaysOfWeek.hashCode());
417     result = 37 * result + ( msgDetMonth == null ? 0: msgDetMonth.hashCode());
418     return result;
419   }
420 
421   /**
422     * Return whether it is disabled.
423     * @return true if it's disabled and false if it's enabled. 
424     */ 
425   public void setDisabled(boolean isDisabled)
426   {
427     _facesBean.setProperty(_DISABLED_KEY, Boolean.valueOf(isDisabled));
428   }
429 
430   /**
431     * Return whether it is disabled.
432     * @return true if it's disabled and false if it's enabled. 
433     */  
434   public boolean isDisabled()
435   {
436     Boolean disabled = (Boolean) _facesBean.getProperty(_DISABLED_KEY);
437     
438     return (disabled != null) ? disabled.booleanValue() : false;
439   }  
440 
441   private Converter _getConverter(
442     FacesContext context,
443     UIComponent component)
444   {
445     Converter converter = null;
446     if (component instanceof ValueHolder)
447     {
448       converter = ((ValueHolder) component).getConverter();
449     }
450 
451     if (converter == null)
452     {
453       // Use the DateTimeConverter's CONVERTER_ID, not Date.class,
454       // because there is in fact not usually a converter registered
455       // at Date.class
456       converter = context.getApplication().createConverter(
457                       DateTimeConverter.CONVERTER_ID);
458     }
459 
460     assert(converter != null);
461 
462     return converter;
463   }
464 
465   
466   protected Calendar getCalendar()
467   {
468     TimeZone tz = null;
469     RequestContext rctx = RequestContext.getCurrentInstance();
470     if (rctx != null)
471     {
472       tz = rctx.getTimeZone();
473     }
474     else
475     {
476       tz = TimeZone.getDefault();
477     }
478     return Calendar.getInstance(tz);
479 
480   }
481   
482   /**
483    * Parses the already converted value to a <code>java.util.Date</code>.
484    * @param value converted value
485    * @return fulltyped <code>java.util.Date</code>
486    * @throws IllegalArgumentException
487    */
488   protected static Date getDateValue(
489       Object value) throws IllegalArgumentException
490     {
491       if (value instanceof Date)
492       {
493         return ( (Date)value );
494       }
495 
496       throw new IllegalArgumentException(_LOG.getMessage(
497         "VALUE_IS_NOT_DATE_TYPE"));
498     }
499 
500   private FacesMessage _getWrongWeekDayMessage(
501       FacesContext context,
502       UIComponent component,
503       Object value,
504       Object weekday)
505   { 
506       Converter converter = _getConverter(context, component);
507 
508       Object cValue = _getConvertedValue(context, component, converter, value);
509       Object cWeekday   = _getConvertedValue(context, component, converter, weekday);
510 
511       Object msg   = _getRawInvalidDaysOfWeekMessageDetail();
512       Object label = ValidatorUtils.getComponentLabel(component);
513 
514       Object[] params = {label, cValue, cWeekday};
515 
516       return MessageFactory.getMessage(context, WEEKDAY_MESSAGE_ID,
517                                         msg, params, component);
518   }
519   private Object _getRawInvalidDaysOfWeekMessageDetail()
520   {
521     return _facesBean.getRawProperty(_INVALID_DAYS_OF_WEEK_MESSAGE_DETAIL_KEY);
522   }
523 
524   private FacesMessage _getWrongMonthMessage(
525       FacesContext context,
526       UIComponent component,
527       Object value,
528       Object weekday)
529   { 
530       Converter converter = _getConverter(context, component);
531 
532       Object cValue = _getConvertedValue(context, component, converter, value);
533       Object cWeekday   = _getConvertedValue(context, component, converter, weekday);
534 
535       Object msg   = _getRawInvalidMonthMessageDetail();
536       Object label = ValidatorUtils.getComponentLabel(component);
537 
538       Object[] params = {label, cValue, cWeekday};
539 
540       return MessageFactory.getMessage(context, MONTH_MESSAGE_ID,
541                                         msg, params, component);
542   }
543   private Object _getRawInvalidMonthMessageDetail()
544   {
545     return _facesBean.getRawProperty(_INVALID_MONTHS_MESSAGE_DETAIL_KEY);
546   }
547 
548   private FacesMessage _getWrongDayMessage(
549       FacesContext context,
550       UIComponent component,
551       Object value,
552       Object day)
553   { 
554       Converter converter = _getConverter(context, component);
555 
556       Object cValue = _getConvertedValue(context, component, converter, value);
557       Object cDay   = _getConvertedValue(context, component, converter, day);
558 
559       Object msg   = _getRawInvalidDaysMessageDetail();
560       Object label = ValidatorUtils.getComponentLabel(component);
561 
562       Object[] params = {label, cValue, cDay};
563 
564       return MessageFactory.getMessage(context, DAY_MESSAGE_ID,
565                                         msg, params, component);
566   }
567   private Object _getRawInvalidDaysMessageDetail()
568   {
569     return _facesBean.getRawProperty(_INVALID_DAYS_MESSAGE_DETAIL_KEY);
570   }
571 
572   private Object _getConvertedValue(
573     FacesContext context,
574     UIComponent  component,
575     Converter    converter,
576     Object       value)
577   {
578     return converter.getAsString(context, component, value);
579   }
580 
581   private final Set<String> _getInvalidMonths()
582   {
583     Set<String> monthSet = new HashSet<String>();
584     String[] month = getInvalidMonths();
585     if(month != null){
586       
587       for (int i = 0; i < month.length; i++)
588       {
589         monthSet.add(month[i].toLowerCase());
590       }
591     }
592       
593     return monthSet;
594   }
595 
596   private final Set<String> _getInvalidDaysOfWeek()
597   {
598     Set<String> daysOfWeekSet = new HashSet<String>();
599     String[] daysOfWeek = getInvalidDaysOfWeek();
600     if(daysOfWeek != null){
601       
602       for (int i = 0; i < daysOfWeek.length; i++)
603       {
604         daysOfWeekSet.add(daysOfWeek[i].toLowerCase());
605       }
606     }
607       
608     return daysOfWeekSet;
609   }
610 
611   private void _initMaps()
612   {
613     _dayMap = new HashMap<Integer, String>();
614     _dayMap.put(Calendar.SUNDAY, "sun");
615     _dayMap.put(Calendar.MONDAY, "mon");
616     _dayMap.put(Calendar.TUESDAY, "tue");
617     _dayMap.put(Calendar.WEDNESDAY, "wed");
618     _dayMap.put(Calendar.THURSDAY, "thu");
619     _dayMap.put(Calendar.FRIDAY, "fri");
620     _dayMap.put(Calendar.SATURDAY, "sat");
621     
622     _monthMap = new HashMap<Integer, String>();
623     _monthMap.put(Calendar.JANUARY, "jan");
624     _monthMap.put(Calendar.FEBRUARY, "feb");
625     _monthMap.put(Calendar.MARCH, "mar");
626     _monthMap.put(Calendar.APRIL, "apr");
627     _monthMap.put(Calendar.MAY, "may");
628     _monthMap.put(Calendar.JUNE, "jun");
629     _monthMap.put(Calendar.JULY, "jul");
630     _monthMap.put(Calendar.AUGUST, "aug");
631     _monthMap.put(Calendar.SEPTEMBER, "sep");
632     _monthMap.put(Calendar.OCTOBER, "oct");
633     _monthMap.put(Calendar.NOVEMBER, "nov");
634     _monthMap.put(Calendar.DECEMBER, "dec");
635   }
636   
637   private static final FacesBean.Type _TYPE = new FacesBean.Type();
638 
639   private static final PropertyKey _INVALID_MONTHS =
640     _TYPE.registerKey("invalidMonths", String[].class);
641 
642   private static final PropertyKey _INVALID_DAYS_OF_WEEK =
643     _TYPE.registerKey("invalidDaysOfWeek", String[].class);
644 
645   private static final PropertyKey _INVALID_DAYS =
646     _TYPE.registerKey("invalidDays", DateListProvider.class);
647 
648   private static final PropertyKey _INVALID_MONTHS_MESSAGE_DETAIL_KEY =
649     _TYPE.registerKey("messageDetailInvalidMonths", String.class);
650 
651   private static final PropertyKey _INVALID_DAYS_OF_WEEK_MESSAGE_DETAIL_KEY =
652     _TYPE.registerKey("messageDetailInvalidDaysOfWeek", String.class);
653 
654   private static final PropertyKey _INVALID_DAYS_MESSAGE_DETAIL_KEY =
655     _TYPE.registerKey("messageDetailInvalidDays", String.class);
656 
657   private static final PropertyKey  _HINT_WEEK_KEY =
658     _TYPE.registerKey("hintWeek", String.class);
659   
660   // Default is false
661   private static final PropertyKey _DISABLED_KEY =
662     _TYPE.registerKey("disabled", Boolean.class, Boolean.FALSE);
663 
664   private static final PropertyKey  _HINT_MONTH_KEY =
665     _TYPE.registerKey("hintMonth", String.class);
666 
667   private FacesBean _facesBean = ValidatorUtils.getFacesBean(_TYPE);
668 
669   private boolean _transientValue = false;
670   
671   private Map<Integer, String> _dayMap = null;
672   private Map<Integer, String> _monthMap = null;
673 
674   private static final TrinidadLogger _LOG =
675     TrinidadLogger.createTrinidadLogger(DateRestrictionValidator.class);
676 
677 }