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