Tue, 10 Feb 2015 18:12:00 +0100
Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.
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;
34 import java.io.IOException;
35 import java.io.Serializable;
36 import java.net.URISyntaxException;
37 import java.text.ParseException;
38 import java.util.Iterator;
40 import net.fortuna.ical4j.model.component.CalendarComponent;
41 import net.fortuna.ical4j.model.property.CalScale;
42 import net.fortuna.ical4j.model.property.Method;
43 import net.fortuna.ical4j.model.property.ProdId;
44 import net.fortuna.ical4j.model.property.Version;
45 import net.fortuna.ical4j.model.property.XProperty;
46 import net.fortuna.ical4j.util.CompatibilityHints;
47 import net.fortuna.ical4j.util.ComponentValidator;
48 import net.fortuna.ical4j.util.PropertyValidator;
49 import net.fortuna.ical4j.util.Strings;
51 import org.apache.commons.lang.builder.EqualsBuilder;
52 import org.apache.commons.lang.builder.HashCodeBuilder;
54 /**
55 * $Id$ [Apr 5, 2004]
56 *
57 * Defines an iCalendar calendar.
58 *
59 * <pre>
60 * 4.6 Calendar Components
61 *
62 * The body of the iCalendar object consists of a sequence of calendar
63 * properties and one or more calendar components. The calendar
64 * properties are attributes that apply to the calendar as a whole. The
65 * calendar components are collections of properties that express a
66 * particular calendar semantic. For example, the calendar component can
67 * specify an event, a to-do, a journal entry, time zone information, or
68 * free/busy time information, or an alarm.
69 *
70 * The body of the iCalendar object is defined by the following
71 * notation:
72 *
73 * icalbody = calprops component
74 *
75 * calprops = 2*(
76 *
77 * ; 'prodid' and 'version' are both REQUIRED,
78 * ; but MUST NOT occur more than once
79 *
80 * prodid /version /
81 *
82 * ; 'calscale' and 'method' are optional,
83 * ; but MUST NOT occur more than once
84 *
85 * calscale /
86 * method /
87 *
88 * x-prop
89 *
90 * )
91 *
92 * component = 1*(eventc / todoc / journalc / freebusyc /
93 * / timezonec / iana-comp / x-comp)
94 *
95 * iana-comp = "BEGIN" ":" iana-token CRLF
96 *
97 * 1*contentline
98 *
99 * "END" ":" iana-token CRLF
100 *
101 * x-comp = "BEGIN" ":" x-name CRLF
102 *
103 * 1*contentline
104 *
105 * "END" ":" x-name CRLF
106 * </pre>
107 *
108 * Example 1 - Creating a new calendar:
109 *
110 * <pre><code>
111 * Calendar calendar = new Calendar();
112 * calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));
113 * calendar.getProperties().add(Version.VERSION_2_0);
114 * calendar.getProperties().add(CalScale.GREGORIAN);
115 *
116 * // Add events, etc..
117 * </code></pre>
118 *
119 * @author Ben Fortuna
120 */
121 public class Calendar implements Serializable {
123 private static final long serialVersionUID = -1654118204678581940L;
125 /**
126 * Begin token.
127 */
128 public static final String BEGIN = "BEGIN";
130 /**
131 * Calendar token.
132 */
133 public static final String VCALENDAR = "VCALENDAR";
135 /**
136 * End token.
137 */
138 public static final String END = "END";
140 private PropertyList properties;
142 private ComponentList components;
144 /**
145 * Default constructor.
146 */
147 public Calendar() {
148 this(new PropertyList(), new ComponentList());
149 }
151 /**
152 * Constructs a new calendar with no properties and the specified components.
153 * @param components a list of components to add to the calendar
154 */
155 public Calendar(final ComponentList components) {
156 this(new PropertyList(), components);
157 }
159 /**
160 * Constructor.
161 * @param p a list of properties
162 * @param c a list of components
163 */
164 public Calendar(final PropertyList p, final ComponentList c) {
165 this.properties = p;
166 this.components = c;
167 }
169 /**
170 * Creates a deep copy of the specified calendar.
171 * @param c the calendar to copy
172 * @throws IOException where an error occurs reading calendar data
173 * @throws ParseException where calendar parsing fails
174 * @throws URISyntaxException where an invalid URI string is encountered
175 */
176 public Calendar(Calendar c) throws ParseException, IOException,
177 URISyntaxException {
179 this(new PropertyList(c.getProperties()), new ComponentList(c
180 .getComponents()));
181 }
183 /**
184 * {@inheritDoc}
185 */
186 public final String toString() {
187 final StringBuffer buffer = new StringBuffer();
188 buffer.append(BEGIN);
189 buffer.append(':');
190 buffer.append(VCALENDAR);
191 buffer.append(Strings.LINE_SEPARATOR);
192 buffer.append(getProperties());
193 buffer.append(getComponents());
194 buffer.append(END);
195 buffer.append(':');
196 buffer.append(VCALENDAR);
197 buffer.append(Strings.LINE_SEPARATOR);
199 return buffer.toString();
200 }
202 /**
203 * @return Returns the components.
204 */
205 public final ComponentList getComponents() {
206 return components;
207 }
209 /**
210 * Convenience method for retrieving a list of named components.
211 * @param name name of components to retrieve
212 * @return a component list containing only components with the specified name
213 */
214 public final ComponentList getComponents(final String name) {
215 return getComponents().getComponents(name);
216 }
218 /**
219 * Convenience method for retrieving a named component.
220 * @param name name of the component to retrieve
221 * @return the first matching component in the component list with the specified name
222 */
223 public final Component getComponent(final String name) {
224 return getComponents().getComponent(name);
225 }
227 /**
228 * @return Returns the properties.
229 */
230 public final PropertyList getProperties() {
231 return properties;
232 }
234 /**
235 * Convenience method for retrieving a list of named properties.
236 * @param name name of properties to retrieve
237 * @return a property list containing only properties with the specified name
238 */
239 public final PropertyList getProperties(final String name) {
240 return getProperties().getProperties(name);
241 }
243 /**
244 * Convenience method for retrieving a named property.
245 * @param name name of the property to retrieve
246 * @return the first matching property in the property list with the specified name
247 */
248 public final Property getProperty(final String name) {
249 return getProperties().getProperty(name);
250 }
252 /**
253 * Perform validation on the calendar, its properties and its components in its current state.
254 * @throws ValidationException where the calendar is not in a valid state
255 */
256 public final void validate() throws ValidationException {
257 validate(true);
258 }
260 /**
261 * Perform validation on the calendar in its current state.
262 * @param recurse indicates whether to validate the calendar's properties and components
263 * @throws ValidationException where the calendar is not in a valid state
264 */
265 public void validate(final boolean recurse) throws ValidationException {
266 // 'prodid' and 'version' are both REQUIRED,
267 // but MUST NOT occur more than once
268 PropertyValidator.getInstance().assertOne(Property.PRODID, properties);
269 PropertyValidator.getInstance().assertOne(Property.VERSION, properties);
271 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
272 // require VERSION:2.0 for RFC2445..
273 if (!Version.VERSION_2_0.equals(getProperty(Property.VERSION))) {
274 throw new ValidationException("Unsupported Version: " + getProperty(Property.VERSION).getValue());
275 }
276 }
278 // 'calscale' and 'method' are optional,
279 // but MUST NOT occur more than once
280 PropertyValidator.getInstance().assertOneOrLess(Property.CALSCALE,
281 properties);
282 PropertyValidator.getInstance().assertOneOrLess(Property.METHOD,
283 properties);
285 // must contain at least one component
286 if (getComponents().isEmpty()) {
287 throw new ValidationException(
288 "Calendar must contain at least one component");
289 }
291 // validate properties..
292 for (final Iterator i = getProperties().iterator(); i.hasNext();) {
293 final Property property = (Property) i.next();
295 if (!(property instanceof XProperty)
296 && !property.isCalendarProperty()) {
297 throw new ValidationException("Invalid property: "
298 + property.getName());
299 }
300 }
302 // validate components..
303 for (final Iterator i = getComponents().iterator(); i.hasNext();) {
304 final Component component = (Component) i.next();
305 if (!(component instanceof CalendarComponent)) {
306 throw new ValidationException("Not a valid calendar component: " + component.getName());
307 }
308 }
310 // if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
311 // validate method..
312 final Method method = (Method) getProperty(Property.METHOD);
313 if (Method.PUBLISH.equals(method)) {
314 if (getComponent(Component.VEVENT) != null) {
315 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
316 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
318 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
319 ComponentValidator.assertNone(Component.VTODO, getComponents());
320 }
321 }
322 else if (getComponent(Component.VFREEBUSY) != null) {
323 ComponentValidator.assertNone(Component.VTODO, getComponents());
324 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
325 ComponentValidator.assertNone(Component.VTIMEZONE, getComponents());
326 ComponentValidator.assertNone(Component.VALARM, getComponents());
327 }
328 else if (getComponent(Component.VTODO) != null) {
329 // ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
330 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
331 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
332 }
333 else if (getComponent(Component.VJOURNAL) != null) {
334 // ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
335 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
336 // ComponentValidator.assertNone(Component.VTODO, getComponents());
337 }
338 }
339 else if (Method.REQUEST.equals(getProperty(Property.METHOD))) {
340 if (getComponent(Component.VEVENT) != null) {
341 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
342 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
343 ComponentValidator.assertNone(Component.VTODO, getComponents());
344 }
345 else if (getComponent(Component.VFREEBUSY) != null) {
346 ComponentValidator.assertNone(Component.VTODO, getComponents());
347 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
348 ComponentValidator.assertNone(Component.VTIMEZONE, getComponents());
349 ComponentValidator.assertNone(Component.VALARM, getComponents());
350 }
351 else if (getComponent(Component.VTODO) != null) {
352 // ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
353 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
354 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
355 }
356 }
357 else if (Method.REPLY.equals(getProperty(Property.METHOD))) {
358 if (getComponent(Component.VEVENT) != null) {
359 ComponentValidator.assertOneOrLess(Component.VTIMEZONE, getComponents());
361 ComponentValidator.assertNone(Component.VALARM, getComponents());
362 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
363 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
364 ComponentValidator.assertNone(Component.VTODO, getComponents());
365 }
366 else if (getComponent(Component.VFREEBUSY) != null) {
367 ComponentValidator.assertNone(Component.VTODO, getComponents());
368 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
369 ComponentValidator.assertNone(Component.VTIMEZONE, getComponents());
370 ComponentValidator.assertNone(Component.VALARM, getComponents());
371 }
372 else if (getComponent(Component.VTODO) != null) {
373 ComponentValidator.assertOneOrLess(Component.VTIMEZONE, getComponents());
375 ComponentValidator.assertNone(Component.VALARM, getComponents());
376 // ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
377 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
378 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
379 }
380 }
381 else if (Method.ADD.equals(getProperty(Property.METHOD))) {
382 if (getComponent(Component.VEVENT) != null) {
383 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
384 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
385 ComponentValidator.assertNone(Component.VTODO, getComponents());
386 }
387 else if (getComponent(Component.VTODO) != null) {
388 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
389 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
390 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
391 }
392 else if (getComponent(Component.VJOURNAL) != null) {
393 ComponentValidator.assertOneOrLess(Component.VTIMEZONE, getComponents());
395 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
396 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
397 // ComponentValidator.assertNone(Component.VTODO, getComponents());
398 }
399 }
400 else if (Method.CANCEL.equals(getProperty(Property.METHOD))) {
401 if (getComponent(Component.VEVENT) != null) {
402 ComponentValidator.assertNone(Component.VALARM, getComponents());
403 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
404 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
405 ComponentValidator.assertNone(Component.VTODO, getComponents());
406 }
407 else if (getComponent(Component.VTODO) != null) {
408 ComponentValidator.assertOneOrLess(Component.VTIMEZONE, getComponents());
410 ComponentValidator.assertNone(Component.VALARM, getComponents());
411 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
412 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
413 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
414 }
415 else if (getComponent(Component.VJOURNAL) != null) {
416 ComponentValidator.assertNone(Component.VALARM, getComponents());
417 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
418 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
419 // ComponentValidator.assertNone(Component.VTODO, getComponents());
420 }
421 }
422 else if (Method.REFRESH.equals(getProperty(Property.METHOD))) {
423 if (getComponent(Component.VEVENT) != null) {
424 ComponentValidator.assertNone(Component.VALARM, getComponents());
425 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
426 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
427 ComponentValidator.assertNone(Component.VTODO, getComponents());
428 }
429 else if (getComponent(Component.VTODO) != null) {
430 ComponentValidator.assertNone(Component.VALARM, getComponents());
431 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
432 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
433 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
434 ComponentValidator.assertNone(Component.VTIMEZONE, getComponents());
435 }
436 }
437 else if (Method.COUNTER.equals(getProperty(Property.METHOD))) {
438 if (getComponent(Component.VEVENT) != null) {
439 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
440 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
441 ComponentValidator.assertNone(Component.VTODO, getComponents());
442 }
443 else if (getComponent(Component.VTODO) != null) {
444 ComponentValidator.assertOneOrLess(Component.VTIMEZONE, getComponents());
446 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
447 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
448 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
449 }
450 }
451 else if (Method.DECLINE_COUNTER.equals(getProperty(Property.METHOD))) {
452 if (getComponent(Component.VEVENT) != null) {
453 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
454 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
455 ComponentValidator.assertNone(Component.VTODO, getComponents());
456 ComponentValidator.assertNone(Component.VTIMEZONE, getComponents());
457 ComponentValidator.assertNone(Component.VALARM, getComponents());
458 }
459 else if (getComponent(Component.VTODO) != null) {
460 ComponentValidator.assertNone(Component.VALARM, getComponents());
461 ComponentValidator.assertNone(Component.VFREEBUSY, getComponents());
462 // ComponentValidator.assertNone(Component.VEVENT, getComponents());
463 ComponentValidator.assertNone(Component.VJOURNAL, getComponents());
464 }
465 }
466 // }
468 // perform ITIP validation on components..
469 if (method != null) {
470 for (final Iterator i = getComponents().iterator(); i.hasNext();) {
471 final CalendarComponent component = (CalendarComponent) i.next();
472 component.validate(method);
473 }
474 }
476 if (recurse) {
477 validateProperties();
478 validateComponents();
479 }
480 }
482 /**
483 * Invoke validation on the calendar properties in its current state.
484 * @throws ValidationException where any of the calendar properties is not in a valid state
485 */
486 private void validateProperties() throws ValidationException {
487 for (final Iterator i = getProperties().iterator(); i.hasNext();) {
488 final Property property = (Property) i.next();
489 property.validate();
490 }
491 }
493 /**
494 * Invoke validation on the calendar components in its current state.
495 * @throws ValidationException where any of the calendar components is not in a valid state
496 */
497 private void validateComponents() throws ValidationException {
498 for (final Iterator i = getComponents().iterator(); i.hasNext();) {
499 final Component component = (Component) i.next();
500 component.validate();
501 }
502 }
504 /**
505 * Returns the mandatory prodid property.
506 * @return the PRODID property, or null if property doesn't exist
507 */
508 public final ProdId getProductId() {
509 return (ProdId) getProperty(Property.PRODID);
510 }
512 /**
513 * Returns the mandatory version property.
514 * @return the VERSION property, or null if property doesn't exist
515 */
516 public final Version getVersion() {
517 return (Version) getProperty(Property.VERSION);
518 }
520 /**
521 * Returns the optional calscale property.
522 * @return the CALSCALE property, or null if property doesn't exist
523 */
524 public final CalScale getCalendarScale() {
525 return (CalScale) getProperty(Property.CALSCALE);
526 }
528 /**
529 * Returns the optional method property.
530 * @return the METHOD property, or null if property doesn't exist
531 */
532 public final Method getMethod() {
533 return (Method) getProperty(Property.METHOD);
534 }
536 /**
537 * {@inheritDoc}
538 */
539 public final boolean equals(final Object arg0) {
540 if (arg0 instanceof Calendar) {
541 final Calendar calendar = (Calendar) arg0;
542 return new EqualsBuilder().append(getProperties(), calendar.getProperties())
543 .append(getComponents(), calendar.getComponents()).isEquals();
544 }
545 return super.equals(arg0);
546 }
548 /**
549 * {@inheritDoc}
550 */
551 public final int hashCode() {
552 return new HashCodeBuilder().append(getProperties()).append(
553 getComponents()).toHashCode();
554 }
555 }