diff options
Diffstat (limited to 'core/java/android/pim/ICalendar.java')
-rw-r--r-- | core/java/android/pim/ICalendar.java | 660 |
1 files changed, 0 insertions, 660 deletions
diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java deleted file mode 100644 index 58c5c63..0000000 --- a/core/java/android/pim/ICalendar.java +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.pim; - -import android.util.Log; - -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.ArrayList; - -/** - * Parses RFC 2445 iCalendar objects. - */ -public class ICalendar { - - private static final String TAG = "Sync"; - - // TODO: keep track of VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM - // components, by type field or by subclass? subclass would allow us to - // enforce grammars. - - /** - * Exception thrown when an iCalendar object has invalid syntax. - */ - public static class FormatException extends Exception { - public FormatException() { - super(); - } - - public FormatException(String msg) { - super(msg); - } - - public FormatException(String msg, Throwable cause) { - super(msg, cause); - } - } - - /** - * A component within an iCalendar (VEVENT, VTODO, VJOURNAL, VFEEBUSY, - * VTIMEZONE, VALARM). - */ - public static class Component { - - // components - private static final String BEGIN = "BEGIN"; - private static final String END = "END"; - private static final String NEWLINE = "\n"; - public static final String VCALENDAR = "VCALENDAR"; - public static final String VEVENT = "VEVENT"; - public static final String VTODO = "VTODO"; - public static final String VJOURNAL = "VJOURNAL"; - public static final String VFREEBUSY = "VFREEBUSY"; - public static final String VTIMEZONE = "VTIMEZONE"; - public static final String VALARM = "VALARM"; - - private final String mName; - private final Component mParent; // see if we can get rid of this - private LinkedList<Component> mChildren = null; - private final LinkedHashMap<String, ArrayList<Property>> mPropsMap = - new LinkedHashMap<String, ArrayList<Property>>(); - - /** - * Creates a new component with the provided name. - * @param name The name of the component. - */ - public Component(String name, Component parent) { - mName = name; - mParent = parent; - } - - /** - * Returns the name of the component. - * @return The name of the component. - */ - public String getName() { - return mName; - } - - /** - * Returns the parent of this component. - * @return The parent of this component. - */ - public Component getParent() { - return mParent; - } - - /** - * Helper that lazily gets/creates the list of children. - * @return The list of children. - */ - protected LinkedList<Component> getOrCreateChildren() { - if (mChildren == null) { - mChildren = new LinkedList<Component>(); - } - return mChildren; - } - - /** - * Adds a child component to this component. - * @param child The child component. - */ - public void addChild(Component child) { - getOrCreateChildren().add(child); - } - - /** - * Returns a list of the Component children of this component. May be - * null, if there are no children. - * - * @return A list of the children. - */ - public List<Component> getComponents() { - return mChildren; - } - - /** - * Adds a Property to this component. - * @param prop - */ - public void addProperty(Property prop) { - String name= prop.getName(); - ArrayList<Property> props = mPropsMap.get(name); - if (props == null) { - props = new ArrayList<Property>(); - mPropsMap.put(name, props); - } - props.add(prop); - } - - /** - * Returns a set of the property names within this component. - * @return A set of property names within this component. - */ - public Set<String> getPropertyNames() { - return mPropsMap.keySet(); - } - - /** - * Returns a list of properties with the specified name. Returns null - * if there are no such properties. - * @param name The name of the property that should be returned. - * @return A list of properties with the requested name. - */ - public List<Property> getProperties(String name) { - return mPropsMap.get(name); - } - - /** - * Returns the first property with the specified name. Returns null - * if there is no such property. - * @param name The name of the property that should be returned. - * @return The first property with the specified name. - */ - public Property getFirstProperty(String name) { - List<Property> props = mPropsMap.get(name); - if (props == null || props.size() == 0) { - return null; - } - return props.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - sb.append(NEWLINE); - return sb.toString(); - } - - /** - * Helper method that appends this component to a StringBuilder. The - * caller is responsible for appending a newline at the end of the - * component. - */ - public void toString(StringBuilder sb) { - sb.append(BEGIN); - sb.append(":"); - sb.append(mName); - sb.append(NEWLINE); - - // append the properties - for (String propertyName : getPropertyNames()) { - for (Property property : getProperties(propertyName)) { - property.toString(sb); - sb.append(NEWLINE); - } - } - - // append the sub-components - if (mChildren != null) { - for (Component component : mChildren) { - component.toString(sb); - sb.append(NEWLINE); - } - } - - sb.append(END); - sb.append(":"); - sb.append(mName); - } - } - - /** - * A property within an iCalendar component (e.g., DTSTART, DTEND, etc., - * within a VEVENT). - */ - public static class Property { - // properties - // TODO: do we want to list these here? the complete list is long. - public static final String DTSTART = "DTSTART"; - public static final String DTEND = "DTEND"; - public static final String DURATION = "DURATION"; - public static final String RRULE = "RRULE"; - public static final String RDATE = "RDATE"; - public static final String EXRULE = "EXRULE"; - public static final String EXDATE = "EXDATE"; - // ... need to add more. - - private final String mName; - private LinkedHashMap<String, ArrayList<Parameter>> mParamsMap = - new LinkedHashMap<String, ArrayList<Parameter>>(); - private String mValue; // TODO: make this final? - - /** - * Creates a new property with the provided name. - * @param name The name of the property. - */ - public Property(String name) { - mName = name; - } - - /** - * Creates a new property with the provided name and value. - * @param name The name of the property. - * @param value The value of the property. - */ - public Property(String name, String value) { - mName = name; - mValue = value; - } - - /** - * Returns the name of the property. - * @return The name of the property. - */ - public String getName() { - return mName; - } - - /** - * Returns the value of this property. - * @return The value of this property. - */ - public String getValue() { - return mValue; - } - - /** - * Sets the value of this property. - * @param value The desired value for this property. - */ - public void setValue(String value) { - mValue = value; - } - - /** - * Adds a {@link Parameter} to this property. - * @param param The parameter that should be added. - */ - public void addParameter(Parameter param) { - ArrayList<Parameter> params = mParamsMap.get(param.name); - if (params == null) { - params = new ArrayList<Parameter>(); - mParamsMap.put(param.name, params); - } - params.add(param); - } - - /** - * Returns the set of parameter names for this property. - * @return The set of parameter names for this property. - */ - public Set<String> getParameterNames() { - return mParamsMap.keySet(); - } - - /** - * Returns the list of parameters with the specified name. May return - * null if there are no such parameters. - * @param name The name of the parameters that should be returned. - * @return The list of parameters with the specified name. - */ - public List<Parameter> getParameters(String name) { - return mParamsMap.get(name); - } - - /** - * Returns the first parameter with the specified name. May return - * nll if there is no such parameter. - * @param name The name of the parameter that should be returned. - * @return The first parameter with the specified name. - */ - public Parameter getFirstParameter(String name) { - ArrayList<Parameter> params = mParamsMap.get(name); - if (params == null || params.size() == 0) { - return null; - } - return params.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this property to a StringBuilder. The - * caller is responsible for appending a newline after this property. - */ - public void toString(StringBuilder sb) { - sb.append(mName); - Set<String> parameterNames = getParameterNames(); - for (String parameterName : parameterNames) { - for (Parameter param : getParameters(parameterName)) { - sb.append(";"); - param.toString(sb); - } - } - sb.append(":"); - sb.append(mValue); - } - } - - /** - * A parameter defined for an iCalendar property. - */ - // TODO: make this a proper class rather than a struct? - public static class Parameter { - public String name; - public String value; - - /** - * Creates a new empty parameter. - */ - public Parameter() { - } - - /** - * Creates a new parameter with the specified name and value. - * @param name The name of the parameter. - * @param value The value of the parameter. - */ - public Parameter(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this parameter to a StringBuilder. - */ - public void toString(StringBuilder sb) { - sb.append(name); - sb.append("="); - sb.append(value); - } - } - - private static final class ParserState { - // public int lineNumber = 0; - public String line; // TODO: just point to original text - public int index; - } - - // use factory method - private ICalendar() { - } - - // TODO: get rid of this -- handle all of the parsing in one pass through - // the text. - private static String normalizeText(String text) { - // it's supposed to be \r\n, but not everyone does that - text = text.replaceAll("\r\n", "\n"); - text = text.replaceAll("\r", "\n"); - - // we deal with line folding, by replacing all "\n " strings - // with nothing. The RFC specifies "\r\n " to be folded, but - // we handle "\n " and "\r " too because we can get those. - text = text.replaceAll("\n ", ""); - - return text; - } - - /** - * Parses text into an iCalendar component. Parses into the provided - * component, if not null, or parses into a new component. In the latter - * case, expects a BEGIN as the first line. Returns the provided or newly - * created top-level component. - */ - // TODO: use an index into the text, so we can make this a recursive - // function? - private static Component parseComponentImpl(Component component, - String text) - throws FormatException { - Component current = component; - ParserState state = new ParserState(); - state.index = 0; - - // split into lines - String[] lines = text.split("\n"); - - // each line is of the format: - // name *(";" param) ":" value - for (String line : lines) { - try { - current = parseLine(line, state, current); - // if the provided component was null, we will return the root - // NOTE: in this case, if the first line is not a BEGIN, a - // FormatException will get thrown. - if (component == null) { - component = current; - } - } catch (FormatException fe) { - if (false) { - Log.v(TAG, "Cannot parse " + line, fe); - } - // for now, we ignore the parse error. Google Calendar seems - // to be emitting some misformatted iCalendar objects. - } - continue; - } - return component; - } - - /** - * Parses a line into the provided component. Creates a new component if - * the line is a BEGIN, adding the newly created component to the provided - * parent. Returns whatever component is the current one (to which new - * properties will be added) in the parse. - */ - private static Component parseLine(String line, ParserState state, - Component component) - throws FormatException { - state.line = line; - int len = state.line.length(); - - // grab the name - char c = 0; - for (state.index = 0; state.index < len; ++state.index) { - c = line.charAt(state.index); - if (c == ';' || c == ':') { - break; - } - } - String name = line.substring(0, state.index); - - if (component == null) { - if (!Component.BEGIN.equals(name)) { - throw new FormatException("Expected BEGIN"); - } - } - - Property property; - if (Component.BEGIN.equals(name)) { - // start a new component - String componentName = extractValue(state); - Component child = new Component(componentName, component); - if (component != null) { - component.addChild(child); - } - return child; - } else if (Component.END.equals(name)) { - // finish the current component - String componentName = extractValue(state); - if (component == null || - !componentName.equals(component.getName())) { - throw new FormatException("Unexpected END " + componentName); - } - return component.getParent(); - } else { - property = new Property(name); - } - - if (c == ';') { - Parameter parameter = null; - while ((parameter = extractParameter(state)) != null) { - property.addParameter(parameter); - } - } - String value = extractValue(state); - property.setValue(value); - component.addProperty(property); - return component; - } - - /** - * Extracts the value ":..." on the current line. The first character must - * be a ':'. - */ - private static String extractValue(ParserState state) - throws FormatException { - String line = state.line; - if (state.index >= line.length() || line.charAt(state.index) != ':') { - throw new FormatException("Expected ':' before end of line in " - + line); - } - String value = line.substring(state.index + 1); - state.index = line.length() - 1; - return value; - } - - /** - * Extracts the next parameter from the line, if any. If there are no more - * parameters, returns null. - */ - private static Parameter extractParameter(ParserState state) - throws FormatException { - String text = state.line; - int len = text.length(); - Parameter parameter = null; - int startIndex = -1; - int equalIndex = -1; - while (state.index < len) { - char c = text.charAt(state.index); - if (c == ':') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - } - return parameter; // may be null - } else if (c == ';') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - return parameter; - } else { - parameter = new Parameter(); - startIndex = state.index; - } - } else if (c == '=') { - equalIndex = state.index; - if ((parameter == null) || (startIndex == -1)) { - throw new FormatException("Expected ';' before '=' in " - + text); - } - parameter.name = text.substring(startIndex + 1, equalIndex); - } else if (c == '"') { - if (parameter == null) { - throw new FormatException("Expected parameter before '\"' in " + text); - } - if (equalIndex == -1) { - throw new FormatException("Expected '=' within parameter in " + text); - } - if (state.index > equalIndex + 1) { - throw new FormatException("Parameter value cannot contain a '\"' in " + text); - } - final int endQuote = text.indexOf('"', state.index + 1); - if (endQuote < 0) { - throw new FormatException("Expected closing '\"' in " + text); - } - parameter.value = text.substring(state.index + 1, endQuote); - state.index = endQuote + 1; - return parameter; - } - ++state.index; - } - throw new FormatException("Expected ':' before end of line in " + text); - } - - /** - * Parses the provided text into an iCalendar object. The top-level - * component must be of type VCALENDAR. - * @param text The text to be parsed. - * @return The top-level VCALENDAR component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VCALENDAR object. - */ - public static Component parseCalendar(String text) throws FormatException { - Component calendar = parseComponent(null, text); - if (calendar == null || !Component.VCALENDAR.equals(calendar.getName())) { - throw new FormatException("Expected " + Component.VCALENDAR); - } - return calendar; - } - - /** - * Parses the provided text into an iCalendar event. The top-level - * component must be of type VEVENT. - * @param text The text to be parsed. - * @return The top-level VEVENT component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VEVENT. - */ - public static Component parseEvent(String text) throws FormatException { - Component event = parseComponent(null, text); - if (event == null || !Component.VEVENT.equals(event.getName())) { - throw new FormatException("Expected " + Component.VEVENT); - } - return event; - } - - /** - * Parses the provided text into an iCalendar component. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar component. - */ - public static Component parseComponent(String text) throws FormatException { - return parseComponent(null, text); - } - - /** - * Parses the provided text, adding to the provided component. - * @param component The component to which the parsed iCalendar data should - * be added. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed as an - * iCalendar object. - */ - public static Component parseComponent(Component component, String text) - throws FormatException { - text = normalizeText(text); - return parseComponentImpl(component, text); - } -} |