src/net/fortuna/ical4j/model/CalendarDateFormatFactory.java

changeset 0
fb9019fb1bf7
equal deleted inserted replaced
-1:000000000000 0:096992a7b0bb
1 /**
2 * Copyright (c) 2012, Ben Fortuna
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * o Neither the name of Ben Fortuna nor the names of any other contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 package net.fortuna.ical4j.model;
33
34 import java.text.FieldPosition;
35 import java.text.NumberFormat;
36 import java.text.ParsePosition;
37 import java.text.SimpleDateFormat;
38 import java.util.Date;
39 import java.util.GregorianCalendar;
40 import java.util.TimeZone;
41
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44
45 /**
46 * $Id$ [06-Apr-2004]
47 *
48 * Creates DateFormat objects optimized for common iCalendar date patterns.
49 *
50 * @author Dave Nault dnault@laszlosystems.com
51 * @see #getInstance(String)
52 */
53 public final class CalendarDateFormatFactory {
54 private static final Log LOG = LogFactory.getLog(CalendarDateFormatFactory.class);
55
56 private static final String DATETIME_PATTERN = "yyyyMMdd'T'HHmmss";
57 private static final String DATETIME_UTC_PATTERN = "yyyyMMdd'T'HHmmss'Z'";
58 private static final String DATE_PATTERN = "yyyyMMdd";
59 private static final String TIME_PATTERN = "HHmmss";
60 private static final String TIME_UTC_PATTERN = "HHmmss'Z'";
61
62 /**
63 * Constructor made private to enforce static nature.
64 */
65 private CalendarDateFormatFactory() {
66 }
67
68 /**
69 * Returns DateFormat objects optimized for common iCalendar date patterns. The DateFormats are *not* thread safe.
70 * Attempts to get or set the Calendar or NumberFormat of an optimized DateFormat will result in an
71 * UnsupportedOperation exception being thrown.
72 *
73 * @param pattern
74 * a SimpleDateFormat-compatible pattern
75 * @return an optimized DateFormat instance if possible, otherwise a normal SimpleDateFormat instance
76 */
77 public static java.text.DateFormat getInstance(String pattern) {
78 java.text.DateFormat instance = null;
79
80 // if (true) {
81 // return new SimpleDateFormat(pattern);
82 // }
83
84 if (pattern.equals(DATETIME_PATTERN) || pattern.equals(DATETIME_UTC_PATTERN)) {
85 instance = new DateTimeFormat(pattern);
86 }
87 else if (pattern.equals(DATE_PATTERN)) {
88 instance = new DateFormat(pattern);
89 }
90 else if (pattern.equals(TIME_PATTERN) || pattern.equals(TIME_UTC_PATTERN)) {
91 instance = new TimeFormat(pattern);
92 }
93 else {
94 if (LOG.isDebugEnabled()) {
95 LOG.debug("unexpected date format pattern: " + pattern);
96 }
97
98 instance = new SimpleDateFormat(pattern);
99 }
100 return instance;
101 }
102
103 private abstract static class CalendarDateFormat extends java.text.DateFormat {
104 /**
105 *
106 */
107 private static final long serialVersionUID = -4191402739860280205L;
108
109 private static final java.util.TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
110
111 private final String pattern;
112
113 private boolean lenient = true;
114
115 private java.util.TimeZone timeZone = DEFAULT_TIME_ZONE;
116
117 public CalendarDateFormat(String pattern) {
118 this.pattern = pattern;
119 }
120
121 public java.util.TimeZone getTimeZone() {
122 return this.timeZone;
123 }
124
125 public void setTimeZone(java.util.TimeZone tz) {
126 this.timeZone = tz;
127 }
128
129 public void setLenient(boolean lenient) {
130 this.lenient = lenient;
131 }
132
133 public boolean isLenient() {
134 return lenient;
135 }
136
137 public java.util.Calendar getCalendar() {
138 throw new UnsupportedOperationException();
139 }
140
141 public void setCalendar(java.util.Calendar c) {
142 throw new UnsupportedOperationException();
143 }
144
145 public NumberFormat getNumberFormat() {
146 throw new UnsupportedOperationException();
147 }
148
149 public void setNumberFormat(NumberFormat n) {
150 throw new UnsupportedOperationException();
151 }
152
153 public Object clone() {
154 // don't call super.clone()
155 final CalendarDateFormat f = (CalendarDateFormat) CalendarDateFormatFactory.getInstance(pattern);
156 f.setTimeZone(getTimeZone());
157 f.setLenient(isLenient());
158 return f;
159 }
160
161 public boolean equals(Object o) {
162 if (this == o) {
163 return true;
164 }
165 if (o == null || getClass() != o.getClass()) {
166 return false;
167 }
168 if (!super.equals(o)) {
169 return false;
170 }
171
172 final CalendarDateFormat that = (CalendarDateFormat) o;
173
174 if (lenient != that.lenient) {
175 return false;
176 }
177 if (!pattern.equals(that.pattern)) {
178 return false;
179 }
180 if (!timeZone.equals(that.timeZone)) {
181 return false;
182 }
183
184 return true;
185 }
186
187 public int hashCode() {
188 int result = super.hashCode();
189 result = 31 * result + pattern.hashCode();
190 result = 31 * result + (lenient ? 1 : 0);
191 result = 31 * result + timeZone.hashCode();
192 return result;
193 }
194 }
195
196 /**
197 * A custom date-time formatter.
198 * Parses and formats these patterns:
199 *
200 * <pre>
201 * yyyyMMdd'T'HHmmss
202 * yyyyMMdd'T'HHmmss'Z'
203 * </pre>
204 */
205 private static class DateTimeFormat extends CalendarDateFormat {
206
207 /**
208 *
209 */
210 private static final long serialVersionUID = 3005824302269636122L;
211
212 final boolean patternEndsWithZ;
213
214 public DateTimeFormat(String pattern) {
215 super(pattern);
216 patternEndsWithZ = pattern.endsWith("'Z'");
217 }
218
219 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
220 final java.util.Calendar cal = new GregorianCalendar(getTimeZone());
221 cal.setTimeInMillis(date.getTime());
222
223 appendPadded(toAppendTo, cal.get(GregorianCalendar.YEAR), 4);
224 appendPadded(toAppendTo, cal.get(GregorianCalendar.MONTH) + 1, 2);
225 appendPadded(toAppendTo, cal.get(GregorianCalendar.DAY_OF_MONTH), 2);
226 toAppendTo.append("T");
227
228 appendPadded(toAppendTo, cal.get(GregorianCalendar.HOUR_OF_DAY), 2);
229 appendPadded(toAppendTo, cal.get(GregorianCalendar.MINUTE), 2);
230 appendPadded(toAppendTo, cal.get(GregorianCalendar.SECOND), 2);
231
232 if (patternEndsWithZ) {
233 toAppendTo.append("Z");
234 }
235
236 return toAppendTo;
237 }
238
239 public Date parse(String source, ParsePosition pos) {
240 // if lenient ignore superfluous input..
241 if (patternEndsWithZ) {
242 if (source.length() > DATETIME_UTC_PATTERN.length() && !isLenient()) {
243 pos.setErrorIndex(DATETIME_UTC_PATTERN.length());
244 return null;
245 }
246 } else if (source.length() > DATETIME_PATTERN.length() && !isLenient()) {
247 pos.setErrorIndex(DATETIME_PATTERN.length());
248 return null;
249 }
250
251 try {
252 if (source.charAt(8) != 'T') {
253 pos.setErrorIndex(8);
254 return null;
255 }
256 if (patternEndsWithZ && source.charAt(15) != 'Z') {
257 pos.setErrorIndex(15);
258 return null;
259 }
260
261 final int year = Integer.parseInt(source.substring(0, 4));
262 final int month = Integer.parseInt(source.substring(4, 6)) - 1;
263 final int day = Integer.parseInt(source.substring(6, 8));
264 final int hour = Integer.parseInt(source.substring(9, 11));
265 final int minute = Integer.parseInt(source.substring(11, 13));
266 final int second = Integer.parseInt(source.substring(13, 15));
267
268 final Date d = makeCalendar(isLenient(), getTimeZone(),
269 year, month, day, hour, minute, second).getTime();
270 pos.setIndex(15);
271 return d;
272 } catch (Exception e) {
273 return null;
274 }
275 }
276 }
277
278 /**
279 * Custom date formatter.
280 * Parses and formats this pattern:
281 *
282 * <pre>
283 * yyyyMMdd
284 * </pre>
285 */
286 private static class DateFormat extends CalendarDateFormat {
287
288 /**
289 *
290 */
291 private static final long serialVersionUID = -7626077667268431779L;
292
293 public DateFormat(String pattern) {
294 super(pattern);
295 }
296
297 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
298 final java.util.Calendar cal = java.util.Calendar.getInstance(getTimeZone());
299 cal.setTimeInMillis(date.getTime());
300
301 appendPadded(toAppendTo, cal.get(GregorianCalendar.YEAR), 4);
302 appendPadded(toAppendTo, cal.get(GregorianCalendar.MONTH) + 1, 2);
303 appendPadded(toAppendTo, cal.get(GregorianCalendar.DAY_OF_MONTH), 2);
304
305 return toAppendTo;
306 }
307
308 public Date parse(String source, ParsePosition pos) {
309 // if lenient ignore superfluous input..
310 if (source.length() > DATE_PATTERN.length() && !isLenient()) {
311 pos.setErrorIndex(DATE_PATTERN.length());
312 return null;
313 }
314
315 try {
316 final int year = Integer.parseInt(source.substring(0, 4));
317 final int month = Integer.parseInt(source.substring(4, 6)) - 1;
318 final int day = Integer.parseInt(source.substring(6, 8));
319
320 final Date d = makeCalendar(isLenient(), getTimeZone(), year, month, day).getTime();
321 pos.setIndex(8);
322 return d;
323 } catch (Exception e) {
324 return null;
325 }
326 }
327 }
328
329 /**
330 * Custom time formatter.
331 * Parses and formats these patterns:
332 *
333 * <pre>
334 * HHmmss
335 * HHmmss'Z'
336 * </pre>
337 */
338 private static class TimeFormat extends CalendarDateFormat {
339
340 /**
341 *
342 */
343 private static final long serialVersionUID = -1367114409994225425L;
344
345 final boolean patternEndsWithZ;
346
347 public TimeFormat(String pattern) {
348 super(pattern);
349 patternEndsWithZ = pattern.endsWith("'Z'");
350 }
351
352 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
353 final java.util.Calendar cal = new GregorianCalendar(getTimeZone());
354 cal.setTimeInMillis(date.getTime());
355
356 appendPadded(toAppendTo, cal.get(GregorianCalendar.HOUR_OF_DAY), 2);
357 appendPadded(toAppendTo, cal.get(GregorianCalendar.MINUTE), 2);
358 appendPadded(toAppendTo, cal.get(GregorianCalendar.SECOND), 2);
359
360 if (patternEndsWithZ) {
361 toAppendTo.append("Z");
362 }
363
364 return toAppendTo;
365 }
366
367 public Date parse(String source, ParsePosition pos) {
368 // if lenient ignore superfluous input..
369 if (patternEndsWithZ) {
370 if (source.length() > TIME_UTC_PATTERN.length() && !isLenient()) {
371 pos.setErrorIndex(TIME_UTC_PATTERN.length());
372 return null;
373 }
374 } else if (source.length() > TIME_PATTERN.length() && !isLenient()) {
375 pos.setErrorIndex(TIME_PATTERN.length());
376 return null;
377 }
378
379 try {
380 if (patternEndsWithZ && source.charAt(6) != 'Z') {
381 pos.setErrorIndex(6);
382 return null;
383 }
384
385 final int hour = Integer.parseInt(source.substring(0, 2));
386 final int minute = Integer.parseInt(source.substring(2, 4));
387 final int second = Integer.parseInt(source.substring(4, 6));
388
389 final Date d = makeCalendar(isLenient(), getTimeZone(), 1970, 0, 1, hour, minute, second).getTime();
390 pos.setIndex(6);
391 return d;
392 } catch (Exception e) {
393 return null;
394 }
395 }
396 }
397
398 private static java.util.Calendar makeCalendar(boolean lenient, java.util.TimeZone timeZone, int year,
399 int zeroBasedMonth, int day, int hour, int minutes, int seconds) {
400 final java.util.Calendar cal = new GregorianCalendar(timeZone);
401 cal.setLenient(lenient);
402 cal.set(year, zeroBasedMonth, day, hour, minutes, seconds);
403 cal.set(java.util.Calendar.MILLISECOND, 0);
404 return cal;
405 }
406
407 private static java.util.Calendar makeCalendar(boolean lenient, TimeZone timeZone, int year, int month, int day) {
408 return makeCalendar(lenient, timeZone, year, month, day, 0, 0, 0);
409 }
410
411 private static void appendPadded(StringBuffer toAppendTo, int value, int fieldWidth) {
412 final String s = Integer.toString(value);
413 final int max = fieldWidth - s.length();
414 for (int i = 0; i < max; i++) {
415 toAppendTo.append("0");
416 }
417 toAppendTo.append(s);
418 }
419
420 }

mercurial