1 package org.apache.velocity.tools.generic;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.Collections;
23 import java.util.Calendar;
24 import java.util.Iterator;
25 import java.util.LinkedHashMap;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.MissingResourceException;
29 import java.util.ResourceBundle;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class ComparisonDateTool extends DateTool
69 {
70
71 public static final long MILLIS_PER_SECOND = 1000l;
72
73
74 public static final long MILLIS_PER_MINUTE = 60l * MILLIS_PER_SECOND;
75
76
77 public static final long MILLIS_PER_HOUR = 60l * MILLIS_PER_MINUTE;
78
79
80 public static final long MILLIS_PER_DAY = 24l * MILLIS_PER_HOUR;
81
82
83 public static final long MILLIS_PER_WEEK = 7l * MILLIS_PER_DAY;
84
85
86 public static final long MILLIS_PER_MONTH = 30l * MILLIS_PER_DAY;
87
88
89 public static final long MILLIS_PER_YEAR = 365l * MILLIS_PER_DAY;
90
91
92 public static final String BUNDLE_NAME_KEY = "bundle";
93
94
95 public static final String DEPTH_KEY = "depth";
96
97
98 public static final String SKIPPED_UNITS_KEY = "skip";
99
100
101 public static final String DEFAULT_BUNDLE_NAME =
102 "org.apache.velocity.tools.generic.times";
103
104
105
106 protected static final String MILLISECOND_KEY = "millisecond";
107 protected static final String SECOND_KEY = "second";
108 protected static final String MINUTE_KEY = "minute";
109 protected static final String HOUR_KEY = "hour";
110 protected static final String DAY_KEY = "day";
111 protected static final String WEEK_KEY = "week";
112 protected static final String MONTH_KEY = "month";
113 protected static final String YEAR_KEY = "year";
114
115
116 protected static final Map TIME_UNITS;
117 static
118 {
119 Map units = new LinkedHashMap(8);
120 units.put(MILLISECOND_KEY, Long.valueOf(1));
121 units.put(SECOND_KEY, Long.valueOf(MILLIS_PER_SECOND));
122 units.put(MINUTE_KEY, Long.valueOf(MILLIS_PER_MINUTE));
123 units.put(HOUR_KEY, Long.valueOf(MILLIS_PER_HOUR));
124 units.put(DAY_KEY, Long.valueOf(MILLIS_PER_DAY));
125 units.put(WEEK_KEY, Long.valueOf(MILLIS_PER_WEEK));
126 units.put(MONTH_KEY, Long.valueOf(MILLIS_PER_MONTH));
127 units.put(YEAR_KEY, Long.valueOf(MILLIS_PER_YEAR));
128 TIME_UNITS = Collections.unmodifiableMap(units);
129 }
130
131
132 protected static final String CURRENT_PREFIX = "current.";
133 protected static final String AFTER_KEY = "after";
134 protected static final String BEFORE_KEY = "before";
135 protected static final String EQUAL_KEY = "equal";
136 protected static final String ZERO_KEY = "zero";
137 protected static final String ABBR_SUFFIX = ".abbr";
138 protected static final String ONE_DAY_SUFFIX = ".day";
139 protected static final String PLURAL_SUFFIX = "s";
140
141
142 protected static final int CURRENT_TYPE = 0;
143 protected static final int RELATIVE_TYPE = 1;
144 protected static final int DIFF_TYPE = 2;
145
146 private String bundleName = DEFAULT_BUNDLE_NAME;
147 private ResourceBundle defaultBundle;
148 private Map timeUnits = TIME_UNITS;
149 private int depth = 1;
150
151
152
153
154
155
156 protected void configure(ValueParser values)
157 {
158
159 super.configure(values);
160
161
162 String bundle = values.getString(BUNDLE_NAME_KEY);
163 if (bundle != null)
164 {
165 this.bundleName = bundle;
166 }
167
168 this.depth = values.getInt(DEPTH_KEY, 1);
169
170
171 String[] skip = values.getStrings(SKIPPED_UNITS_KEY);
172 if (skip != null)
173 {
174 timeUnits = new LinkedHashMap(TIME_UNITS);
175 for (int i=0; i < skip.length; i++)
176 {
177 timeUnits.remove(skip[i]);
178 }
179 }
180 }
181
182
183
184
185 protected String getText(String key, Locale locale)
186 {
187 Locale defaultLocale = getLocale();
188 ResourceBundle bundle = null;
189
190 if (locale == null || locale.equals(defaultLocale))
191 {
192 if (defaultBundle == null)
193 {
194
195 try
196 {
197
198 defaultBundle = ResourceBundle.getBundle(this.bundleName,
199 defaultLocale);
200 }
201 catch (MissingResourceException e) {}
202 }
203
204
205 bundle = defaultBundle;
206 }
207 else
208 {
209
210 try
211 {
212 bundle = ResourceBundle.getBundle(this.bundleName, locale);
213 }
214 catch (MissingResourceException e) {}
215 }
216
217
218 if (bundle != null)
219 {
220 try
221 {
222
223 return bundle.getString(key);
224 }
225 catch (MissingResourceException e) {}
226 }
227
228
229 return "???" + key + "???";
230 }
231
232
233
234
235
236
237
238 public static long toYears(long ms)
239 {
240 return ms / MILLIS_PER_YEAR;
241 }
242
243
244
245
246 public static long toMonths(long ms)
247 {
248 return ms / MILLIS_PER_MONTH;
249 }
250
251
252
253
254 public static long toWeeks(long ms)
255 {
256 return ms / MILLIS_PER_WEEK;
257 }
258
259
260
261
262 public static long toDays(long ms)
263 {
264 return ms / MILLIS_PER_DAY;
265 }
266
267
268
269
270 public static long toHours(long ms)
271 {
272 return ms / MILLIS_PER_HOUR;
273 }
274
275
276
277
278 public static long toMinutes(long ms)
279 {
280 return ms / MILLIS_PER_MINUTE;
281 }
282
283
284
285
286 public static long toSeconds(long ms)
287 {
288 return ms / MILLIS_PER_SECOND;
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302 public Comparison whenIs(Object then)
303 {
304 return compare(getCalendar(), then, CURRENT_TYPE);
305 }
306
307
308
309
310
311
312
313
314
315
316 public Comparison whenIs(Object now, Object then)
317 {
318 return compare(now, then, RELATIVE_TYPE);
319 }
320
321
322
323
324
325
326
327
328
329
330 public Comparison difference(Object now, Object then)
331 {
332 return compare(now, then, DIFF_TYPE);
333 }
334
335 protected Comparison compare(Object now, Object then, int type)
336 {
337 Calendar calThen = toCalendar(then);
338 Calendar calNow = toCalendar(now);
339 if (calThen == null || calNow == null)
340 {
341 return null;
342 }
343
344 long ms = calThen.getTimeInMillis() - calNow.getTimeInMillis();
345 return new Comparison(ms, type, this.depth, false, null);
346 }
347
348
349
350
351
352
353
354
355
356
357 protected String toString(long ms, int type, int depth,
358 boolean abbr, Locale loc)
359 {
360
361 if (ms == 0)
362 {
363 String sameKey = (abbr) ? ABBR_SUFFIX : "";
364 if (type == CURRENT_TYPE)
365 {
366 sameKey = CURRENT_PREFIX + EQUAL_KEY + sameKey;
367 }
368 else if (type == RELATIVE_TYPE)
369 {
370 sameKey = EQUAL_KEY + sameKey;
371 }
372 else
373 {
374 sameKey = ZERO_KEY + sameKey;
375 }
376 return getText(sameKey, loc);
377 }
378
379 boolean isBefore = false;
380 if (ms < 0)
381 {
382 isBefore = true;
383
384 ms *= -1;
385 }
386
387
388 String friendly = toString(ms, depth, abbr, loc);
389
390
391 if (type == DIFF_TYPE)
392 {
393
394 if (isBefore)
395 {
396 friendly = "-" + friendly;
397 }
398
399 return friendly;
400 }
401
402
403 String directionKey = (isBefore) ? BEFORE_KEY : AFTER_KEY;
404 if (type == CURRENT_TYPE)
405 {
406 directionKey = CURRENT_PREFIX + directionKey;
407
408 if (friendly != null && friendly.startsWith("1"))
409 {
410
411
412 String dayKey = (abbr) ? DAY_KEY + ABBR_SUFFIX : DAY_KEY;
413 if (friendly.equals("1 " + getText(dayKey, loc)))
414 {
415
416 directionKey += ONE_DAY_SUFFIX;
417
418
419
420 return getText(directionKey, loc);
421 }
422 }
423 }
424
425
426
427 if (abbr)
428 {
429 directionKey += ABBR_SUFFIX;
430 }
431
432
433 return friendly + " " + getText(directionKey, loc);
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447
448 protected String toString(long diff, int maxUnitDepth,
449 boolean abbreviate, Locale locale)
450 {
451
452 if (diff <= 0)
453 {
454 return null;
455 }
456
457 if (maxUnitDepth > timeUnits.size())
458 {
459 maxUnitDepth = timeUnits.size();
460 }
461
462 long value = 0;
463 long remainder = 0;
464
465
466 Iterator i = timeUnits.keySet().iterator();
467 String unitKey = (String)i.next();
468 Long unit = (Long)timeUnits.get(unitKey);
469 while (i.hasNext())
470 {
471
472 String nextUnitKey = (String)i.next();
473 Long nextUnit = (Long)timeUnits.get(nextUnitKey);
474
475
476 if (diff < nextUnit.longValue())
477 {
478
479 value = diff / unit.longValue();
480 remainder = diff - (value * unit.longValue());
481 break;
482 }
483
484
485 unitKey = nextUnitKey;
486 unit = nextUnit;
487 }
488
489
490 if (unitKey.equals(YEAR_KEY))
491 {
492 value = diff / unit.longValue();
493 remainder = diff - (value * unit.longValue());
494 }
495
496
497 if (value != 1)
498 {
499 unitKey += PLURAL_SUFFIX;
500 }
501
502 if (abbreviate)
503 {
504 unitKey += ABBR_SUFFIX;
505 }
506
507
508 String output = value + " " + getText(unitKey, locale);
509
510
511 if (maxUnitDepth > 1 && remainder > 0)
512 {
513 output += " " + toString(remainder, maxUnitDepth - 1,
514 abbreviate, locale);
515 }
516 return output;
517 }
518
519
520
521 public class Comparison
522 {
523 private final long milliseconds;
524 private final int type;
525 private final int maxUnitDepth;
526 private final boolean abbreviate;
527 private final Locale locale;
528
529 public Comparison(long ms, int type, int depth, boolean abbr, Locale loc)
530 {
531 this.milliseconds = ms;
532 this.type = type;
533 this.maxUnitDepth = depth;
534 this.abbreviate = abbr;
535 this.locale = loc;
536 }
537
538
539
540
541
542 public Comparison abbr(boolean abbr)
543 {
544 return new Comparison(this.milliseconds, this.type,
545 this.maxUnitDepth, abbr, this.locale);
546 }
547
548
549
550
551
552 public Comparison depth(int depth)
553 {
554 return new Comparison(this.milliseconds, this.type,
555 depth, this.abbreviate, this.locale);
556 }
557
558
559
560
561
562
563
564 public Comparison locale(Locale loc)
565 {
566 return new Comparison(this.milliseconds, this.type,
567 this.maxUnitDepth, this.abbreviate, loc);
568 }
569
570
571
572
573 public long getYears()
574 {
575 return ComparisonDateTool.toYears(this.milliseconds);
576 }
577
578
579
580
581 public long getMonths()
582 {
583 return ComparisonDateTool.toMonths(this.milliseconds);
584 }
585
586
587
588
589 public long getWeeks()
590 {
591 return ComparisonDateTool.toWeeks(this.milliseconds);
592 }
593
594
595
596
597 public long getDays()
598 {
599 return ComparisonDateTool.toDays(this.milliseconds);
600 }
601
602
603
604
605 public long getHours()
606 {
607 return ComparisonDateTool.toHours(this.milliseconds);
608 }
609
610
611
612
613 public long getMinutes()
614 {
615 return ComparisonDateTool.toMinutes(this.milliseconds);
616 }
617
618
619
620
621 public long getSeconds()
622 {
623 return ComparisonDateTool.toSeconds(this.milliseconds);
624 }
625
626
627
628
629 public long getMilliseconds()
630 {
631 return this.milliseconds;
632 }
633
634
635
636
637
638
639
640 public Comparison getFull()
641 {
642 return depth(ComparisonDateTool.this.timeUnits.size());
643 }
644
645
646
647
648
649
650
651
652 public Comparison getDifference()
653 {
654 return new Comparison(this.milliseconds, DIFF_TYPE,
655 this.maxUnitDepth, this.abbreviate, this.locale);
656 }
657
658
659
660
661
662
663
664
665 public Comparison getRelative()
666 {
667 return new Comparison(this.milliseconds, RELATIVE_TYPE,
668 this.maxUnitDepth, this.abbreviate, this.locale);
669 }
670
671
672
673
674
675
676 public Comparison getAbbr()
677 {
678 return abbr(true);
679 }
680
681
682
683
684 public String toString()
685 {
686 return ComparisonDateTool.this.toString(this.milliseconds,
687 this.type,
688 this.maxUnitDepth,
689 this.abbreviate,
690 this.locale);
691 }
692 }
693
694 }