FileDocCategorySizeDatePackage
VCardFormat.javaAPI DocphoneME MR2 API (J2ME)16386Wed May 02 18:00:28 BST 2007com.sun.kvem.midp.pim.formats

VCardFormat.java

/*
 *   
 *
 * Copyright  1990-2007 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.kvem.midp.pim.formats;

import com.sun.kvem.midp.pim.ContactImpl;
import com.sun.kvem.midp.pim.LineReader;
import com.sun.kvem.midp.pim.PIMFormat;
import com.sun.kvem.midp.pim.PIMHandler;
import com.sun.kvem.midp.pim.UnsupportedPIMFormatException;
import com.sun.kvem.midp.pim.AbstractPIMList;
import com.sun.kvem.midp.pim.AbstractPIMItem;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import javax.microedition.pim.Contact;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMException;
import javax.microedition.pim.PIMItem;
import javax.microedition.pim.PIMList;

/**
 * Partial implementation of PIMEncoding for VCard/2.1 and VCard/3.0.
 *
 */
public abstract class VCardFormat extends EndMatcher implements PIMFormat {

    /**
     * Returns the version number of vCard implemented.
     * @return the VCard version number
     */
    protected abstract String getVersion();

    /**
     * Gets the vCard property name used to store categories.
     * @return the vCard category property name
     */
    protected abstract String getCategoryProperty();

    /**
     * Gets the vCard property name used to store classes.
     * @return the class property name
     */
    protected abstract String getClassProperty();

    /**
     * Gets the binary value describing all flags in a vCard line.
     * @param attributes fields to be parsed
     * @return binary coded settings
     */
    protected abstract int parseAttributes(String[] attributes);

    /**
     * Gets the name of the default binary encoding.
     * This is "BASE64" for vCard 2.1 and "B" for vCard 3.0.
     * @return the default binary encoding
     */
    protected abstract String getBinaryEncodingName();

    /**
     * VCard formatting class.
     */
    public VCardFormat() {
        super("VCARD");
    }

    /**
     * Gets the code name of this encoding (e.g. "VCARD/2.1").
     * @return the encoding name
     */
    public String getName() {
        return "VCARD/" + getVersion();
    }

    /**
     * Checks to see if a given PIM list type is supported by this encoding.
     * @param pimListType int representing the PIM list type to check
     * @return true if the type can be read and written by this encoding,
     * false otherwise
     */
    public boolean isTypeSupported(int pimListType) {
        return pimListType == PIM.CONTACT_LIST;
    }

    /**
     * Serializes a PIMItem.
     * @param out Stream to which serialized data is written
     * @param encoding Character encoding to use for serialized data
     * @param pimItem The item to write to the stream
     * @throws IOException if an error occurs while writing
     */
    public void encode(OutputStream out, String encoding, PIMItem pimItem)
        throws IOException {
        Writer w = new OutputStreamWriter(out, encoding);
        w.write("BEGIN:VCARD\r\n");
        w.write("VERSION:");
        w.write(getVersion());
        w.write("\r\n");
        // write all fields
        int[] fields = pimItem.getFields();
        FormatSupport.sort(fields);
        for (int i = 0; i < fields.length; i++) {
            int valueCount = pimItem.countValues(fields[i]);
            for (int j = 0; j < valueCount; j++) {
                writeValue(w, pimItem, fields[i], j);
            }
        }
        // write categories.
        String categories = FormatSupport.join(pimItem.getCategories(), ",");
        if (categories.length() > 0) {
            w.write(getCategoryProperty());
            w.write(":");
            w.write(categories);
            w.write("\r\n");
        }
        w.write("END:VCARD\r\n");
        w.flush();
    }

    /**
     * Writes a single vCard line.
     * @param w output stream target
     * @param item the data to to written
     * @param field the attribute to be processed
     * @param index the offset of the data to be processed
     * @throws IOException if an error occurs while writing
     */
    protected void writeValue(Writer w, PIMItem item, int field, int index)
        throws IOException {

        String label = VCardSupport.getFieldLabel(field);
        switch (field) {
            case Contact.FORMATTED_NAME:
            case Contact.FORMATTED_ADDR:
            case Contact.PHOTO_URL:
            case Contact.TEL:
            case Contact.EMAIL:
            case Contact.TITLE:
            case Contact.ORG:
            case Contact.NICKNAME:
            case Contact.NOTE:
            case Contact.UID:
            case Contact.URL:
            case Contact.PUBLIC_KEY_STRING: {
                String sValue = item.getString(field, index);
                if (sValue != null) {
                    w.write(label);
                    writeAttributes(w, item.getAttributes(field, index));
                    w.write(":");
                    w.write(sValue);
                    w.write("\r\n");
                }
                break;
            }
            case Contact.NAME:
            case Contact.ADDR: {
                String[] aValue = item.getStringArray(field, index);
                if (aValue != null) {
                    w.write(label);
                    writeAttributes(w, item.getAttributes(field, index));
                    w.write(":");
                    writeStringArray(w, aValue);
                    w.write("\r\n");
                }
                break;
            }
            case Contact.PHOTO:
            case Contact.PUBLIC_KEY: {
                byte[] bValue = item.getBinary(field, index);
                if (bValue != null) {
                    w.write(label);
                    w.write(";ENCODING=");
                    w.write(getBinaryEncodingName());
                    writeAttributes(w, item.getAttributes(field, index));
                    w.write(":\r\n    ");
                    w.write(Base64Encoding.toBase64(
                        bValue,
                        76 /* line width */,
                        4 /* indent */));
                    w.write("\r\n");
                }
                break;
            }
            case Contact.BIRTHDAY:
            case Contact.REVISION:
                w.write(label);
                writeAttributes(w, item.getAttributes(field, index));
                w.write(":");
                writeDate(w, item.getDate(field, index));
                w.write("\r\n");
                break;
            case Contact.CLASS: {
                int iValue = item.getInt(field, index);
                String sValue = VCardSupport.getClassType(iValue);
                if (sValue != null) {
                    w.write(getClassProperty());
                    writeAttributes(w, item.getAttributes(field, index));
                    w.write(":");
                    w.write(sValue);
                    w.write("\r\n");
                }
                break;
            }
            default:
                // field cannot be written. ignore it.
        }
    }

    /**
     * Writes a vCard field with multiple elements, such as ADR.
     * @param w output stream target
     * @param data the strings to write
     * @throws IOException if an error occurs while writing
     */
    protected void writeStringArray(Writer w, String[] data)
        throws IOException {
        for (int i = 0; i < data.length; i++) {
            if (data[i] != null) {
                w.write(data[i]);
            }
            if (i != data.length -1) {
                w.write(';');
            }
        }
    }

    /**
     * Writes a vCard date field.
     * @param w output stream target
     * @param date data to be written
     * @throws IOException if an error occurs while writing
     */
    protected void writeDate(Writer w, long date) throws IOException {
        w.write(PIMHandler.getInstance().composeDate(date));
    }

    /**
     * Writes the attributes for a field.
     * @param w output stream target
     * @param attributes data to be written
     * @throws IOException if an error occurs while writing
     */
    protected abstract void writeAttributes(Writer w, int attributes)
        throws IOException;

    /**
     * Constructs one or more PIMItems from serialized data.
     * @param in Stream containing serialized data
     * @param encoding Character encoding of the stream
     * @param list PIMList to which items should be added, or null if the items
     * should not be part of a list
     * @throws UnsupportedPIMFormatException if the serialized data cannot be
     * interpreted by this encoding.
     * @return a non-empty array of PIMItems containing the objects described in
     * the serialized data, or null if no items are available
     * @throws IOException if an error occurs while reading
     */
    public PIMItem[] decode(InputStream in, String encoding, PIMList list)
        throws IOException {

        LineReader r = new LineReader(in, encoding, this);
        ContactImpl contact = decode(r, list);
        if (contact == null) {
            return null;
        } else {
            return new ContactImpl[] { contact };
        }
    }

    /**
     * Constructs a single PIMItem from serialized data.
     * @param in LineReader containing serialized data
     * @param list PIM list to which the item belongs
     * @throws UnsupportedPIMFormatException if the serialized data cannot be
     * interpreted by this encoding.
     * @return an unserialized Contact, or null if no data was available
     */
    private ContactImpl decode(LineReader in, PIMList list)
    throws IOException {
        String line = in.readLine();
        if (line == null) {
            return null;
        }
        if (!line.toUpperCase().equals("BEGIN:VCARD")) {
            throw new UnsupportedPIMFormatException("Not a vCard :'"
                + line + "'");
        }
        String categoryProperty = getCategoryProperty();
        ContactImpl contact = new ContactImpl((AbstractPIMList)list);
        while ((line = in.readLine()) != null) {
            FormatSupport.DataElement element =
                FormatSupport.parseObjectLine(line);
            if (element.propertyName.equals("END")) {
                return contact;
            } else if (element.propertyName.equals("VERSION")) {
                if (!element.data.equals(getVersion())) {
                    throw new UnsupportedPIMFormatException("Version "
                        + element.data + " not supported");
                }
            } else if (element.propertyName.equals(categoryProperty)) {
                String[] categories = FormatSupport.split(element.data, ',', 0);
                for (int j = 0; j < categories.length; j++) {
                    try {
                        contact.addToCategory(categories[j]);
                    } catch (PIMException e) {
                        // cannot add to category
                    }
                }
            } else {
                importData(contact, element.propertyName, element.attributes,
                    element.data, contact.getPIMListHandle());
            }
        }
        throw new IOException("Unterminated vCard");
    }

    /**
     * Parses a single vCard line.
     * @param contact element to populate
     * @param prefix filter for selecting fields
     * @param attributes fields to be processed
     * @param data input to be filtered
     * @param listHandle handle of the list containing the contact being parsed
     */
    private void importData(Contact contact,
    String prefix, String[] attributes, String data, Object listHandle) {

        int attr = parseAttributes(attributes);
        int field = VCardSupport.getFieldCode(prefix);
        if (field == -1 && prefix.equals(getClassProperty())) {
            field = Contact.CLASS;
        }
        if (!PIMHandler.getInstance()
	    .isSupportedField(listHandle, field)) {
            return;
        }
        switch (field) {
            case Contact.FORMATTED_NAME:
            case Contact.FORMATTED_ADDR:
            case Contact.TEL:
            case Contact.EMAIL:
            case Contact.TITLE:
            case Contact.ORG:
            case Contact.NICKNAME:
            case Contact.NOTE:
            case Contact.UID:
            case Contact.URL: {
                String sdata = FormatSupport.parseString(attributes, data);
                contact.addString(field, attr, sdata);
                break;
            }
            case Contact.NAME:
            case Contact.ADDR: {
                String[] elements =
                FormatSupport.parseStringArray(attributes, data);
                int elementCount = PIMHandler.getInstance()
                    .getStringArraySize(listHandle, field);
                if (elements.length != elementCount) {
                    String[] a = new String[elementCount];
                    System.arraycopy(elements, 0, a, 0,
                        Math.min(elements.length, elementCount));
                    elements = a;
                }
                contact.addStringArray(field, attr, elements);
                break;
            }
            case Contact.BIRTHDAY:
            case Contact.REVISION: {
                long date = PIMHandler.getInstance().parseDate(data);
                contact.addDate(field, attr, date);
                break;
            }
            case Contact.PHOTO: {
                String valueType =
                FormatSupport.getAttributeValue(attributes, "VALUE=", null);
                if (valueType == null) {
                    // binary data
                    byte[] bdata = FormatSupport.parseBinary(attributes, data);
                    if (bdata.length != 0) {
                        contact.addBinary(Contact.PHOTO, attr, bdata, 0,
                            bdata.length);
                    }
                } else if (valueType.equals("URL")) {
                    String sdata = FormatSupport.parseString(attributes, data);
                    contact.addString(Contact.PHOTO_URL, attr, sdata);
                } else {
                    // ignore; value type not recognized
                }
                break;
            }
            case Contact.PUBLIC_KEY: {
                String encoding = FormatSupport.getEncoding(attributes);
                if (encoding.equals(FormatSupport.PLAIN_TEXT)) {
                    String sdata = FormatSupport.parseString(attributes, data);
                    contact.addString(Contact.PUBLIC_KEY_STRING, attr, sdata);
                } else {
                    byte[] bdata = FormatSupport.parseBinary(attributes, data);
                    if (bdata.length != 0) {
                        contact.addBinary(Contact.PUBLIC_KEY, attr,
                            bdata, 0, bdata.length);
                    }
                }
                break;
            }
            case Contact.CLASS: {
                int i = VCardSupport.getClassCode(data);
                if (i != -1) {
                    contact.addInt(Contact.CLASS, Contact.ATTR_NONE, i);
                }
                break;
            }
            default:
                // System.err.println("No match for field prefix '"
                // + prefix + "' (REMOVE THIS MESSAGE)");
        }
    }

}