19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.pim;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Config;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedHashMap;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedList;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Parses RFC 2445 iCalendar objects.
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ICalendar {
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "Sync";
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // TODO: keep track of VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // components, by type field or by subclass?  subclass would allow us to
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // enforce grammars.
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Exception thrown when an iCalendar object has invalid syntax.
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class FormatException extends Exception {
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public FormatException() {
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super();
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public FormatException(String msg) {
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(msg);
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public FormatException(String msg, Throwable cause) {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(msg, cause);
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A component within an iCalendar (VEVENT, VTODO, VJOURNAL, VFEEBUSY,
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * VTIMEZONE, VALARM).
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Component {
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // components
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static final String BEGIN = "BEGIN";
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static final String END = "END";
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static final String NEWLINE = "\n";
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VCALENDAR = "VCALENDAR";
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VEVENT = "VEVENT";
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VTODO = "VTODO";
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VJOURNAL = "VJOURNAL";
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VFREEBUSY = "VFREEBUSY";
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VTIMEZONE = "VTIMEZONE";
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String VALARM = "VALARM";
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String mName;
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Component mParent; // see if we can get rid of this
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private LinkedList<Component> mChildren = null;
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final LinkedHashMap<String, ArrayList<Property>> mPropsMap =
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new LinkedHashMap<String, ArrayList<Property>>();
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a new component with the provided name.
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the component.
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Component(String name, Component parent) {
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mName = name;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mParent = parent;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the name of the component.
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The name of the component.
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getName() {
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mName;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the parent of this component.
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The parent of this component.
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Component getParent() {
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mParent;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Helper that lazily gets/creates the list of children.
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The list of children.
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        protected LinkedList<Component> getOrCreateChildren() {
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mChildren == null) {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mChildren = new LinkedList<Component>();
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mChildren;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Adds a child component to this component.
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param child The child component.
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void addChild(Component child) {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getOrCreateChildren().add(child);
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns a list of the Component children of this component.  May be
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * null, if there are no children.
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return A list of the children.
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<Component> getComponents() {
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mChildren;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Adds a Property to this component.
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param prop
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void addProperty(Property prop) {
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name= prop.getName();
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ArrayList<Property> props = mPropsMap.get(name);
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (props == null) {
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                props = new ArrayList<Property>();
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPropsMap.put(name, props);
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            props.add(prop);
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns a set of the property names within this component.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return A set of property names within this component.
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Set<String> getPropertyNames() {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mPropsMap.keySet();
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns a list of properties with the specified name.  Returns null
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if there are no such properties.
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the property that should be returned.
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return A list of properties with the requested name.
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<Property> getProperties(String name) {
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mPropsMap.get(name);
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the first property with the specified name.  Returns null
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if there is no such property.
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the property that should be returned.
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The first property with the specified name.
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Property getFirstProperty(String name) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            List<Property> props = mPropsMap.get(name);
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (props == null || props.size() == 0) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return props.get(0);
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder sb = new StringBuilder();
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            toString(sb);
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(NEWLINE);
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sb.toString();
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Helper method that appends this component to a StringBuilder.  The
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * caller is responsible for appending a newline at the end of the
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * component.
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void toString(StringBuilder sb) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(BEGIN);
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(":");
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(mName);
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(NEWLINE);
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // append the properties
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (String propertyName : getPropertyNames()) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (Property property : getProperties(propertyName)) {
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    property.toString(sb);
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(NEWLINE);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // append the sub-components
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mChildren != null) {
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (Component component : mChildren) {
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    component.toString(sb);
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(NEWLINE);
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(END);
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(":");
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(mName);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A property within an iCalendar component (e.g., DTSTART, DTEND, etc.,
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * within a VEVENT).
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Property {
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // properties
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: do we want to list these here?  the complete list is long.
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String DTSTART = "DTSTART";
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String DTEND = "DTEND";
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String DURATION = "DURATION";
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String RRULE = "RRULE";
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String RDATE = "RDATE";
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String EXRULE = "EXRULE";
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final String EXDATE = "EXDATE";
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // ... need to add more.
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String mName;
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private LinkedHashMap<String, ArrayList<Parameter>> mParamsMap =
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new LinkedHashMap<String, ArrayList<Parameter>>();
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mValue; // TODO: make this final?
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a new property with the provided name.
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the property.
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Property(String name) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mName = name;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a new property with the provided name and value.
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the property.
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param value The value of the property.
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Property(String name, String value) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mName = name;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mValue = value;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the name of the property.
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The name of the property.
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getName() {
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mName;
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the value of this property.
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The value of this property.
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getValue() {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mValue;
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the value of this property.
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param value The desired value for this property.
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setValue(String value) {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mValue = value;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Adds a {@link Parameter} to this property.
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param param The parameter that should be added.
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void addParameter(Parameter param) {
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ArrayList<Parameter> params = mParamsMap.get(param.name);
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (params == null) {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                params = new ArrayList<Parameter>();
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mParamsMap.put(param.name, params);
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            params.add(param);
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the set of parameter names for this property.
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The set of parameter names for this property.
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Set<String> getParameterNames() {
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mParamsMap.keySet();
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the list of parameters with the specified name.  May return
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * null if there are no such parameters.
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the parameters that should be returned.
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The list of parameters with the specified name.
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<Parameter> getParameters(String name) {
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mParamsMap.get(name);
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the first parameter with the specified name.  May return
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * nll if there is no such parameter.
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the parameter that should be returned.
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return The first parameter with the specified name.
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Parameter getFirstParameter(String name) {
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ArrayList<Parameter> params = mParamsMap.get(name);
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (params == null || params.size() == 0) {
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return params.get(0);
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder sb = new StringBuilder();
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            toString(sb);
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sb.toString();
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Helper method that appends this property to a StringBuilder.  The
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * caller is responsible for appending a newline after this property.
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void toString(StringBuilder sb) {
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(mName);
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Set<String> parameterNames = getParameterNames();
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (String parameterName : parameterNames) {
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (Parameter param : getParameters(parameterName)) {
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(";");
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    param.toString(sb);
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(":");
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(mValue);
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A parameter defined for an iCalendar property.
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // TODO: make this a proper class rather than a struct?
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Parameter {
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String name;
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String value;
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a new empty parameter.
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Parameter() {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a new parameter with the specified name and value.
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param name The name of the parameter.
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param value The value of the parameter.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Parameter(String name, String value) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.name = name;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.value = value;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder sb = new StringBuilder();
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            toString(sb);
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sb.toString();
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Helper method that appends this parameter to a StringBuilder.
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void toString(StringBuilder sb) {
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(name);
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append("=");
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(value);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final class ParserState {
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // public int lineNumber = 0;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String line; // TODO: just point to original text
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int index;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // use factory method
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ICalendar() {
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // TODO: get rid of this -- handle all of the parsing in one pass through
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // the text.
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String normalizeText(String text) {
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // it's supposed to be \r\n, but not everyone does that
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text = text.replaceAll("\r\n", "\n");
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text = text.replaceAll("\r", "\n");
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // we deal with line folding, by replacing all "\n " strings
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // with nothing.  The RFC specifies "\r\n " to be folded, but
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // we handle "\n " and "\r " too because we can get those.
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text = text.replaceAll("\n ", "");
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return text;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses text into an iCalendar component.  Parses into the provided
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * component, if not null, or parses into a new component.  In the latter
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * case, expects a BEGIN as the first line.  Returns the provided or newly
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * created top-level component.
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // TODO: use an index into the text, so we can make this a recursive
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // function?
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Component parseComponentImpl(Component component,
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                String text)
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws FormatException {
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Component current = component;
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ParserState state = new ParserState();
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        state.index = 0;
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // split into lines
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String[] lines = text.split("\n");
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // each line is of the format:
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // name *(";" param) ":" value
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (String line : lines) {
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                current = parseLine(line, state, current);
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // if the provided component was null, we will return the root
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // NOTE: in this case, if the first line is not a BEGIN, a
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // FormatException will get thrown.
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (component == null) {
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    component = current;
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (FormatException fe) {
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Config.LOGV) {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.v(TAG, "Cannot parse " + line, fe);
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // for now, we ignore the parse error.  Google Calendar seems
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // to be emitting some misformatted iCalendar objects.
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            continue;
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return component;
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses a line into the provided component.  Creates a new component if
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the line is a BEGIN, adding the newly created component to the provided
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * parent.  Returns whatever component is the current one (to which new
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * properties will be added) in the parse.
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Component parseLine(String line, ParserState state,
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Component component)
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws FormatException {
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        state.line = line;
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = state.line.length();
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // grab the name
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = 0;
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (state.index = 0; state.index < len; ++state.index) {
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = line.charAt(state.index);
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ';' || c == ':') {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String name = line.substring(0, state.index);
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (component == null) {
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!Component.BEGIN.equals(name)) {
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new FormatException("Expected BEGIN");
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Property property;
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (Component.BEGIN.equals(name)) {
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // start a new component
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String componentName = extractValue(state);
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Component child = new Component(componentName, component);
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (component != null) {
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                component.addChild(child);
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return child;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (Component.END.equals(name)) {
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // finish the current component
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String componentName = extractValue(state);
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (component == null ||
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    !componentName.equals(component.getName())) {
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new FormatException("Unexpected END " + componentName);
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return component.getParent();
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            property = new Property(name);
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == ';') {
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Parameter parameter = null;
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while ((parameter = extractParameter(state)) != null) {
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                property.addParameter(parameter);
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String value = extractValue(state);
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        property.setValue(value);
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        component.addProperty(property);
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return component;
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Extracts the value ":..." on the current line.  The first character must
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be a ':'.
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String extractValue(ParserState state)
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws FormatException {
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String line = state.line;
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (state.index >= line.length() || line.charAt(state.index) != ':') {
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new FormatException("Expected ':' before end of line in "
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + line);
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String value = line.substring(state.index + 1);
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        state.index = line.length() - 1;
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return value;
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Extracts the next parameter from the line, if any.  If there are no more
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * parameters, returns null.
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Parameter extractParameter(ParserState state)
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws FormatException {
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String text = state.line;
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parameter parameter = null;
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int startIndex = -1;
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int equalIndex = -1;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (state.index < len) {
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(state.index);
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ':') {
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (parameter != null) {
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (equalIndex == -1) {
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        throw new FormatException("Expected '=' within "
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                + "parameter in " + text);
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parameter.value = text.substring(equalIndex + 1,
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     state.index);
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return parameter; // may be null
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == ';') {
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (parameter != null) {
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (equalIndex == -1) {
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        throw new FormatException("Expected '=' within "
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                + "parameter in " + text);
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parameter.value = text.substring(equalIndex + 1,
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     state.index);
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return parameter;
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parameter = new Parameter();
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    startIndex = state.index;
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '=') {
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                equalIndex = state.index;
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if ((parameter == null) || (startIndex == -1)) {
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new FormatException("Expected ';' before '=' in "
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            + text);
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parameter.name = text.substring(startIndex + 1, equalIndex);
58106912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert            } else if (c == '"') {
58206912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                if (parameter == null) {
58306912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                    throw new FormatException("Expected parameter before '\"' in " + text);
58406912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                }
58506912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                if (equalIndex == -1) {
58606912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                    throw new FormatException("Expected '=' within parameter in " + text);
58706912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                }
58806912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                if (state.index > equalIndex + 1) {
58906912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                    throw new FormatException("Parameter value cannot contain a '\"' in " + text);
59006912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                }
59106912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                final int endQuote = text.indexOf('"', state.index + 1);
59206912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                if (endQuote < 0) {
59306912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                    throw new FormatException("Expected closing '\"' in " + text);
59406912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                }
59506912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                parameter.value = text.substring(state.index + 1, endQuote);
59606912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                state.index = endQuote + 1;
59706912bddc6b9db4fd01ed689ca4a8e16b0790244Alon Albert                return parameter;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ++state.index;
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        throw new FormatException("Expected ':' before end of line in " + text);
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses the provided text into an iCalendar object.  The top-level
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * component must be of type VCALENDAR.
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text The text to be parsed.
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The top-level VCALENDAR component.
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws FormatException Thrown if the text could not be parsed into an
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * iCalendar VCALENDAR object.
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Component parseCalendar(String text) throws FormatException {
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Component calendar = parseComponent(null, text);
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (calendar == null || !Component.VCALENDAR.equals(calendar.getName())) {
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new FormatException("Expected " + Component.VCALENDAR);
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return calendar;
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses the provided text into an iCalendar event.  The top-level
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * component must be of type VEVENT.
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text The text to be parsed.
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The top-level VEVENT component.
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws FormatException Thrown if the text could not be parsed into an
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * iCalendar VEVENT.
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Component parseEvent(String text) throws FormatException {
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Component event = parseComponent(null, text);
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (event == null || !Component.VEVENT.equals(event.getName())) {
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new FormatException("Expected " + Component.VEVENT);
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return event;
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses the provided text into an iCalendar component.
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text The text to be parsed.
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The top-level component.
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws FormatException Thrown if the text could not be parsed into an
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * iCalendar component.
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Component parseComponent(String text) throws FormatException {
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return parseComponent(null, text);
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Parses the provided text, adding to the provided component.
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param component The component to which the parsed iCalendar data should
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be added.
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text The text to be parsed.
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The top-level component.
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws FormatException Thrown if the text could not be parsed as an
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * iCalendar object.
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Component parseComponent(Component component, String text)
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        throws FormatException {
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text = normalizeText(text);
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return parseComponentImpl(component, text);
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
662