1 package com.atlassian.core.util;
2
3 import java.text.DecimalFormat;
4 import java.text.NumberFormat;
5 import java.text.ParsePosition;
6 import java.util.HashMap;
7 import java.util.Locale;
8 import java.util.Map;
9 import java.util.regex.Matcher;
10 import java.util.regex.Pattern;
11
12 import com.atlassian.core.i18n.I18nTextProvider;
13 import com.atlassian.core.util.DateUtils.Duration;
14
15 import org.apache.commons.lang.StringUtils;
16
17
18
19
20 public class DurationUtils
21 {
22
23
24
25 private static final String UNIT_DAY = "core.durationutils.unit.day";
26
27
28
29
30 private static final String UNIT_HOUR = "core.durationutils.unit.hour";
31
32
33
34
35 private static final String UNIT_MINUTE = "core.durationutils.unit.minute";
36
37
38 private static final Pattern COUNT_WITH_OPTIONAL_UNITS = Pattern.compile("([,\\.\\xA0'\\p{Nd}]+)\\s*(?:([^,\\s]+),?)?\\s*");
39
40 public static long getDurationSeconds(String durationStr, long secondsPerDay, long secondsPerWeek, final Duration defaultUnit,
41 Locale locale, I18nTextProvider i18n)
42 throws InvalidDurationException
43 {
44 Map<String, Duration> durationTokens = getDurationTokens(i18n);
45
46 return getDurationSeconds(durationStr, secondsPerDay, secondsPerWeek, defaultUnit, locale, durationTokens);
47 }
48
49 public static long getDurationSeconds(String durationStr, long secondsPerDay, long secondsPerWeek, final Duration defaultUnit,
50 Locale locale, Map<String, Duration> tokens)
51 throws InvalidDurationException
52 {
53 if (StringUtils.isBlank(durationStr))
54 {
55 return 0;
56 }
57
58 durationStr = durationStr.trim();
59
60 NumberFormat nf = DecimalFormat.getNumberInstance(locale);
61
62 long seconds = 0;
63
64 Matcher m = COUNT_WITH_OPTIONAL_UNITS.matcher(durationStr);
65
66 while (m.lookingAt())
67 {
68
69 ParsePosition pp = new ParsePosition(0);
70
71 String number = m.group(1);
72
73 Number n = nf.parse(number, pp);
74
75 if (pp.getIndex() != number.length())
76 {
77 throw new InvalidDurationException("Bad number '" + number + "' in duration string '" + durationStr + "'");
78 }
79
80 String unitName = m.group(2);
81
82 Duration unit;
83 if (unitName != null)
84 {
85 unit = tokens.get(unitName);
86 if (unit == null)
87 {
88 throw new InvalidDurationException("No unit for '" + unitName + "'");
89 }
90 }
91 else
92 {
93 unit = defaultUnit;
94 }
95
96 long s = (long) (unit.getModifiedSeconds(secondsPerDay, secondsPerWeek) * n.doubleValue());
97
98 if (unit != defaultUnit && s % 60 != 0)
99 {
100 throw new InvalidDurationException("Durations must be in whole minutes");
101 }
102
103 seconds += s;
104
105 m.region(m.end(), durationStr.length());
106 }
107
108 if (m.regionStart() != durationStr.length())
109 {
110 throw new InvalidDurationException("Invalid characters in duration: " + durationStr);
111 }
112
113 return seconds;
114 }
115
116 public static Map<String, Duration> getDurationTokens(I18nTextProvider i18n)
117 {
118 Map<String, Duration> tokens = new HashMap<String, DateUtils.Duration>();
119
120 tokens.put(getDurationToken(i18n, UNIT_DAY), Duration.DAY);
121 tokens.put(getDurationToken(i18n, UNIT_HOUR), Duration.HOUR);
122 tokens.put(getDurationToken(i18n, UNIT_MINUTE), Duration.MINUTE);
123
124 for (Duration d : Duration.values())
125 {
126 String n = d.name().toLowerCase();
127 tokens.put(i18n.getText("core.dateutils." + n), d);
128 tokens.put(i18n.getText("core.dateutils." + n + "s"), d);
129 }
130
131 return tokens;
132 }
133
134 private static String getDurationToken(I18nTextProvider i18n, String unit)
135 {
136 String s = i18n.getText(unit);
137 return s.replace("{0}", "").trim();
138 }
139 }