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