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.util;
34 import java.io.FileInputStream;
35 import java.io.IOException;
36 import java.net.URL;
37 import java.nio.charset.Charset;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.Map;
42 import net.fortuna.ical4j.data.CalendarBuilder;
43 import net.fortuna.ical4j.data.ParserException;
44 import net.fortuna.ical4j.model.Calendar;
45 import net.fortuna.ical4j.model.Component;
46 import net.fortuna.ical4j.model.ComponentList;
47 import net.fortuna.ical4j.model.ConstraintViolationException;
48 import net.fortuna.ical4j.model.IndexedComponentList;
49 import net.fortuna.ical4j.model.Parameter;
50 import net.fortuna.ical4j.model.Property;
51 import net.fortuna.ical4j.model.component.VTimeZone;
52 import net.fortuna.ical4j.model.parameter.TzId;
53 import net.fortuna.ical4j.model.property.Method;
54 import net.fortuna.ical4j.model.property.Uid;
56 /**
57 * $Id$
58 *
59 * Created on 10/11/2006
60 *
61 * Utility method for working with {@link Calendar}s.
62 * @author Ben Fortuna
63 */
64 public final class Calendars {
66 /**
67 * Constructor made private to enforce static nature.
68 */
69 private Calendars() {
70 }
72 /**
73 * Loads a calendar from the specified file.
74 * @param filename the name of the file from which to load calendar data
75 * @return returns a new calendar instance initialised from the specified file
76 * @throws IOException occurs when there is an error reading the specified file
77 * @throws ParserException occurs when the data in the specified file is invalid
78 */
79 public static Calendar load(final String filename) throws IOException, ParserException {
80 final FileInputStream fin = new FileInputStream(filename);
81 final CalendarBuilder builder = new CalendarBuilder();
82 return builder.build(fin);
83 }
85 /**
86 * Loads a calendar from the specified URL.
87 * @param url the URL from which to load calendar data
88 * @return returns a new calendar instance initialised from the specified URL
89 * @throws IOException occurs when there is an error reading from the specified URL
90 * @throws ParserException occurs when the data in the specified URL is invalid
91 */
92 public static Calendar load(final URL url) throws IOException, ParserException {
93 final CalendarBuilder builder = new CalendarBuilder();
94 return builder.build(url.openStream());
95 }
97 /**
98 * Merge all properties and components from two specified calendars into one instance.
99 * Note that the merge process is not very sophisticated, and may result in invalid calendar
100 * data (e.g. multiple properties of a type that should only be specified once).
101 * @param c1 the first calendar to merge
102 * @param c2 the second calendar to merge
103 * @return a Calendar instance containing all properties and components from both of the specified calendars
104 */
105 public static Calendar merge(final Calendar c1, final Calendar c2) {
106 final Calendar result = new Calendar();
107 result.getProperties().addAll(c1.getProperties());
108 for (final Iterator i = c2.getProperties().iterator(); i.hasNext();) {
109 final Property p = (Property) i.next();
110 if (!result.getProperties().contains(p)) {
111 result.getProperties().add(p);
112 }
113 }
114 result.getComponents().addAll(c1.getComponents());
115 for (final Iterator i = c2.getComponents().iterator(); i.hasNext();) {
116 final Component c = (Component) i.next();
117 if (!result.getComponents().contains(c)) {
118 result.getComponents().add(c);
119 }
120 }
121 return result;
122 }
124 /**
125 * Wraps a component in a calendar.
126 * @param component the component to wrap with a calendar
127 * @return a calendar containing the specified component
128 */
129 public static Calendar wrap(final Component component) {
130 final ComponentList components = new ComponentList();
131 components.add(component);
132 return new Calendar(components);
133 }
135 /**
136 * Splits a calendar object into distinct calendar objects for unique
137 * identifers (UID).
138 * @param calendar a calendar instance
139 * @return an array of calendar objects
140 */
141 public static Calendar[] split(final Calendar calendar) {
142 // if calendar contains one component or less, or is composed entirely of timezone
143 // definitions, return the original calendar unmodified..
144 if (calendar.getComponents().size() <= 1
145 || calendar.getComponents(Component.VTIMEZONE).size() == calendar.getComponents().size()) {
146 return new Calendar[] {calendar};
147 }
149 final IndexedComponentList timezones = new IndexedComponentList(calendar.getComponents(Component.VTIMEZONE),
150 Property.TZID);
152 final Map calendars = new HashMap();
153 for (final Iterator i = calendar.getComponents().iterator(); i.hasNext();) {
154 final Component c = (Component) i.next();
155 if (c instanceof VTimeZone) {
156 continue;
157 }
159 final Uid uid = (Uid) c.getProperty(Property.UID);
161 Calendar uidCal = (Calendar) calendars.get(uid);
162 if (uidCal == null) {
163 uidCal = new Calendar(calendar.getProperties(), new ComponentList());
164 // remove METHOD property for split calendars..
165 for (final Iterator mp = uidCal.getProperties(Property.METHOD).iterator(); mp.hasNext();) {
166 uidCal.getProperties().remove(mp.next());
167 }
168 calendars.put(uid, uidCal);
169 }
171 for (final Iterator j = c.getProperties().iterator(); j.hasNext();) {
172 final Property p = (Property) j.next();
173 final TzId tzid = (TzId) p.getParameter(Parameter.TZID);
174 if (tzid != null) {
175 final VTimeZone timezone = (VTimeZone) timezones.getComponent(tzid.getValue());
176 if (!uidCal.getComponents().contains(timezone)) {
177 uidCal.getComponents().add(timezone);
178 }
179 }
180 }
181 uidCal.getComponents().add(c);
182 }
183 return (Calendar[]) calendars.values().toArray(new Calendar[calendars.values().size()]);
184 }
186 /**
187 * Returns a unique identifier as specified by components in the provided calendar.
188 * @param calendar a calendar instance
189 * @return the UID property
190 * @throws ConstraintViolationException if zero or more than one unique identifer is found in the specified calendar
191 */
192 public static Uid getUid(final Calendar calendar) throws ConstraintViolationException {
193 Uid uid = null;
194 for (final Iterator i = calendar.getComponents().iterator(); i.hasNext();) {
195 final Component c = (Component) i.next();
196 for (final Iterator j = c.getProperties(Property.UID).iterator(); j.hasNext();) {
197 final Uid foundUid = (Uid) j.next();
198 if (uid != null && !uid.equals(foundUid)) {
199 throw new ConstraintViolationException("More than one UID found in calendar");
200 }
201 uid = foundUid;
202 }
203 }
204 if (uid == null) {
205 throw new ConstraintViolationException("Calendar must specify a single unique identifier (UID)");
206 }
207 return uid;
208 }
210 /**
211 * Returns an appropriate MIME Content-Type for the specified calendar object.
212 * @param calendar a calendar instance
213 * @param charset an optional encoding
214 * @return a content type string
215 */
216 public static String getContentType(Calendar calendar, Charset charset) {
217 final StringBuffer b = new StringBuffer("text/calendar");
219 final Method method = (Method) calendar.getProperty(Property.METHOD);
220 if (method != null) {
221 b.append("; method=");
222 b.append(method.getValue());
223 }
225 if (charset != null) {
226 b.append("; charset=");
227 b.append(charset);
228 }
229 return b.toString();
230 }
231 }