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

changeset 0
fb9019fb1bf7
child 3
73bdfa70b04e
equal deleted inserted replaced
-1:000000000000 0:b208ffaef969
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.component;
33
34 import java.io.IOException;
35 import java.net.URISyntaxException;
36 import java.text.ParseException;
37 import java.util.Iterator;
38
39 import net.fortuna.ical4j.model.Component;
40 import net.fortuna.ical4j.model.ComponentList;
41 import net.fortuna.ical4j.model.Date;
42 import net.fortuna.ical4j.model.Property;
43 import net.fortuna.ical4j.model.PropertyList;
44 import net.fortuna.ical4j.model.ValidationException;
45 import net.fortuna.ical4j.model.Validator;
46 import net.fortuna.ical4j.model.property.LastModified;
47 import net.fortuna.ical4j.model.property.Method;
48 import net.fortuna.ical4j.model.property.TzId;
49 import net.fortuna.ical4j.model.property.TzUrl;
50 import net.fortuna.ical4j.util.PropertyValidator;
51 import net.fortuna.ical4j.util.Strings;
52
53 import org.apache.commons.lang.ObjectUtils;
54 import org.apache.commons.lang.builder.HashCodeBuilder;
55
56 /**
57 * $Id$ [Apr 5, 2004]
58 *
59 * Defines an iCalendar VTIMEZONE component.
60 *
61 * <pre>
62 * 4.6.5 Time Zone Component
63 *
64 * Component Name: VTIMEZONE
65 *
66 * Purpose: Provide a grouping of component properties that defines a
67 * time zone.
68 *
69 * Formal Definition: A &quot;VTIMEZONE&quot; calendar component is defined by the
70 * following notation:
71 *
72 * timezonec = &quot;BEGIN&quot; &quot;:&quot; &quot;VTIMEZONE&quot; CRLF
73 *
74 * 2*(
75 *
76 * ; 'tzid' is required, but MUST NOT occur more
77 * ; than once
78 *
79 * tzid /
80 *
81 * ; 'last-mod' and 'tzurl' are optional,
82 * but MUST NOT occur more than once
83 *
84 * last-mod / tzurl /
85 *
86 * ; one of 'standardc' or 'daylightc' MUST occur
87 * ..; and each MAY occur more than once.
88 *
89 * standardc / daylightc /
90 *
91 * ; the following is optional,
92 * ; and MAY occur more than once
93 *
94 * x-prop
95 *
96 * )
97 *
98 * &quot;END&quot; &quot;:&quot; &quot;VTIMEZONE&quot; CRLF
99 *
100 * standardc = &quot;BEGIN&quot; &quot;:&quot; &quot;STANDARD&quot; CRLF
101 *
102 * tzprop
103 *
104 * &quot;END&quot; &quot;:&quot; &quot;STANDARD&quot; CRLF
105 *
106 * daylightc = &quot;BEGIN&quot; &quot;:&quot; &quot;DAYLIGHT&quot; CRLF
107 *
108 * tzprop
109 *
110 * &quot;END&quot; &quot;:&quot; &quot;DAYLIGHT&quot; CRLF
111 *
112 * tzprop = 3*(
113 *
114 * ; the following are each REQUIRED,
115 * ; but MUST NOT occur more than once
116 *
117 * dtstart / tzoffsetto / tzoffsetfrom /
118 *
119 * ; the following are optional,
120 * ; and MAY occur more than once
121 *
122 * comment / rdate / rrule / tzname / x-prop
123 *
124 * )
125 * </pre>
126 *
127 * @author Ben Fortuna
128 */
129 public class VTimeZone extends CalendarComponent {
130
131 private static final long serialVersionUID = 5629679741050917815L;
132
133 private final Validator itipValidator = new ITIPValidator();
134
135 private ComponentList observances;
136
137 /**
138 * Default constructor.
139 */
140 public VTimeZone() {
141 super(VTIMEZONE);
142 this.observances = new ComponentList();
143 }
144
145 /**
146 * Constructs a new instance containing the specified properties.
147 * @param properties a list of properties
148 */
149 public VTimeZone(final PropertyList properties) {
150 super(VTIMEZONE, properties);
151 this.observances = new ComponentList();
152 }
153
154 /**
155 * Constructs a new vtimezone component with no properties and the specified list of type components.
156 * @param observances a list of type components
157 */
158 public VTimeZone(final ComponentList observances) {
159 super(VTIMEZONE);
160 this.observances = observances;
161 }
162
163 /**
164 * Constructor.
165 * @param properties a list of properties
166 * @param observances a list of timezone types
167 */
168 public VTimeZone(final PropertyList properties,
169 final ComponentList observances) {
170 super(VTIMEZONE, properties);
171 this.observances = observances;
172 }
173
174 /**
175 * {@inheritDoc}
176 */
177 public final String toString() {
178 final StringBuffer b = new StringBuffer();
179 b.append(BEGIN);
180 b.append(':');
181 b.append(getName());
182 b.append(Strings.LINE_SEPARATOR);
183 b.append(getProperties());
184 b.append(observances);
185 b.append(END);
186 b.append(':');
187 b.append(getName());
188 b.append(Strings.LINE_SEPARATOR);
189 return b.toString();
190 }
191
192 /**
193 * {@inheritDoc}
194 */
195 public final void validate(final boolean recurse)
196 throws ValidationException {
197
198 /*
199 * ; 'tzid' is required, but MUST NOT occur more ; than once tzid /
200 */
201 PropertyValidator.getInstance().assertOne(Property.TZID,
202 getProperties());
203
204 /*
205 * ; 'last-mod' and 'tzurl' are optional, but MUST NOT occur more than once last-mod / tzurl /
206 */
207 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED,
208 getProperties());
209 PropertyValidator.getInstance().assertOneOrLess(Property.TZURL,
210 getProperties());
211
212 /*
213 * ; one of 'standardc' or 'daylightc' MUST occur ..; and each MAY occur more than once. standardc / daylightc /
214 */
215 if (getObservances().getComponent(Observance.STANDARD) == null
216 && getObservances().getComponent(Observance.DAYLIGHT) == null) {
217 throw new ValidationException("Sub-components ["
218 + Observance.STANDARD + "," + Observance.DAYLIGHT
219 + "] must be specified at least once");
220 }
221
222 for (final Iterator i = getObservances().iterator(); i.hasNext();) {
223 ((Component) i.next()).validate(recurse);
224 }
225
226 /*
227 * ; the following is optional, ; and MAY occur more than once x-prop
228 */
229
230 if (recurse) {
231 validateProperties();
232 }
233 }
234
235 /**
236 * {@inheritDoc}
237 */
238 protected Validator getValidator(Method method) {
239 return itipValidator;
240 }
241
242 /**
243 * Common validation for all iTIP methods.
244 *
245 * <pre>
246 * Component/Property Presence
247 * ------------------- ----------------------------------------------
248 * VTIMEZONE 0+ MUST be present if any date/time refers
249 * to timezone
250 * DAYLIGHT 0+ MUST be one or more of either STANDARD or
251 * DAYLIGHT
252 * COMMENT 0 or 1
253 * DTSTART 1 MUST be local time format
254 * RDATE 0+ if present RRULE MUST NOT be present
255 * RRULE 0+ if present RDATE MUST NOT be present
256 * TZNAME 0 or 1
257 * TZOFFSET 1
258 * TZOFFSETFROM 1
259 * TZOFFSETTO 1
260 * X-PROPERTY 0+
261 * LAST-MODIFIED 0 or 1
262 * STANDARD 0+ MUST be one or more of either STANDARD or
263 * DAYLIGHT
264 * COMMENT 0 or 1
265 * DTSTART 1 MUST be local time format
266 * RDATE 0+ if present RRULE MUST NOT be present
267 * RRULE 0+ if present RDATE MUST NOT be present
268 * TZNAME 0 or 1
269 * TZOFFSETFROM 1
270 * TZOFFSETTO 1
271 * X-PROPERTY 0+
272 * TZID 1
273 * TZURL 0 or 1
274 * X-PROPERTY 0+
275 * </pre>
276 */
277 private class ITIPValidator implements Validator {
278
279 private static final long serialVersionUID = 1L;
280
281 /**
282 * {@inheritDoc}
283 */
284 public void validate() throws ValidationException {
285 for (final Iterator i = getObservances().iterator(); i.hasNext();) {
286 final Observance observance = (Observance) i.next();
287 PropertyValidator.getInstance().assertOne(Property.DTSTART, observance.getProperties());
288 PropertyValidator.getInstance().assertOne(Property.TZOFFSETFROM, observance.getProperties());
289 PropertyValidator.getInstance().assertOne(Property.TZOFFSETTO, observance.getProperties());
290
291 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, observance.getProperties());
292 PropertyValidator.getInstance().assertOneOrLess(Property.TZNAME, observance.getProperties());
293 }
294 }
295 }
296
297 /**
298 * @return Returns the types.
299 */
300 public final ComponentList getObservances() {
301 return observances;
302 }
303
304 /**
305 * Returns the latest applicable timezone observance for the specified date.
306 * @param date the latest possible date for a timezone observance onset
307 * @return the latest applicable timezone observance for the specified date or null if there are no applicable
308 * observances
309 */
310 public final Observance getApplicableObservance(final Date date) {
311 Observance latestObservance = null;
312 Date latestOnset = null;
313 for (final Iterator i = getObservances().iterator(); i.hasNext();) {
314 final Observance observance = (Observance) i.next();
315 final Date onset = observance.getLatestOnset(date);
316 if (latestOnset == null
317 || (onset != null && onset.after(latestOnset))) {
318 latestOnset = onset;
319 latestObservance = observance;
320 }
321 }
322 return latestObservance;
323 }
324
325 /**
326 * @return the mandatory timezone identifier property
327 */
328 public final TzId getTimeZoneId() {
329 return (TzId) getProperty(Property.TZID);
330 }
331
332 /**
333 * @return the optional last-modified property
334 */
335 public final LastModified getLastModified() {
336 return (LastModified) getProperty(Property.LAST_MODIFIED);
337 }
338
339 /**
340 * @return the optional timezone url property
341 */
342 public final TzUrl getTimeZoneUrl() {
343 return (TzUrl) getProperty(Property.TZURL);
344 }
345
346 /**
347 * {@inheritDoc}
348 */
349 public boolean equals(final Object arg0) {
350 if (arg0 instanceof VTimeZone) {
351 return super.equals(arg0)
352 && ObjectUtils.equals(observances, ((VTimeZone) arg0)
353 .getObservances());
354 }
355 return super.equals(arg0);
356 }
357
358 /**
359 * {@inheritDoc}
360 */
361 public int hashCode() {
362 return new HashCodeBuilder().append(getName()).append(getProperties())
363 .append(getObservances()).toHashCode();
364 }
365
366 /**
367 * Overrides default copy method to add support for copying observance sub-components.
368 * @return a copy of the instance
369 * @throws ParseException where an error occurs parsing data
370 * @throws IOException where an error occurs reading data
371 * @throws URISyntaxException where an invalid URI is encountered
372 * @see net.fortuna.ical4j.model.Component#copy()
373 */
374 public Component copy() throws ParseException, IOException, URISyntaxException {
375 final VTimeZone copy = (VTimeZone) super.copy();
376 copy.observances = new ComponentList(observances);
377 return copy;
378 }
379 }

mercurial