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

changeset 0
fb9019fb1bf7
child 3
73bdfa70b04e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/net/fortuna/ical4j/model/component/VTimeZone.java	Tue Feb 10 18:12:00 2015 +0100
     1.3 @@ -0,0 +1,379 @@
     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.net.URISyntaxException;
    1.39 +import java.text.ParseException;
    1.40 +import java.util.Iterator;
    1.41 +
    1.42 +import net.fortuna.ical4j.model.Component;
    1.43 +import net.fortuna.ical4j.model.ComponentList;
    1.44 +import net.fortuna.ical4j.model.Date;
    1.45 +import net.fortuna.ical4j.model.Property;
    1.46 +import net.fortuna.ical4j.model.PropertyList;
    1.47 +import net.fortuna.ical4j.model.ValidationException;
    1.48 +import net.fortuna.ical4j.model.Validator;
    1.49 +import net.fortuna.ical4j.model.property.LastModified;
    1.50 +import net.fortuna.ical4j.model.property.Method;
    1.51 +import net.fortuna.ical4j.model.property.TzId;
    1.52 +import net.fortuna.ical4j.model.property.TzUrl;
    1.53 +import net.fortuna.ical4j.util.PropertyValidator;
    1.54 +import net.fortuna.ical4j.util.Strings;
    1.55 +
    1.56 +import org.apache.commons.lang.ObjectUtils;
    1.57 +import org.apache.commons.lang.builder.HashCodeBuilder;
    1.58 +
    1.59 +/**
    1.60 + * $Id$ [Apr 5, 2004]
    1.61 + *
    1.62 + * Defines an iCalendar VTIMEZONE component.
    1.63 + * 
    1.64 + * <pre>
    1.65 + *       4.6.5 Time Zone Component
    1.66 + *  
    1.67 + *          Component Name: VTIMEZONE
    1.68 + *  
    1.69 + *          Purpose: Provide a grouping of component properties that defines a
    1.70 + *          time zone.
    1.71 + *  
    1.72 + *          Formal Definition: A &quot;VTIMEZONE&quot; calendar component is defined by the
    1.73 + *          following notation:
    1.74 + *  
    1.75 + *            timezonec  = &quot;BEGIN&quot; &quot;:&quot; &quot;VTIMEZONE&quot; CRLF
    1.76 + *  
    1.77 + *                         2*(
    1.78 + *  
    1.79 + *                         ; 'tzid' is required, but MUST NOT occur more
    1.80 + *                         ; than once
    1.81 + *  
    1.82 + *                       tzid /
    1.83 + *  
    1.84 + *                         ; 'last-mod' and 'tzurl' are optional,
    1.85 + *                       but MUST NOT occur more than once
    1.86 + *  
    1.87 + *                       last-mod / tzurl /
    1.88 + *  
    1.89 + *                         ; one of 'standardc' or 'daylightc' MUST occur
    1.90 + *                       ..; and each MAY occur more than once.
    1.91 + *  
    1.92 + *                       standardc / daylightc /
    1.93 + *  
    1.94 + *                       ; the following is optional,
    1.95 + *                       ; and MAY occur more than once
    1.96 + *  
    1.97 + *                         x-prop
    1.98 + *  
    1.99 + *                         )
   1.100 + *  
   1.101 + *                         &quot;END&quot; &quot;:&quot; &quot;VTIMEZONE&quot; CRLF
   1.102 + *  
   1.103 + *            standardc  = &quot;BEGIN&quot; &quot;:&quot; &quot;STANDARD&quot; CRLF
   1.104 + *  
   1.105 + *                         tzprop
   1.106 + *  
   1.107 + *                         &quot;END&quot; &quot;:&quot; &quot;STANDARD&quot; CRLF
   1.108 + *  
   1.109 + *            daylightc  = &quot;BEGIN&quot; &quot;:&quot; &quot;DAYLIGHT&quot; CRLF
   1.110 + *  
   1.111 + *                         tzprop
   1.112 + *  
   1.113 + *                         &quot;END&quot; &quot;:&quot; &quot;DAYLIGHT&quot; CRLF
   1.114 + *  
   1.115 + *            tzprop     = 3*(
   1.116 + *  
   1.117 + *                       ; the following are each REQUIRED,
   1.118 + *                       ; but MUST NOT occur more than once
   1.119 + *  
   1.120 + *                       dtstart / tzoffsetto / tzoffsetfrom /
   1.121 + *  
   1.122 + *                       ; the following are optional,
   1.123 + *                       ; and MAY occur more than once
   1.124 + *  
   1.125 + *                       comment / rdate / rrule / tzname / x-prop
   1.126 + *  
   1.127 + *                       )
   1.128 + * </pre>
   1.129 + * 
   1.130 + * @author Ben Fortuna
   1.131 + */
   1.132 +public class VTimeZone extends CalendarComponent {
   1.133 +
   1.134 +    private static final long serialVersionUID = 5629679741050917815L;
   1.135 +
   1.136 +    private final Validator itipValidator = new ITIPValidator();
   1.137 +    
   1.138 +    private ComponentList observances;
   1.139 +
   1.140 +    /**
   1.141 +     * Default constructor.
   1.142 +     */
   1.143 +    public VTimeZone() {
   1.144 +        super(VTIMEZONE);
   1.145 +        this.observances = new ComponentList();
   1.146 +    }
   1.147 +
   1.148 +    /**
   1.149 +     * Constructs a new instance containing the specified properties.
   1.150 +     * @param properties a list of properties
   1.151 +     */
   1.152 +    public VTimeZone(final PropertyList properties) {
   1.153 +        super(VTIMEZONE, properties);
   1.154 +        this.observances = new ComponentList();
   1.155 +    }
   1.156 +
   1.157 +    /**
   1.158 +     * Constructs a new vtimezone component with no properties and the specified list of type components.
   1.159 +     * @param observances a list of type components
   1.160 +     */
   1.161 +    public VTimeZone(final ComponentList observances) {
   1.162 +        super(VTIMEZONE);
   1.163 +        this.observances = observances;
   1.164 +    }
   1.165 +
   1.166 +    /**
   1.167 +     * Constructor.
   1.168 +     * @param properties a list of properties
   1.169 +     * @param observances a list of timezone types
   1.170 +     */
   1.171 +    public VTimeZone(final PropertyList properties,
   1.172 +            final ComponentList observances) {
   1.173 +        super(VTIMEZONE, properties);
   1.174 +        this.observances = observances;
   1.175 +    }
   1.176 +
   1.177 +    /**
   1.178 +     * {@inheritDoc}
   1.179 +     */
   1.180 +    public final String toString() {
   1.181 +        final StringBuffer b = new StringBuffer();
   1.182 +        b.append(BEGIN);
   1.183 +        b.append(':');
   1.184 +        b.append(getName());
   1.185 +        b.append(Strings.LINE_SEPARATOR);
   1.186 +        b.append(getProperties());
   1.187 +        b.append(observances);
   1.188 +        b.append(END);
   1.189 +        b.append(':');
   1.190 +        b.append(getName());
   1.191 +        b.append(Strings.LINE_SEPARATOR);
   1.192 +        return b.toString();
   1.193 +    }
   1.194 +
   1.195 +    /**
   1.196 +     * {@inheritDoc}
   1.197 +     */
   1.198 +    public final void validate(final boolean recurse)
   1.199 +            throws ValidationException {
   1.200 +
   1.201 +        /*
   1.202 +         * ; 'tzid' is required, but MUST NOT occur more ; than once tzid /
   1.203 +         */
   1.204 +        PropertyValidator.getInstance().assertOne(Property.TZID,
   1.205 +                getProperties());
   1.206 +
   1.207 +        /*
   1.208 +         * ; 'last-mod' and 'tzurl' are optional, but MUST NOT occur more than once last-mod / tzurl /
   1.209 +         */
   1.210 +        PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED,
   1.211 +                getProperties());
   1.212 +        PropertyValidator.getInstance().assertOneOrLess(Property.TZURL,
   1.213 +                getProperties());
   1.214 +
   1.215 +        /*
   1.216 +         * ; one of 'standardc' or 'daylightc' MUST occur ..; and each MAY occur more than once. standardc / daylightc /
   1.217 +         */
   1.218 +        if (getObservances().getComponent(Observance.STANDARD) == null
   1.219 +                && getObservances().getComponent(Observance.DAYLIGHT) == null) {
   1.220 +            throw new ValidationException("Sub-components ["
   1.221 +                    + Observance.STANDARD + "," + Observance.DAYLIGHT
   1.222 +                    + "] must be specified at least once");
   1.223 +        }
   1.224 +
   1.225 +        for (final Iterator i = getObservances().iterator(); i.hasNext();) {
   1.226 +            ((Component) i.next()).validate(recurse);
   1.227 +        }
   1.228 +        
   1.229 +        /*
   1.230 +         * ; the following is optional, ; and MAY occur more than once x-prop
   1.231 +         */
   1.232 +
   1.233 +        if (recurse) {
   1.234 +            validateProperties();
   1.235 +        }
   1.236 +    }
   1.237 +
   1.238 +    /**
   1.239 +     * {@inheritDoc}
   1.240 +     */
   1.241 +    protected Validator getValidator(Method method) {
   1.242 +        return itipValidator;
   1.243 +    }
   1.244 +
   1.245 +    /**
   1.246 +     * Common validation for all iTIP methods.
   1.247 +     * 
   1.248 +     * <pre>
   1.249 +     *    Component/Property  Presence
   1.250 +     *    ------------------- ----------------------------------------------
   1.251 +     *    VTIMEZONE           0+      MUST be present if any date/time refers
   1.252 +     *                                to timezone
   1.253 +     *        DAYLIGHT        0+      MUST be one or more of either STANDARD or
   1.254 +     *                                DAYLIGHT
   1.255 +     *           COMMENT      0 or 1
   1.256 +     *           DTSTART      1       MUST be local time format
   1.257 +     *           RDATE        0+      if present RRULE MUST NOT be present
   1.258 +     *           RRULE        0+      if present RDATE MUST NOT be present
   1.259 +     *           TZNAME       0 or 1
   1.260 +     *           TZOFFSET     1
   1.261 +     *           TZOFFSETFROM 1
   1.262 +     *           TZOFFSETTO   1
   1.263 +     *           X-PROPERTY   0+
   1.264 +     *        LAST-MODIFIED   0 or 1
   1.265 +     *        STANDARD        0+      MUST be one or more of either STANDARD or
   1.266 +     *                                DAYLIGHT
   1.267 +     *           COMMENT      0 or 1
   1.268 +     *           DTSTART      1       MUST be local time format
   1.269 +     *           RDATE        0+      if present RRULE MUST NOT be present
   1.270 +     *           RRULE        0+      if present RDATE MUST NOT be present
   1.271 +     *           TZNAME       0 or 1
   1.272 +     *           TZOFFSETFROM 1
   1.273 +     *           TZOFFSETTO   1
   1.274 +     *           X-PROPERTY   0+
   1.275 +     *        TZID            1
   1.276 +     *        TZURL           0 or 1
   1.277 +     *        X-PROPERTY      0+
   1.278 +     * </pre>
   1.279 +     */
   1.280 +    private class ITIPValidator implements Validator {
   1.281 +        
   1.282 +		private static final long serialVersionUID = 1L;
   1.283 +
   1.284 +        /**
   1.285 +         * {@inheritDoc}
   1.286 +         */
   1.287 +        public void validate() throws ValidationException {
   1.288 +            for (final Iterator i = getObservances().iterator(); i.hasNext();) {
   1.289 +                final Observance observance = (Observance) i.next();
   1.290 +                PropertyValidator.getInstance().assertOne(Property.DTSTART, observance.getProperties());
   1.291 +                PropertyValidator.getInstance().assertOne(Property.TZOFFSETFROM, observance.getProperties());
   1.292 +                PropertyValidator.getInstance().assertOne(Property.TZOFFSETTO, observance.getProperties());
   1.293 +                
   1.294 +                PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, observance.getProperties());
   1.295 +                PropertyValidator.getInstance().assertOneOrLess(Property.TZNAME, observance.getProperties());
   1.296 +            }
   1.297 +        }
   1.298 +    }
   1.299 +    
   1.300 +    /**
   1.301 +     * @return Returns the types.
   1.302 +     */
   1.303 +    public final ComponentList getObservances() {
   1.304 +        return observances;
   1.305 +    }
   1.306 +
   1.307 +    /**
   1.308 +     * Returns the latest applicable timezone observance for the specified date.
   1.309 +     * @param date the latest possible date for a timezone observance onset
   1.310 +     * @return the latest applicable timezone observance for the specified date or null if there are no applicable
   1.311 +     * observances
   1.312 +     */
   1.313 +    public final Observance getApplicableObservance(final Date date) {
   1.314 +        Observance latestObservance = null;
   1.315 +        Date latestOnset = null;
   1.316 +        for (final Iterator i = getObservances().iterator(); i.hasNext();) {
   1.317 +            final Observance observance = (Observance) i.next();
   1.318 +            final Date onset = observance.getLatestOnset(date);
   1.319 +            if (latestOnset == null
   1.320 +                    || (onset != null && onset.after(latestOnset))) {
   1.321 +                latestOnset = onset;
   1.322 +                latestObservance = observance;
   1.323 +            }
   1.324 +        }
   1.325 +        return latestObservance;
   1.326 +    }
   1.327 +
   1.328 +    /**
   1.329 +     * @return the mandatory timezone identifier property
   1.330 +     */
   1.331 +    public final TzId getTimeZoneId() {
   1.332 +        return (TzId) getProperty(Property.TZID);
   1.333 +    }
   1.334 +
   1.335 +    /**
   1.336 +     * @return the optional last-modified property
   1.337 +     */
   1.338 +    public final LastModified getLastModified() {
   1.339 +        return (LastModified) getProperty(Property.LAST_MODIFIED);
   1.340 +    }
   1.341 +
   1.342 +    /**
   1.343 +     * @return the optional timezone url property
   1.344 +     */
   1.345 +    public final TzUrl getTimeZoneUrl() {
   1.346 +        return (TzUrl) getProperty(Property.TZURL);
   1.347 +    }
   1.348 +
   1.349 +    /**
   1.350 +     * {@inheritDoc}
   1.351 +     */
   1.352 +    public boolean equals(final Object arg0) {
   1.353 +        if (arg0 instanceof VTimeZone) {
   1.354 +            return super.equals(arg0)
   1.355 +                    && ObjectUtils.equals(observances, ((VTimeZone) arg0)
   1.356 +                            .getObservances());
   1.357 +        }
   1.358 +        return super.equals(arg0);
   1.359 +    }
   1.360 +
   1.361 +    /**
   1.362 +     * {@inheritDoc}
   1.363 +     */
   1.364 +    public int hashCode() {
   1.365 +        return new HashCodeBuilder().append(getName()).append(getProperties())
   1.366 +                .append(getObservances()).toHashCode();
   1.367 +    }
   1.368 +
   1.369 +    /**
   1.370 +     * Overrides default copy method to add support for copying observance sub-components.
   1.371 +     * @return a copy of the instance
   1.372 +     * @throws ParseException where an error occurs parsing data
   1.373 +     * @throws IOException where an error occurs reading data
   1.374 +     * @throws URISyntaxException where an invalid URI is encountered
   1.375 +     * @see net.fortuna.ical4j.model.Component#copy()
   1.376 +     */
   1.377 +    public Component copy() throws ParseException, IOException, URISyntaxException {
   1.378 +        final VTimeZone copy = (VTimeZone) super.copy();
   1.379 +        copy.observances = new ComponentList(observances);
   1.380 +        return copy;
   1.381 +    }
   1.382 +}

mercurial