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.Serializable;
35 import java.text.ParseException;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.Set;
40 import java.util.StringTokenizer;
41 import java.util.TreeSet;
43 import org.apache.commons.lang.builder.EqualsBuilder;
44 import org.apache.commons.lang.builder.HashCodeBuilder;
46 /**
47 * $Id$ [23-Apr-2004]
48 *
49 * Defines a list of iCalendar periods. NOTE: By implementing the
50 * <code>java.util.SortedSet</code> interface period lists will always be
51 * sorted according to natural ordering.
52 *
53 * @author Ben Fortuna
54 */
55 public class PeriodList implements Set, Serializable {
57 private static final long serialVersionUID = -2317587285790834492L;
59 private final Set periods;
61 private TimeZone timezone;
63 private boolean utc;
65 private final boolean unmodifiable;
67 /**
68 * Default constructor.
69 */
70 public PeriodList() {
71 this(true);
72 }
74 /**
75 * @param utc indicates whether the period list is in UTC time
76 */
77 public PeriodList(boolean utc) {
78 this(utc, false);
79 }
81 /**
82 * @param utc indicates whether the period list is in UTC time
83 */
84 public PeriodList(boolean utc, final boolean unmodifiable) {
85 this.utc = utc;
86 this.unmodifiable = unmodifiable;
87 if (unmodifiable) {
88 periods = Collections.EMPTY_SET;
89 }
90 else {
91 periods = new TreeSet();
92 }
93 }
95 /**
96 * Parses the specified string representation to create a list of periods.
97 *
98 * @param aValue
99 * a string representation of a list of periods
100 * @throws ParseException
101 * thrown when an invalid string representation of a period list
102 * is specified
103 */
104 public PeriodList(final String aValue) throws ParseException {
105 this();
106 final StringTokenizer t = new StringTokenizer(aValue, ",");
107 while (t.hasMoreTokens()) {
108 add((Object) new Period(t.nextToken()));
109 }
110 }
112 /**
113 * {@inheritDoc}
114 */
115 public final String toString() {
116 final StringBuffer b = new StringBuffer();
117 for (final Iterator i = iterator(); i.hasNext();) {
118 b.append(i.next().toString());
119 if (i.hasNext()) {
120 b.append(',');
121 }
122 }
123 return b.toString();
124 }
126 /**
127 * Add a period to the list.
128 *
129 * @param period
130 * the period to add
131 * @return true
132 * @see java.util.List#add(java.lang.Object)
133 */
134 public final boolean add(final Period period) {
135 if (isUtc()) {
136 period.setUtc(true);
137 }
138 else {
139 period.setTimeZone(timezone);
140 }
141 return add((Object) period);
142 }
144 /**
145 * Overrides superclass to throw an <code>IllegalArgumentException</code>
146 * where argument is not a <code>net.fortuna.ical4j.model.Period</code>.
147 * @param period a period to add to the list
148 * @return true if the period was added, otherwise false
149 * @see java.util.List#add(E)
150 */
151 public final boolean add(final Object period) {
152 if (!(period instanceof Period)) {
153 throw new IllegalArgumentException("Argument not a "
154 + Period.class.getName());
155 }
156 return periods.add(period);
157 }
159 /**
160 * Remove a period from the list.
161 *
162 * @param period
163 * the period to remove
164 * @return true if the list contained the specified period
165 * @see java.util.List#remove(java.lang.Object)
166 */
167 public final boolean remove(final Period period) {
168 return remove((Object) period);
169 }
171 /**
172 * Returns a normalised version of this period list. Normalisation includes
173 * combining overlapping periods, removing periods contained by other
174 * periods, combining adjacent periods, and removing periods that consume
175 * no time. NOTE: If the period list is
176 * already normalised then this period list is returned.
177 *
178 * @return a period list
179 */
180 public final PeriodList normalise() {
181 Period prevPeriod = null;
182 Period period = null;
183 final PeriodList newList = new PeriodList(isUtc());
184 if (timezone != null) {
185 newList.setTimeZone(timezone);
186 }
187 boolean normalised = false;
188 for (final Iterator i = iterator(); i.hasNext();) {
189 period = (Period) i.next();
190 if (period.isEmpty()) {
191 period = prevPeriod;
192 normalised = true;
193 }
194 else if (prevPeriod != null) {
195 // ignore periods contained by other periods..
196 if (prevPeriod.contains(period)) {
197 period = prevPeriod;
198 normalised = true;
199 }
200 // combine intersecting periods..
201 else if (prevPeriod.intersects(period)) {
202 period = prevPeriod.add(period);
203 normalised = true;
204 }
205 // combine adjacent periods..
206 else if (prevPeriod.adjacent(period)) {
207 period = prevPeriod.add(period);
208 normalised = true;
209 }
210 else {
211 // if current period is recognised as distinct
212 // from previous period, add the previous period
213 // to the list..
214 newList.add(prevPeriod);
215 }
216 }
217 prevPeriod = period;
218 }
219 // remember to add the last period to the list..
220 if (prevPeriod != null) {
221 newList.add(prevPeriod);
222 }
223 // only return new list if normalisation
224 // has ocurred..
225 if (normalised) {
226 return newList;
227 }
228 else {
229 return this;
230 }
231 }
233 /**
234 * A convenience method that combines all the periods in the specified list to
235 * this list. The result returned is a new PeriodList instance, except where
236 * no periods are specified in the arguments. In such cases this instance is returned.
237 *
238 * Normalisation is also performed automatically after all periods have been added.
239 *
240 * @param periods a list of periods to add
241 * @return a period list instance
242 */
243 public final PeriodList add(final PeriodList periods) {
244 if (periods != null) {
245 final PeriodList newList = new PeriodList();
246 newList.addAll(this);
247 for (final Iterator i = periods.iterator(); i.hasNext();) {
248 newList.add((Period) i.next());
249 }
250 return newList.normalise();
251 }
252 return this;
253 }
255 /**
256 * Subtracts the intersection of this list with the specified list of
257 * periods from this list and returns the results as a new period list. If
258 * no intersection is identified this list is returned.
259 *
260 * @param subtractions
261 * a list of periods to subtract from this list
262 * @return a period list
263 */
264 public final PeriodList subtract(final PeriodList subtractions) {
265 if (subtractions == null || subtractions.isEmpty()) {
266 return this;
267 }
269 PeriodList result = this;
270 PeriodList tmpResult = new PeriodList();
272 for (final Iterator i = subtractions.iterator(); i.hasNext();) {
273 final Period subtraction = (Period) i.next();
274 for (final Iterator j = result.iterator(); j.hasNext();) {
275 final Period period = (Period) j.next();
276 tmpResult.addAll(period.subtract(subtraction));
277 }
278 result = tmpResult;
279 tmpResult = new PeriodList();
280 }
282 return result;
283 }
285 public final boolean isUnmodifiable() {
286 return unmodifiable;
287 }
289 /**
290 * Indicates whether this list is in local or UTC format.
291 * @return Returns true if in UTC format, otherwise false.
292 */
293 public final boolean isUtc() {
294 return utc;
295 }
297 /**
298 * Sets whether this list is in UTC or local time format.
299 * @param utc The utc to set.
300 */
301 public final void setUtc(final boolean utc) {
302 for (final Iterator i = iterator(); i.hasNext();) {
303 final Period period = (Period) i.next();
304 period.setUtc(utc);
305 }
306 this.timezone = null;
307 this.utc = utc;
308 }
310 /**
311 * Applies the specified timezone to all dates in the list.
312 * All dates added to this list will also have this timezone
313 * applied.
314 * @param timeZone the timezone for the period list
315 */
316 public final void setTimeZone(final TimeZone timeZone) {
317 for (final Iterator i = iterator(); i.hasNext();) {
318 final Period period = (Period) i.next();
319 period.setTimeZone(timeZone);
320 }
321 this.timezone = timeZone;
322 this.utc = false;
323 }
325 /**
326 * @return Returns the timeZone.
327 */
328 public final TimeZone getTimeZone() {
329 return timezone;
330 }
332 /**
333 * {@inheritDoc}
334 */
335 public final boolean addAll(Collection arg0) {
336 for (Iterator i = arg0.iterator(); i.hasNext();) {
337 add(i.next());
338 }
339 return true;
340 }
342 /**
343 * {@inheritDoc}
344 */
345 public final void clear() {
346 periods.clear();
347 }
349 /**
350 * {@inheritDoc}
351 */
352 public final boolean contains(Object o) {
353 return periods.contains(o);
354 }
356 /**
357 * {@inheritDoc}
358 */
359 public final boolean containsAll(Collection arg0) {
360 return periods.containsAll(arg0);
361 }
363 /**
364 * {@inheritDoc}
365 */
366 public final boolean isEmpty() {
367 return periods.isEmpty();
368 }
370 /**
371 * {@inheritDoc}
372 */
373 public final Iterator iterator() {
374 return periods.iterator();
375 }
377 /**
378 * {@inheritDoc}
379 */
380 public final boolean remove(Object o) {
381 return periods.remove(o);
382 }
384 /**
385 * {@inheritDoc}
386 */
387 public final boolean removeAll(Collection arg0) {
388 return periods.removeAll(arg0);
389 }
391 /**
392 * {@inheritDoc}
393 */
394 public final boolean retainAll(Collection arg0) {
395 return periods.retainAll(arg0);
396 }
398 /**
399 * {@inheritDoc}
400 */
401 public final int size() {
402 return periods.size();
403 }
405 /**
406 * {@inheritDoc}
407 */
408 public final Object[] toArray() {
409 return periods.toArray();
410 }
412 /**
413 * {@inheritDoc}
414 */
415 public final Object[] toArray(Object[] arg0) {
416 return periods.toArray(arg0);
417 }
419 public final boolean equals(Object obj) {
420 if (!getClass().isAssignableFrom(obj.getClass())) {
421 return false;
422 }
423 final PeriodList rhs = (PeriodList) obj;
424 return new EqualsBuilder().append(periods, rhs.periods)
425 .append(timezone, rhs.timezone)
426 .append(utc, utc)
427 .isEquals();
428 }
430 public final int hashCode() {
431 return new HashCodeBuilder().append(periods)
432 .append(timezone)
433 .append(utc)
434 .toHashCode();
435 }
436 }