src/net/fortuna/ical4j/model/component/Observance.java

branch
ICAL4J_EMBED_1
changeset 15
cc93757aeca3
parent 14
5ae3e5665a0b
child 18
6dcaece8ec41
     1.1 --- a/src/net/fortuna/ical4j/model/component/Observance.java	Thu Feb 12 18:02:00 2015 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,334 +0,0 @@
     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.component;
    1.36 -
    1.37 -import java.io.IOException;
    1.38 -import java.text.DateFormat;
    1.39 -import java.text.ParseException;
    1.40 -import java.text.SimpleDateFormat;
    1.41 -import java.util.Arrays;
    1.42 -import java.util.Calendar;
    1.43 -import java.util.Collections;
    1.44 -import java.util.Iterator;
    1.45 -import java.util.Map;
    1.46 -import java.util.TreeMap;
    1.47 -
    1.48 -import net.fortuna.ical4j.model.Component;
    1.49 -import net.fortuna.ical4j.model.Date;
    1.50 -import net.fortuna.ical4j.model.DateList;
    1.51 -import net.fortuna.ical4j.model.DateTime;
    1.52 -import net.fortuna.ical4j.model.Property;
    1.53 -import net.fortuna.ical4j.model.PropertyList;
    1.54 -import net.fortuna.ical4j.model.ValidationException;
    1.55 -import net.fortuna.ical4j.model.parameter.Value;
    1.56 -import net.fortuna.ical4j.model.property.DtStart;
    1.57 -import net.fortuna.ical4j.model.property.RDate;
    1.58 -import net.fortuna.ical4j.model.property.RRule;
    1.59 -import net.fortuna.ical4j.model.property.TzOffsetFrom;
    1.60 -import net.fortuna.ical4j.model.property.TzOffsetTo;
    1.61 -import net.fortuna.ical4j.util.Dates;
    1.62 -import net.fortuna.ical4j.util.PropertyValidator;
    1.63 -import net.fortuna.ical4j.util.TimeZones;
    1.64 -
    1.65 -import org.apache.commons.logging.Log;
    1.66 -import org.apache.commons.logging.LogFactory;
    1.67 -
    1.68 -/**
    1.69 - * $Id$ [05-Apr-2004]
    1.70 - *
    1.71 - * Defines an iCalendar sub-component representing a timezone observance. Class made abstract such that only Standard
    1.72 - * and Daylight instances are valid.
    1.73 - * @author Ben Fortuna
    1.74 - */
    1.75 -public abstract class Observance extends Component {
    1.76 -
    1.77 -    /**
    1.78 -     * 
    1.79 -     */
    1.80 -    private static final long serialVersionUID = 2523330383042085994L;
    1.81 -
    1.82 -    /**
    1.83 -     * one of 'standardc' or 'daylightc' MUST occur and each MAY occur more than once.
    1.84 -     */
    1.85 -    public static final String STANDARD = "STANDARD";
    1.86 -
    1.87 -    /**
    1.88 -     * Token for daylight observance.
    1.89 -     */
    1.90 -    public static final String DAYLIGHT = "DAYLIGHT";
    1.91 -
    1.92 -    // TODO: clear cache when observance definition changes (??)
    1.93 -    private long[] onsetsMillisec;
    1.94 -    private DateTime[] onsetsDates;
    1.95 -    private Map onsets = new TreeMap();
    1.96 -    private Date initialOnset = null;
    1.97 -    
    1.98 -    /**
    1.99 -     * Used for parsing times in a UTC date-time representation.
   1.100 -     */
   1.101 -    private static final String UTC_PATTERN = "yyyyMMdd'T'HHmmss";
   1.102 -    private static final DateFormat UTC_FORMAT = new SimpleDateFormat(
   1.103 -            UTC_PATTERN);
   1.104 -    
   1.105 -    static {
   1.106 -        UTC_FORMAT.setTimeZone(TimeZones.getUtcTimeZone());
   1.107 -        UTC_FORMAT.setLenient(false);
   1.108 -    }
   1.109 -
   1.110 -    /* If this is set we have rrules. If we get a date after this rebuild onsets */
   1.111 -    private Date onsetLimit;
   1.112 -
   1.113 -    /**
   1.114 -     * Constructs a timezone observance with the specified name and no properties.
   1.115 -     * @param name the name of this observance component
   1.116 -     */
   1.117 -    protected Observance(final String name) {
   1.118 -        super(name);
   1.119 -    }
   1.120 -
   1.121 -    /**
   1.122 -     * Constructor protected to enforce use of sub-classes from this library.
   1.123 -     * @param name the name of the time type
   1.124 -     * @param properties a list of properties
   1.125 -     */
   1.126 -    protected Observance(final String name, final PropertyList properties) {
   1.127 -        super(name, properties);
   1.128 -    }
   1.129 -
   1.130 -    /**
   1.131 -     * {@inheritDoc}
   1.132 -     */
   1.133 -    public final void validate(final boolean recurse) throws ValidationException {
   1.134 -
   1.135 -        // From "4.8.3.3 Time Zone Offset From":
   1.136 -        // Conformance: This property MUST be specified in a "VTIMEZONE"
   1.137 -        // calendar component.
   1.138 -        PropertyValidator.getInstance().assertOne(Property.TZOFFSETFROM,
   1.139 -                getProperties());
   1.140 -
   1.141 -        // From "4.8.3.4 Time Zone Offset To":
   1.142 -        // Conformance: This property MUST be specified in a "VTIMEZONE"
   1.143 -        // calendar component.
   1.144 -        PropertyValidator.getInstance().assertOne(Property.TZOFFSETTO,
   1.145 -                getProperties());
   1.146 -
   1.147 -        /*
   1.148 -         * ; the following are each REQUIRED, ; but MUST NOT occur more than once dtstart / tzoffsetto / tzoffsetfrom /
   1.149 -         */
   1.150 -        PropertyValidator.getInstance().assertOne(Property.DTSTART,
   1.151 -                getProperties());
   1.152 -
   1.153 -        /*
   1.154 -         * ; the following are optional, ; and MAY occur more than once comment / rdate / rrule / tzname / x-prop
   1.155 -         */
   1.156 -
   1.157 -        if (recurse) {
   1.158 -            validateProperties();
   1.159 -        }
   1.160 -    }
   1.161 -
   1.162 -    /**
   1.163 -     * Returns the latest applicable onset of this observance for the specified date.
   1.164 -     * @param date the latest date that an observance onset may occur
   1.165 -     * @return the latest applicable observance date or null if there is no applicable observance onset for the
   1.166 -     * specified date
   1.167 -     */
   1.168 -    public final Date getLatestOnset(final Date date) {
   1.169 -        
   1.170 -        if (initialOnset == null) {
   1.171 -            try {
   1.172 -                initialOnset = applyOffsetFrom(calculateOnset(((DtStart) getProperty(Property.DTSTART)).getDate()));
   1.173 -            } catch (ParseException e) {
   1.174 -                Log log = LogFactory.getLog(Observance.class);
   1.175 -                log.error("Unexpected error calculating initial onset", e);
   1.176 -                // XXX: is this correct?
   1.177 -                return null;
   1.178 -            }
   1.179 -        }
   1.180 -        
   1.181 -        // observance not applicable if date is before the effective date of this observance..
   1.182 -        if (date.before(initialOnset)) {
   1.183 -            return null;
   1.184 -        }
   1.185 -
   1.186 -        if ((onsetsMillisec != null) && (onsetLimit == null || date.before(onsetLimit))) {
   1.187 -            return getCachedOnset(date);
   1.188 -        }
   1.189 -
   1.190 -        Date onset = initialOnset;
   1.191 -        Date initialOnsetUTC;
   1.192 -        // get first onset without adding TZFROM as this may lead to a day boundary
   1.193 -        // change which would be incompatible with BYDAY RRULES
   1.194 -        // we will have to add the offset to all cacheable onsets
   1.195 -        try {
   1.196 -            initialOnsetUTC = calculateOnset(((DtStart) getProperty(Property.DTSTART)).getDate());
   1.197 -        } catch (ParseException e) {
   1.198 -            Log log = LogFactory.getLog(Observance.class);
   1.199 -            log.error("Unexpected error calculating initial onset", e);
   1.200 -            // XXX: is this correct?
   1.201 -            return null;
   1.202 -        }
   1.203 -        // collect all onsets for the purposes of caching..
   1.204 -        final DateList cacheableOnsets = new DateList();
   1.205 -        cacheableOnsets.setUtc(true);
   1.206 -        cacheableOnsets.add(initialOnset);
   1.207 -
   1.208 -        // check rdates for latest applicable onset..
   1.209 -        final PropertyList rdates = getProperties(Property.RDATE);
   1.210 -        for (final Iterator i = rdates.iterator(); i.hasNext();) {
   1.211 -            final RDate rdate = (RDate) i.next();
   1.212 -            for (final Iterator j = rdate.getDates().iterator(); j.hasNext();) {
   1.213 -                try {
   1.214 -                    final DateTime rdateOnset = applyOffsetFrom(calculateOnset((Date) j.next()));
   1.215 -                    if (!rdateOnset.after(date) && rdateOnset.after(onset)) {
   1.216 -                        onset = rdateOnset;
   1.217 -                    }
   1.218 -                    /*
   1.219 -                     * else if (rdateOnset.after(date) && rdateOnset.after(onset) && (nextOnset == null ||
   1.220 -                     * rdateOnset.before(nextOnset))) { nextOnset = rdateOnset; }
   1.221 -                     */
   1.222 -                    cacheableOnsets.add(rdateOnset);
   1.223 -                } catch (ParseException e) {
   1.224 -                    Log log = LogFactory.getLog(Observance.class);
   1.225 -                    log.error("Unexpected error calculating onset", e);
   1.226 -                }
   1.227 -            }
   1.228 -        }
   1.229 -
   1.230 -        // check recurrence rules for latest applicable onset..
   1.231 -        final PropertyList rrules = getProperties(Property.RRULE);
   1.232 -        for (final Iterator i = rrules.iterator(); i.hasNext();) {
   1.233 -            final RRule rrule = (RRule) i.next();
   1.234 -            // include future onsets to determine onset period..
   1.235 -            final Calendar cal = Dates.getCalendarInstance(date);
   1.236 -            cal.setTime(date);
   1.237 -            cal.add(Calendar.YEAR, 10);
   1.238 -            onsetLimit = Dates.getInstance(cal.getTime(), Value.DATE_TIME);
   1.239 -            final DateList recurrenceDates = rrule.getRecur().getDates(initialOnsetUTC,
   1.240 -                    onsetLimit, Value.DATE_TIME);
   1.241 -            for (final Iterator j = recurrenceDates.iterator(); j.hasNext();) {
   1.242 -                final DateTime rruleOnset = applyOffsetFrom((DateTime) j.next());
   1.243 -                if (!rruleOnset.after(date) && rruleOnset.after(onset)) {
   1.244 -                    onset = rruleOnset;
   1.245 -                }
   1.246 -                /*
   1.247 -                 * else if (rruleOnset.after(date) && rruleOnset.after(onset) && (nextOnset == null ||
   1.248 -                 * rruleOnset.before(nextOnset))) { nextOnset = rruleOnset; }
   1.249 -                 */
   1.250 -                cacheableOnsets.add(rruleOnset);
   1.251 -            }
   1.252 -        }
   1.253 -
   1.254 -        // cache onsets..
   1.255 -        Collections.sort(cacheableOnsets);
   1.256 -        DateTime cacheableOnset = null;
   1.257 -        this.onsetsMillisec = new long[cacheableOnsets.size()];
   1.258 -        this.onsetsDates = new DateTime[onsetsMillisec.length];
   1.259 -
   1.260 -        for (int i = 0; i < onsetsMillisec.length; i++) {
   1.261 -            cacheableOnset = (DateTime)cacheableOnsets.get(i);
   1.262 -            onsetsMillisec[i] = cacheableOnset.getTime();
   1.263 -            onsetsDates[i] = cacheableOnset;
   1.264 -        }
   1.265 -
   1.266 -        return onset;
   1.267 -    }
   1.268 -
   1.269 -    /**
   1.270 -     * Returns a cached onset for the specified date.
   1.271 -     * @param date
   1.272 -     * @return a cached onset date or null if no cached onset is applicable for the specified date
   1.273 -     */
   1.274 -    private DateTime getCachedOnset(final Date date) {
   1.275 -        int index = Arrays.binarySearch(onsetsMillisec, date.getTime());
   1.276 -        if (index >= 0) {
   1.277 -            return onsetsDates[index];
   1.278 -        } else {
   1.279 -            int insertionIndex = -index -1;
   1.280 -            return onsetsDates[insertionIndex -1];
   1.281 -        }
   1.282 -    }
   1.283 -
   1.284 -    /**
   1.285 -     * Returns the mandatory dtstart property.
   1.286 -     * @return the DTSTART property or null if not specified
   1.287 -     */
   1.288 -    public final DtStart getStartDate() {
   1.289 -        return (DtStart) getProperty(Property.DTSTART);
   1.290 -    }
   1.291 -
   1.292 -    /**
   1.293 -     * Returns the mandatory tzoffsetfrom property.
   1.294 -     * @return the TZOFFSETFROM property or null if not specified
   1.295 -     */
   1.296 -    public final TzOffsetFrom getOffsetFrom() {
   1.297 -        return (TzOffsetFrom) getProperty(Property.TZOFFSETFROM);
   1.298 -    }
   1.299 -
   1.300 -    /**
   1.301 -     * Returns the mandatory tzoffsetto property.
   1.302 -     * @return the TZOFFSETTO property or null if not specified
   1.303 -     */
   1.304 -    public final TzOffsetTo getOffsetTo() {
   1.305 -        return (TzOffsetTo) getProperty(Property.TZOFFSETTO);
   1.306 -    }
   1.307 -    
   1.308 -//    private Date calculateOnset(DateProperty dateProperty) {
   1.309 -//        return calculateOnset(dateProperty.getValue());
   1.310 -//    }
   1.311 -//    
   1.312 -    private DateTime calculateOnset(Date date) throws ParseException {
   1.313 -        return calculateOnset(date.toString());
   1.314 -    }
   1.315 -    
   1.316 -    private DateTime calculateOnset(String dateStr) throws ParseException {
   1.317 -        
   1.318 -        // Translate local onset into UTC time by parsing local time 
   1.319 -        // as GMT and adjusting by TZOFFSETFROM if required
   1.320 -        long utcOnset;
   1.321 -       
   1.322 -        synchronized (UTC_FORMAT) {
   1.323 -            utcOnset = UTC_FORMAT.parse(dateStr).getTime();
   1.324 -        }
   1.325 -
   1.326 -        // return a UTC
   1.327 -        DateTime onset = new DateTime(true);
   1.328 -        onset.setTime(utcOnset);
   1.329 -        return onset;
   1.330 -    }
   1.331 -
   1.332 -    private DateTime applyOffsetFrom(DateTime orig) {
   1.333 -        DateTime withOffset = new DateTime(true);
   1.334 -        withOffset.setTime(orig.getTime() - getOffsetFrom().getOffset().getOffset());
   1.335 -        return withOffset;
   1.336 -    }
   1.337 -}
   1.338 \ No newline at end of file

mercurial