/* * 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.syncml.pim.vcalendar; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import android.syncml.pim.VBuilder; public class VCalParser_V20 extends VCalParser_V10 { private static final String V10LINEBREAKER = "\r\n"; private static final HashSet<String> acceptableComponents = new HashSet<String>( Arrays.asList("VEVENT", "VTODO", "VALARM", "VTIMEZONE")); private static final HashSet<String> acceptableV20Props = new HashSet<String>( Arrays.asList("DESCRIPTION", "DTEND", "DTSTART", "DUE", "COMPLETED", "RRULE", "STATUS", "SUMMARY", "LOCATION")); private boolean hasTZ = false; // MUST only have one TZ property private String[] lines; private int index; @Override public boolean parse(InputStream is, String encoding, VBuilder builder) throws IOException { // get useful info for android calendar, and alter to vcal 1.0 byte[] bytes = new byte[is.available()]; is.read(bytes); String scStr = new String(bytes); StringBuilder v10str = new StringBuilder(""); lines = splitProperty(scStr); index = 0; if ("BEGIN:VCALENDAR".equals(lines[index])) v10str.append("BEGIN:VCALENDAR" + V10LINEBREAKER); else return false; index++; if (false == parseV20Calbody(lines, v10str) || index > lines.length - 1) return false; if (lines.length - 1 == index && "END:VCALENDAR".equals(lines[index])) v10str.append("END:VCALENDAR" + V10LINEBREAKER); else return false; return super.parse( // use vCal 1.0 parser new ByteArrayInputStream(v10str.toString().getBytes()), encoding, builder); } /** * Parse and pick acceptable iCalendar body and translate it to * calendarV1.0 format. * @param lines iCalendar components/properties line list. * @param buffer calendarV10 format string buffer * @return true for success, or false */ private boolean parseV20Calbody(String[] lines, StringBuilder buffer) { try { while (!"VERSION:2.0".equals(lines[index])) index++; buffer.append("VERSION:1.0" + V10LINEBREAKER); index++; for (; index < lines.length - 1; index++) { String[] keyAndValue = lines[index].split(":", 2); String key = keyAndValue[0]; String value = keyAndValue[1]; if ("BEGIN".equals(key.trim())) { if (!key.equals(key.trim())) return false; // MUST be "BEGIN:componentname" index++; if (false == parseV20Component(value, buffer)) return false; } } } catch (ArrayIndexOutOfBoundsException e) { return false; } return true; } /** * Parse and pick acceptable calendar V2.0's component and translate it to * V1.0 format. * @param compName component name * @param buffer calendarV10 format string buffer * @return true for success, or false * @throws ArrayIndexOutOfBoundsException */ private boolean parseV20Component(String compName, StringBuilder buffer) throws ArrayIndexOutOfBoundsException { String endTag = "END:" + compName; String[] propAndValue; String propName, value; if (acceptableComponents.contains(compName)) { if ("VEVENT".equals(compName) || "VTODO".equals(compName)) { buffer.append("BEGIN:" + compName + V10LINEBREAKER); while (!endTag.equals(lines[index])) { propAndValue = lines[index].split(":", 2); propName = propAndValue[0].split(";", 2)[0]; value = propAndValue[1]; if ("".equals(lines[index])) buffer.append(V10LINEBREAKER); else if (acceptableV20Props.contains(propName)) { buffer.append(propName + ":" + value + V10LINEBREAKER); } else if ("BEGIN".equals(propName.trim())) { // MUST be BEGIN:VALARM if (propName.equals(propName.trim()) && "VALARM".equals(value)) { buffer.append("AALARM:default" + V10LINEBREAKER); while (!"END:VALARM".equals(lines[index])) index++; } else return false; } index++; } // end while buffer.append(endTag + V10LINEBREAKER); } else if ("VALARM".equals(compName)) { // VALARM component MUST // only appear within either VEVENT or VTODO return false; } else if ("VTIMEZONE".equals(compName)) { do { if (false == hasTZ) {// MUST only have 1 time TZ property propAndValue = lines[index].split(":", 2); propName = propAndValue[0].split(";", 2)[0]; if ("TZOFFSETFROM".equals(propName)) { value = propAndValue[1]; buffer.append("TZ" + ":" + value + V10LINEBREAKER); hasTZ = true; } } index++; } while (!endTag.equals(lines[index])); } else return false; } else { while (!endTag.equals(lines[index])) index++; } return true; } /** split ever property line to String[], not split folding line. */ private String[] splitProperty(String scStr) { /* * Property splitted by \n, and unfold folding lines by removing * CRLF+LWSP-char */ scStr = scStr.replaceAll("\r\n", "\n").replaceAll("\n ", "") .replaceAll("\n\t", ""); String[] strs = scStr.split("\n"); return strs; } }