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

changeset 0
fb9019fb1bf7
     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 +}

mercurial