1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/net/fortuna/ical4j/model/CalendarDateFormatFactory.java Tue Feb 10 18:12:00 2015 +0100 1.3 @@ -0,0 +1,420 @@ 1.4 +/** 1.5 + * Copyright (c) 2012, Ben Fortuna 1.6 + * All rights reserved. 1.7 + * 1.8 + * Redistribution and use in source and binary forms, with or without 1.9 + * modification, are permitted provided that the following conditions 1.10 + * are met: 1.11 + * 1.12 + * o Redistributions of source code must retain the above copyright 1.13 + * notice, this list of conditions and the following disclaimer. 1.14 + * 1.15 + * o Redistributions in binary form must reproduce the above copyright 1.16 + * notice, this list of conditions and the following disclaimer in the 1.17 + * documentation and/or other materials provided with the distribution. 1.18 + * 1.19 + * o Neither the name of Ben Fortuna nor the names of any other contributors 1.20 + * may be used to endorse or promote products derived from this software 1.21 + * without specific prior written permission. 1.22 + * 1.23 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.24 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.25 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.26 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 1.27 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 1.28 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 1.29 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 1.30 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 1.31 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 1.32 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.33 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.34 + */ 1.35 +package net.fortuna.ical4j.model; 1.36 + 1.37 +import java.text.FieldPosition; 1.38 +import java.text.NumberFormat; 1.39 +import java.text.ParsePosition; 1.40 +import java.text.SimpleDateFormat; 1.41 +import java.util.Date; 1.42 +import java.util.GregorianCalendar; 1.43 +import java.util.TimeZone; 1.44 + 1.45 +import org.apache.commons.logging.Log; 1.46 +import org.apache.commons.logging.LogFactory; 1.47 + 1.48 +/** 1.49 + * $Id$ [06-Apr-2004] 1.50 + * 1.51 + * Creates DateFormat objects optimized for common iCalendar date patterns. 1.52 + * 1.53 + * @author Dave Nault dnault@laszlosystems.com 1.54 + * @see #getInstance(String) 1.55 + */ 1.56 +public final class CalendarDateFormatFactory { 1.57 + private static final Log LOG = LogFactory.getLog(CalendarDateFormatFactory.class); 1.58 + 1.59 + private static final String DATETIME_PATTERN = "yyyyMMdd'T'HHmmss"; 1.60 + private static final String DATETIME_UTC_PATTERN = "yyyyMMdd'T'HHmmss'Z'"; 1.61 + private static final String DATE_PATTERN = "yyyyMMdd"; 1.62 + private static final String TIME_PATTERN = "HHmmss"; 1.63 + private static final String TIME_UTC_PATTERN = "HHmmss'Z'"; 1.64 + 1.65 + /** 1.66 + * Constructor made private to enforce static nature. 1.67 + */ 1.68 + private CalendarDateFormatFactory() { 1.69 + } 1.70 + 1.71 + /** 1.72 + * Returns DateFormat objects optimized for common iCalendar date patterns. The DateFormats are *not* thread safe. 1.73 + * Attempts to get or set the Calendar or NumberFormat of an optimized DateFormat will result in an 1.74 + * UnsupportedOperation exception being thrown. 1.75 + * 1.76 + * @param pattern 1.77 + * a SimpleDateFormat-compatible pattern 1.78 + * @return an optimized DateFormat instance if possible, otherwise a normal SimpleDateFormat instance 1.79 + */ 1.80 + public static java.text.DateFormat getInstance(String pattern) { 1.81 + java.text.DateFormat instance = null; 1.82 + 1.83 + // if (true) { 1.84 + // return new SimpleDateFormat(pattern); 1.85 + // } 1.86 + 1.87 + if (pattern.equals(DATETIME_PATTERN) || pattern.equals(DATETIME_UTC_PATTERN)) { 1.88 + instance = new DateTimeFormat(pattern); 1.89 + } 1.90 + else if (pattern.equals(DATE_PATTERN)) { 1.91 + instance = new DateFormat(pattern); 1.92 + } 1.93 + else if (pattern.equals(TIME_PATTERN) || pattern.equals(TIME_UTC_PATTERN)) { 1.94 + instance = new TimeFormat(pattern); 1.95 + } 1.96 + else { 1.97 + if (LOG.isDebugEnabled()) { 1.98 + LOG.debug("unexpected date format pattern: " + pattern); 1.99 + } 1.100 + 1.101 + instance = new SimpleDateFormat(pattern); 1.102 + } 1.103 + return instance; 1.104 + } 1.105 + 1.106 + private abstract static class CalendarDateFormat extends java.text.DateFormat { 1.107 + /** 1.108 + * 1.109 + */ 1.110 + private static final long serialVersionUID = -4191402739860280205L; 1.111 + 1.112 + private static final java.util.TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault(); 1.113 + 1.114 + private final String pattern; 1.115 + 1.116 + private boolean lenient = true; 1.117 + 1.118 + private java.util.TimeZone timeZone = DEFAULT_TIME_ZONE; 1.119 + 1.120 + public CalendarDateFormat(String pattern) { 1.121 + this.pattern = pattern; 1.122 + } 1.123 + 1.124 + public java.util.TimeZone getTimeZone() { 1.125 + return this.timeZone; 1.126 + } 1.127 + 1.128 + public void setTimeZone(java.util.TimeZone tz) { 1.129 + this.timeZone = tz; 1.130 + } 1.131 + 1.132 + public void setLenient(boolean lenient) { 1.133 + this.lenient = lenient; 1.134 + } 1.135 + 1.136 + public boolean isLenient() { 1.137 + return lenient; 1.138 + } 1.139 + 1.140 + public java.util.Calendar getCalendar() { 1.141 + throw new UnsupportedOperationException(); 1.142 + } 1.143 + 1.144 + public void setCalendar(java.util.Calendar c) { 1.145 + throw new UnsupportedOperationException(); 1.146 + } 1.147 + 1.148 + public NumberFormat getNumberFormat() { 1.149 + throw new UnsupportedOperationException(); 1.150 + } 1.151 + 1.152 + public void setNumberFormat(NumberFormat n) { 1.153 + throw new UnsupportedOperationException(); 1.154 + } 1.155 + 1.156 + public Object clone() { 1.157 + // don't call super.clone() 1.158 + final CalendarDateFormat f = (CalendarDateFormat) CalendarDateFormatFactory.getInstance(pattern); 1.159 + f.setTimeZone(getTimeZone()); 1.160 + f.setLenient(isLenient()); 1.161 + return f; 1.162 + } 1.163 + 1.164 + public boolean equals(Object o) { 1.165 + if (this == o) { 1.166 + return true; 1.167 + } 1.168 + if (o == null || getClass() != o.getClass()) { 1.169 + return false; 1.170 + } 1.171 + if (!super.equals(o)) { 1.172 + return false; 1.173 + } 1.174 + 1.175 + final CalendarDateFormat that = (CalendarDateFormat) o; 1.176 + 1.177 + if (lenient != that.lenient) { 1.178 + return false; 1.179 + } 1.180 + if (!pattern.equals(that.pattern)) { 1.181 + return false; 1.182 + } 1.183 + if (!timeZone.equals(that.timeZone)) { 1.184 + return false; 1.185 + } 1.186 + 1.187 + return true; 1.188 + } 1.189 + 1.190 + public int hashCode() { 1.191 + int result = super.hashCode(); 1.192 + result = 31 * result + pattern.hashCode(); 1.193 + result = 31 * result + (lenient ? 1 : 0); 1.194 + result = 31 * result + timeZone.hashCode(); 1.195 + return result; 1.196 + } 1.197 + } 1.198 + 1.199 + /** 1.200 + * A custom date-time formatter. 1.201 + * Parses and formats these patterns: 1.202 + * 1.203 + * <pre> 1.204 + * yyyyMMdd'T'HHmmss 1.205 + * yyyyMMdd'T'HHmmss'Z' 1.206 + * </pre> 1.207 + */ 1.208 + private static class DateTimeFormat extends CalendarDateFormat { 1.209 + 1.210 + /** 1.211 + * 1.212 + */ 1.213 + private static final long serialVersionUID = 3005824302269636122L; 1.214 + 1.215 + final boolean patternEndsWithZ; 1.216 + 1.217 + public DateTimeFormat(String pattern) { 1.218 + super(pattern); 1.219 + patternEndsWithZ = pattern.endsWith("'Z'"); 1.220 + } 1.221 + 1.222 + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { 1.223 + final java.util.Calendar cal = new GregorianCalendar(getTimeZone()); 1.224 + cal.setTimeInMillis(date.getTime()); 1.225 + 1.226 + appendPadded(toAppendTo, cal.get(GregorianCalendar.YEAR), 4); 1.227 + appendPadded(toAppendTo, cal.get(GregorianCalendar.MONTH) + 1, 2); 1.228 + appendPadded(toAppendTo, cal.get(GregorianCalendar.DAY_OF_MONTH), 2); 1.229 + toAppendTo.append("T"); 1.230 + 1.231 + appendPadded(toAppendTo, cal.get(GregorianCalendar.HOUR_OF_DAY), 2); 1.232 + appendPadded(toAppendTo, cal.get(GregorianCalendar.MINUTE), 2); 1.233 + appendPadded(toAppendTo, cal.get(GregorianCalendar.SECOND), 2); 1.234 + 1.235 + if (patternEndsWithZ) { 1.236 + toAppendTo.append("Z"); 1.237 + } 1.238 + 1.239 + return toAppendTo; 1.240 + } 1.241 + 1.242 + public Date parse(String source, ParsePosition pos) { 1.243 + // if lenient ignore superfluous input.. 1.244 + if (patternEndsWithZ) { 1.245 + if (source.length() > DATETIME_UTC_PATTERN.length() && !isLenient()) { 1.246 + pos.setErrorIndex(DATETIME_UTC_PATTERN.length()); 1.247 + return null; 1.248 + } 1.249 + } else if (source.length() > DATETIME_PATTERN.length() && !isLenient()) { 1.250 + pos.setErrorIndex(DATETIME_PATTERN.length()); 1.251 + return null; 1.252 + } 1.253 + 1.254 + try { 1.255 + if (source.charAt(8) != 'T') { 1.256 + pos.setErrorIndex(8); 1.257 + return null; 1.258 + } 1.259 + if (patternEndsWithZ && source.charAt(15) != 'Z') { 1.260 + pos.setErrorIndex(15); 1.261 + return null; 1.262 + } 1.263 + 1.264 + final int year = Integer.parseInt(source.substring(0, 4)); 1.265 + final int month = Integer.parseInt(source.substring(4, 6)) - 1; 1.266 + final int day = Integer.parseInt(source.substring(6, 8)); 1.267 + final int hour = Integer.parseInt(source.substring(9, 11)); 1.268 + final int minute = Integer.parseInt(source.substring(11, 13)); 1.269 + final int second = Integer.parseInt(source.substring(13, 15)); 1.270 + 1.271 + final Date d = makeCalendar(isLenient(), getTimeZone(), 1.272 + year, month, day, hour, minute, second).getTime(); 1.273 + pos.setIndex(15); 1.274 + return d; 1.275 + } catch (Exception e) { 1.276 + return null; 1.277 + } 1.278 + } 1.279 + } 1.280 + 1.281 + /** 1.282 + * Custom date formatter. 1.283 + * Parses and formats this pattern: 1.284 + * 1.285 + * <pre> 1.286 + * yyyyMMdd 1.287 + * </pre> 1.288 + */ 1.289 + private static class DateFormat extends CalendarDateFormat { 1.290 + 1.291 + /** 1.292 + * 1.293 + */ 1.294 + private static final long serialVersionUID = -7626077667268431779L; 1.295 + 1.296 + public DateFormat(String pattern) { 1.297 + super(pattern); 1.298 + } 1.299 + 1.300 + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { 1.301 + final java.util.Calendar cal = java.util.Calendar.getInstance(getTimeZone()); 1.302 + cal.setTimeInMillis(date.getTime()); 1.303 + 1.304 + appendPadded(toAppendTo, cal.get(GregorianCalendar.YEAR), 4); 1.305 + appendPadded(toAppendTo, cal.get(GregorianCalendar.MONTH) + 1, 2); 1.306 + appendPadded(toAppendTo, cal.get(GregorianCalendar.DAY_OF_MONTH), 2); 1.307 + 1.308 + return toAppendTo; 1.309 + } 1.310 + 1.311 + public Date parse(String source, ParsePosition pos) { 1.312 + // if lenient ignore superfluous input.. 1.313 + if (source.length() > DATE_PATTERN.length() && !isLenient()) { 1.314 + pos.setErrorIndex(DATE_PATTERN.length()); 1.315 + return null; 1.316 + } 1.317 + 1.318 + try { 1.319 + final int year = Integer.parseInt(source.substring(0, 4)); 1.320 + final int month = Integer.parseInt(source.substring(4, 6)) - 1; 1.321 + final int day = Integer.parseInt(source.substring(6, 8)); 1.322 + 1.323 + final Date d = makeCalendar(isLenient(), getTimeZone(), year, month, day).getTime(); 1.324 + pos.setIndex(8); 1.325 + return d; 1.326 + } catch (Exception e) { 1.327 + return null; 1.328 + } 1.329 + } 1.330 + } 1.331 + 1.332 + /** 1.333 + * Custom time formatter. 1.334 + * Parses and formats these patterns: 1.335 + * 1.336 + * <pre> 1.337 + * HHmmss 1.338 + * HHmmss'Z' 1.339 + * </pre> 1.340 + */ 1.341 + private static class TimeFormat extends CalendarDateFormat { 1.342 + 1.343 + /** 1.344 + * 1.345 + */ 1.346 + private static final long serialVersionUID = -1367114409994225425L; 1.347 + 1.348 + final boolean patternEndsWithZ; 1.349 + 1.350 + public TimeFormat(String pattern) { 1.351 + super(pattern); 1.352 + patternEndsWithZ = pattern.endsWith("'Z'"); 1.353 + } 1.354 + 1.355 + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { 1.356 + final java.util.Calendar cal = new GregorianCalendar(getTimeZone()); 1.357 + cal.setTimeInMillis(date.getTime()); 1.358 + 1.359 + appendPadded(toAppendTo, cal.get(GregorianCalendar.HOUR_OF_DAY), 2); 1.360 + appendPadded(toAppendTo, cal.get(GregorianCalendar.MINUTE), 2); 1.361 + appendPadded(toAppendTo, cal.get(GregorianCalendar.SECOND), 2); 1.362 + 1.363 + if (patternEndsWithZ) { 1.364 + toAppendTo.append("Z"); 1.365 + } 1.366 + 1.367 + return toAppendTo; 1.368 + } 1.369 + 1.370 + public Date parse(String source, ParsePosition pos) { 1.371 + // if lenient ignore superfluous input.. 1.372 + if (patternEndsWithZ) { 1.373 + if (source.length() > TIME_UTC_PATTERN.length() && !isLenient()) { 1.374 + pos.setErrorIndex(TIME_UTC_PATTERN.length()); 1.375 + return null; 1.376 + } 1.377 + } else if (source.length() > TIME_PATTERN.length() && !isLenient()) { 1.378 + pos.setErrorIndex(TIME_PATTERN.length()); 1.379 + return null; 1.380 + } 1.381 + 1.382 + try { 1.383 + if (patternEndsWithZ && source.charAt(6) != 'Z') { 1.384 + pos.setErrorIndex(6); 1.385 + return null; 1.386 + } 1.387 + 1.388 + final int hour = Integer.parseInt(source.substring(0, 2)); 1.389 + final int minute = Integer.parseInt(source.substring(2, 4)); 1.390 + final int second = Integer.parseInt(source.substring(4, 6)); 1.391 + 1.392 + final Date d = makeCalendar(isLenient(), getTimeZone(), 1970, 0, 1, hour, minute, second).getTime(); 1.393 + pos.setIndex(6); 1.394 + return d; 1.395 + } catch (Exception e) { 1.396 + return null; 1.397 + } 1.398 + } 1.399 + } 1.400 + 1.401 + private static java.util.Calendar makeCalendar(boolean lenient, java.util.TimeZone timeZone, int year, 1.402 + int zeroBasedMonth, int day, int hour, int minutes, int seconds) { 1.403 + final java.util.Calendar cal = new GregorianCalendar(timeZone); 1.404 + cal.setLenient(lenient); 1.405 + cal.set(year, zeroBasedMonth, day, hour, minutes, seconds); 1.406 + cal.set(java.util.Calendar.MILLISECOND, 0); 1.407 + return cal; 1.408 + } 1.409 + 1.410 + private static java.util.Calendar makeCalendar(boolean lenient, TimeZone timeZone, int year, int month, int day) { 1.411 + return makeCalendar(lenient, timeZone, year, month, day, 0, 0, 0); 1.412 + } 1.413 + 1.414 + private static void appendPadded(StringBuffer toAppendTo, int value, int fieldWidth) { 1.415 + final String s = Integer.toString(value); 1.416 + final int max = fieldWidth - s.length(); 1.417 + for (int i = 0; i < max; i++) { 1.418 + toAppendTo.append("0"); 1.419 + } 1.420 + toAppendTo.append(s); 1.421 + } 1.422 + 1.423 +}