Tue, 10 Feb 2015 19:58:00 +0100
Upgrade the upgraded ical4j component to use org.apache.commons.lang3.
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;
34 import java.io.IOException;
35 import java.net.URISyntaxException;
36 import java.text.ParseException;
37 import java.util.Iterator;
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;
53 import org.apache.commons.lang3.ObjectUtils;
54 import org.apache.commons.lang3.builder.HashCodeBuilder;
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 "VTIMEZONE" calendar component is defined by the
70 * following notation:
71 *
72 * timezonec = "BEGIN" ":" "VTIMEZONE" 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 * "END" ":" "VTIMEZONE" CRLF
99 *
100 * standardc = "BEGIN" ":" "STANDARD" CRLF
101 *
102 * tzprop
103 *
104 * "END" ":" "STANDARD" CRLF
105 *
106 * daylightc = "BEGIN" ":" "DAYLIGHT" CRLF
107 *
108 * tzprop
109 *
110 * "END" ":" "DAYLIGHT" 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 {
131 private static final long serialVersionUID = 5629679741050917815L;
133 private final Validator itipValidator = new ITIPValidator();
135 private ComponentList observances;
137 /**
138 * Default constructor.
139 */
140 public VTimeZone() {
141 super(VTIMEZONE);
142 this.observances = new ComponentList();
143 }
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 }
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 }
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 }
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 }
192 /**
193 * {@inheritDoc}
194 */
195 public final void validate(final boolean recurse)
196 throws ValidationException {
198 /*
199 * ; 'tzid' is required, but MUST NOT occur more ; than once tzid /
200 */
201 PropertyValidator.getInstance().assertOne(Property.TZID,
202 getProperties());
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());
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 }
222 for (final Iterator i = getObservances().iterator(); i.hasNext();) {
223 ((Component) i.next()).validate(recurse);
224 }
226 /*
227 * ; the following is optional, ; and MAY occur more than once x-prop
228 */
230 if (recurse) {
231 validateProperties();
232 }
233 }
235 /**
236 * {@inheritDoc}
237 */
238 protected Validator getValidator(Method method) {
239 return itipValidator;
240 }
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 {
279 private static final long serialVersionUID = 1L;
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());
291 PropertyValidator.getInstance().assertOneOrLess(Property.TZNAME, observance.getProperties());
292 }
293 }
294 }
296 /**
297 * @return Returns the types.
298 */
299 public final ComponentList getObservances() {
300 return observances;
301 }
303 /**
304 * Returns the latest applicable timezone observance for the specified date.
305 * @param date the latest possible date for a timezone observance onset
306 * @return the latest applicable timezone observance for the specified date or null if there are no applicable
307 * observances
308 */
309 public final Observance getApplicableObservance(final Date date) {
310 Observance latestObservance = null;
311 Date latestOnset = null;
312 for (final Iterator i = getObservances().iterator(); i.hasNext();) {
313 final Observance observance = (Observance) i.next();
314 final Date onset = observance.getLatestOnset(date);
315 if (latestOnset == null
316 || (onset != null && onset.after(latestOnset))) {
317 latestOnset = onset;
318 latestObservance = observance;
319 }
320 }
321 return latestObservance;
322 }
324 /**
325 * @return the mandatory timezone identifier property
326 */
327 public final TzId getTimeZoneId() {
328 return (TzId) getProperty(Property.TZID);
329 }
331 /**
332 * @return the optional last-modified property
333 */
334 public final LastModified getLastModified() {
335 return (LastModified) getProperty(Property.LAST_MODIFIED);
336 }
338 /**
339 * @return the optional timezone url property
340 */
341 public final TzUrl getTimeZoneUrl() {
342 return (TzUrl) getProperty(Property.TZURL);
343 }
345 /**
346 * {@inheritDoc}
347 */
348 public boolean equals(final Object arg0) {
349 if (arg0 instanceof VTimeZone) {
350 return super.equals(arg0)
351 && ObjectUtils.equals(observances, ((VTimeZone) arg0)
352 .getObservances());
353 }
354 return super.equals(arg0);
355 }
357 /**
358 * {@inheritDoc}
359 */
360 public int hashCode() {
361 return new HashCodeBuilder().append(getName()).append(getProperties())
362 .append(getObservances()).toHashCode();
363 }
365 /**
366 * Overrides default copy method to add support for copying observance sub-components.
367 * @return a copy of the instance
368 * @throws ParseException where an error occurs parsing data
369 * @throws IOException where an error occurs reading data
370 * @throws URISyntaxException where an invalid URI is encountered
371 * @see net.fortuna.ical4j.model.Component#copy()
372 */
373 public Component copy() throws ParseException, IOException, URISyntaxException {
374 final VTimeZone copy = (VTimeZone) super.copy();
375 copy.observances = new ComponentList(observances);
376 return copy;
377 }
378 }