diff -r 000000000000 -r fb9019fb1bf7 src/net/fortuna/ical4j/util/Calendars.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/fortuna/ical4j/util/Calendars.java Tue Feb 10 18:12:00 2015 +0100 @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2012, Ben Fortuna + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * o Neither the name of Ben Fortuna nor the names of any other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.fortuna.ical4j.util; + +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Calendar; +import net.fortuna.ical4j.model.Component; +import net.fortuna.ical4j.model.ComponentList; +import net.fortuna.ical4j.model.ConstraintViolationException; +import net.fortuna.ical4j.model.IndexedComponentList; +import net.fortuna.ical4j.model.Parameter; +import net.fortuna.ical4j.model.Property; +import net.fortuna.ical4j.model.component.VTimeZone; +import net.fortuna.ical4j.model.parameter.TzId; +import net.fortuna.ical4j.model.property.Method; +import net.fortuna.ical4j.model.property.Uid; + +/** + * $Id$ + * + * Created on 10/11/2006 + * + * Utility method for working with {@link Calendar}s. + * @author Ben Fortuna + */ +public final class Calendars { + + /** + * Constructor made private to enforce static nature. + */ + private Calendars() { + } + + /** + * Loads a calendar from the specified file. + * @param filename the name of the file from which to load calendar data + * @return returns a new calendar instance initialised from the specified file + * @throws IOException occurs when there is an error reading the specified file + * @throws ParserException occurs when the data in the specified file is invalid + */ + public static Calendar load(final String filename) throws IOException, ParserException { + final FileInputStream fin = new FileInputStream(filename); + final CalendarBuilder builder = new CalendarBuilder(); + return builder.build(fin); + } + + /** + * Loads a calendar from the specified URL. + * @param url the URL from which to load calendar data + * @return returns a new calendar instance initialised from the specified URL + * @throws IOException occurs when there is an error reading from the specified URL + * @throws ParserException occurs when the data in the specified URL is invalid + */ + public static Calendar load(final URL url) throws IOException, ParserException { + final CalendarBuilder builder = new CalendarBuilder(); + return builder.build(url.openStream()); + } + + /** + * Merge all properties and components from two specified calendars into one instance. + * Note that the merge process is not very sophisticated, and may result in invalid calendar + * data (e.g. multiple properties of a type that should only be specified once). + * @param c1 the first calendar to merge + * @param c2 the second calendar to merge + * @return a Calendar instance containing all properties and components from both of the specified calendars + */ + public static Calendar merge(final Calendar c1, final Calendar c2) { + final Calendar result = new Calendar(); + result.getProperties().addAll(c1.getProperties()); + for (final Iterator i = c2.getProperties().iterator(); i.hasNext();) { + final Property p = (Property) i.next(); + if (!result.getProperties().contains(p)) { + result.getProperties().add(p); + } + } + result.getComponents().addAll(c1.getComponents()); + for (final Iterator i = c2.getComponents().iterator(); i.hasNext();) { + final Component c = (Component) i.next(); + if (!result.getComponents().contains(c)) { + result.getComponents().add(c); + } + } + return result; + } + + /** + * Wraps a component in a calendar. + * @param component the component to wrap with a calendar + * @return a calendar containing the specified component + */ + public static Calendar wrap(final Component component) { + final ComponentList components = new ComponentList(); + components.add(component); + return new Calendar(components); + } + + /** + * Splits a calendar object into distinct calendar objects for unique + * identifers (UID). + * @param calendar a calendar instance + * @return an array of calendar objects + */ + public static Calendar[] split(final Calendar calendar) { + // if calendar contains one component or less, or is composed entirely of timezone + // definitions, return the original calendar unmodified.. + if (calendar.getComponents().size() <= 1 + || calendar.getComponents(Component.VTIMEZONE).size() == calendar.getComponents().size()) { + return new Calendar[] {calendar}; + } + + final IndexedComponentList timezones = new IndexedComponentList(calendar.getComponents(Component.VTIMEZONE), + Property.TZID); + + final Map calendars = new HashMap(); + for (final Iterator i = calendar.getComponents().iterator(); i.hasNext();) { + final Component c = (Component) i.next(); + if (c instanceof VTimeZone) { + continue; + } + + final Uid uid = (Uid) c.getProperty(Property.UID); + + Calendar uidCal = (Calendar) calendars.get(uid); + if (uidCal == null) { + uidCal = new Calendar(calendar.getProperties(), new ComponentList()); + // remove METHOD property for split calendars.. + for (final Iterator mp = uidCal.getProperties(Property.METHOD).iterator(); mp.hasNext();) { + uidCal.getProperties().remove(mp.next()); + } + calendars.put(uid, uidCal); + } + + for (final Iterator j = c.getProperties().iterator(); j.hasNext();) { + final Property p = (Property) j.next(); + final TzId tzid = (TzId) p.getParameter(Parameter.TZID); + if (tzid != null) { + final VTimeZone timezone = (VTimeZone) timezones.getComponent(tzid.getValue()); + if (!uidCal.getComponents().contains(timezone)) { + uidCal.getComponents().add(timezone); + } + } + } + uidCal.getComponents().add(c); + } + return (Calendar[]) calendars.values().toArray(new Calendar[calendars.values().size()]); + } + + /** + * Returns a unique identifier as specified by components in the provided calendar. + * @param calendar a calendar instance + * @return the UID property + * @throws ConstraintViolationException if zero or more than one unique identifer is found in the specified calendar + */ + public static Uid getUid(final Calendar calendar) throws ConstraintViolationException { + Uid uid = null; + for (final Iterator i = calendar.getComponents().iterator(); i.hasNext();) { + final Component c = (Component) i.next(); + for (final Iterator j = c.getProperties(Property.UID).iterator(); j.hasNext();) { + final Uid foundUid = (Uid) j.next(); + if (uid != null && !uid.equals(foundUid)) { + throw new ConstraintViolationException("More than one UID found in calendar"); + } + uid = foundUid; + } + } + if (uid == null) { + throw new ConstraintViolationException("Calendar must specify a single unique identifier (UID)"); + } + return uid; + } + + /** + * Returns an appropriate MIME Content-Type for the specified calendar object. + * @param calendar a calendar instance + * @param charset an optional encoding + * @return a content type string + */ + public static String getContentType(Calendar calendar, Charset charset) { + final StringBuffer b = new StringBuffer("text/calendar"); + + final Method method = (Method) calendar.getProperty(Property.METHOD); + if (method != null) { + b.append("; method="); + b.append(method.getValue()); + } + + if (charset != null) { + b.append("; charset="); + b.append(charset); + } + return b.toString(); + } +}