/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.j2me.pim.formats; import com.sun.j2me.pim.UnsupportedPIMFormatException; import java.io.UnsupportedEncodingException; import java.util.Vector; import com.sun.j2me.jsr75.StringUtil; /** * Supporting methods for interpreting vCard and vCalendar encodings. * */ public class FormatSupport { /** Code name of the Quoted-Printable binary encoding. */ public static final String QUOTED_PRINTABLE = "QUOTED_PRINTABLE"; /** Code name of the Base64 binary encoding. */ public static final String BASE64 = "BASE64"; /** Code name of the B binary encoding. */ public static final String BEE = "B"; /** Code name of plain text binary encoding. */ public static final String PLAIN_TEXT = "PLAIN_TEXT"; /** Name of default character encoding. */ public static final String UTF8 = "UTF-8"; /** Repeat rule daily frequency char representation. */ public static final char DAILY = 'D'; /** Repeat rule weekly frequency char representation. */ public static final char WEEKLY = 'W'; /** Repeat rule monthly frequency char representation. */ public static final char MONTHLY = 'M'; /** Repeat rule yearly frequency char representation. */ public static final char YEARLY = 'Y'; /** Repeat rule day-in-month char representation. */ public static final char DAY_IN_MONTH = 'D'; /** Repeat rule week-in-month char representation. */ public static final char WEEK_IN_MONTH = 'P'; /** Repeat rule day-in-year char representation. */ public static final char DAY_IN_YEAR = 'D'; /** Repeat rule month-in-year char representation. */ public static final char MONTH_IN_YEAR = 'M'; /** * Gets the character set specified by the given property attributes. * The default is UTF-8, unless the attributes contain a CHARSET= entry. * @param attributes an array of vCard or vCalendar property attributes * @return the encoding specified by the attributes */ public static String getCharSet(String[] attributes) { String charset = getAttributeValue(attributes, "CHARSET=", UTF8); try { "".getBytes(charset); return charset; } catch (UnsupportedEncodingException e) { // cannot use this encoding. return UTF8; } } /** * Gets an attribute of the form (key)(value), if one exists in the supplied * attributes list. * @param attributes an array of attributes * @param key the attribute key (e.g. "CHARSET=") * @param defaultValue a default value to be returned if no matching * attribute is found. * @return the value of the requested attribute, or defaultValue if the * attribute is not present. */ public static String getAttributeValue(String[] attributes, String key, String defaultValue) { for (int i = 0; i < attributes.length; i++) { if (attributes[i].startsWith(key)) { return attributes[i].substring(key.length()); } } return defaultValue; } /** * Gets the encoding used for a value with the given attributes. * * @param attributes an array of attributes * @return either VCardSupport.QUOTED_PRINTABLE, VCardSupport.BASE64 * or VCardSupport.PLAIN_TEXT */ public static String getEncoding(String[] attributes) { for (int i = 0; i < attributes.length; i++) { String s = attributes[i].toUpperCase(); if (s.equals("ENCODING=QUOTED-PRINTABLE") || s.equals("QUOTED-PRINTABLE")) { return QUOTED_PRINTABLE; } if (s.equals("ENCODING=BASE64") || s.equals("BASE64") || s.equals("ENCODING=B")) { return BASE64; } } return PLAIN_TEXT; } /** * Converts a string from the given UTF-8 plain text encoding to the * specified encoding. * @param data input data to be converted * @param encoding input data encoding * @param charset output encoding * @return encoded string */ public static String convertString(String data, String encoding, String charset) { if (encoding.equals(QUOTED_PRINTABLE)) { byte[] b = QuotedPrintableEncoding.fromQuotedPrintable(data); try { return new String(b, charset); } catch (UnsupportedEncodingException e) { // should not happen if charset was returned from getCharSet() return new String(b); } } else if (encoding.equals(BASE64)) { byte[] b = Base64Encoding.fromBase64(data); try { return new String(b, charset); } catch (UnsupportedEncodingException e) { // should not happen if charset was returned from getCharSet() return new String(b); } } else if (charset.equals(UTF8)) { return data; } else { try { return new String(data.getBytes(UTF8), charset); } catch (UnsupportedEncodingException e) { throw new Error(UTF8 + " encoding not available"); } } } /** * Sorts an array of integers. * @param a the list of integers */ public static void sort(int[] a) { // insertion sort for (int j = 1; j < a.length; j++) { int v = a[j]; int i = j - 1; while (i >= 0 && a[i] > v) { a[i + 1] = a[i]; i--; } a[i + 1] = v; } } /** * Checks to see if a sorted array of integers contains a given integer. * @param a input array to be checked * @param value to be checked int the array * @return <code>true</code> if the value is found int the array */ public static boolean contains(int[] a, int value) { // binary chop search int lowerBound = 0; int upperBound = a.length - 1; while (upperBound - lowerBound >= 0) { int i = lowerBound + (upperBound - lowerBound) / 2; int v = a[i]; if (v > value) { // look between lowerBound and i upperBound = i - 1; } else if (v < value) { // look between i and upperBound lowerBound = i + 1; } else { return true; } } return false; } /** * Handles data element parsing for V-object inputs. */ public static class DataElement { /** Name of the property. */ String propertyName; /** Attributes of the element. */ String[] attributes; /** Data to be processed. */ String data; } /** * Extracts data from a vCard or vCalendar line. * * @param line the input line, in the form * (propertyname)[;(attributes)]:(data) * @return the property data * @throws UnsupportedPIMFormatException if the line is not in the expected * format */ public static DataElement parseObjectLine(String line) throws UnsupportedPIMFormatException { // break the line into property name, attributes and data int i = line.indexOf(':'); if (i == -1 || i == 0) { // every line in a vCalendar object must have a colon delimiter throw new UnsupportedPIMFormatException( "Invalid line: '" + line + "'"); } DataElement element = new DataElement(); element.data = line.substring(i + 1).trim(); String prefix = line.substring(0, i).trim(); i = prefix.indexOf(';'); if (i == -1) { element.propertyName = prefix.toUpperCase(); element.attributes = new String[0]; } else { element.propertyName = prefix.substring(0, i).toUpperCase(); element.attributes = StringUtil.split(prefix, ';', i + 1); for (int j = 0; j < element.attributes.length; j++) { element.attributes[j] = element.attributes[j].toUpperCase(); } } // propertyName could contain a group name. (e.g. HOME.FN:) // we don't have to do anything with the group name - there is // really nothing to do with it - but we do have to process it. // remove a group name, if one exists: i = element.propertyName.lastIndexOf('.'); if (i != -1) { element.propertyName = element.propertyName.substring(i + 1); } return element; } /** * Interpret a vCard or vCalendar data element as a string, taking * into account any encoding parameters specified in the attribute array. * @param attributes An array of attributes obtained from a class to * parseObjectLine. * @param data The string data of a vCard or vCalendar object line, * obtained from a call to parseObjectLine. * @return the decoded string data */ public static String parseString(String[] attributes, String data) { String charset = getCharSet(attributes); String encoding = getEncoding(attributes); return convertString(data, encoding, charset); } /** * Interpret a vCard or vCalendar data element as a string array, taking * into account any encoding parameters specified in the attribute array. * @param attributes An array of attributes obtained from a call to * parseObjectLine. * @param data The string data of a vCard or vCalendar object line, * obtained from a call to parseObjectLine. * @return the decoded string array data */ public static String[] parseStringArray(String[] attributes, String data) { String charset = getCharSet(attributes); String encoding = getEncoding(attributes); String[] elements = StringUtil.split(data, ';', 0, false); for (int i = 0; i < elements.length; i++) { elements[i] = convertString(elements[i], encoding, charset); // treat empty elements as null if ("".equals(elements[i])) { elements[i] = null; } } return elements; } /** * Interpret a vCard or vCalendar data element as a byte array, taking * into account any encoding parameters specified in the attribute array. * @param attributes An array of attributes obtained from a class to * parseObjectLine. * @param data The string data of a vCard or vCalendar object line, * obtained from a call to parseObjectLine. * @return the decoded binary data */ public static byte[] parseBinary(String[] attributes, String data) { String encoding = getEncoding(attributes); if (encoding.equals(QUOTED_PRINTABLE)) { return QuotedPrintableEncoding.fromQuotedPrintable(data); } else if (encoding.equals(BASE64)) { return Base64Encoding.fromBase64(data); } else { return data.getBytes(); } } /** * Check if a given list type is supported in the system * * @param pimListType the list type. * Can be one of the following: * <ul> * <li> PIM.CONTACT_LIST * <li> PIM.EVENT_LIST * <li> PIM.TODO_LIST * </ul> * @return <code>true</code> is the given list type is supported, * <code>false</code> otherwise */ public static native boolean isListTypeSupported(int pimListType); }