Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ac671ad authored by Daisuke Miyakawa's avatar Daisuke Miyakawa
Browse files

Add comments for vCard parser.

Bug: 2576594
Change-Id: I3303a350bdea7b1b84bd1fb318d31ba5a229457d
parent 182a2f38
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -91,10 +91,12 @@ public class VCardBuilder {
    private static final String VCARD_WS = " ";
    private static final String VCARD_PARAM_EQUAL = "=";

    private static final String VCARD_PARAM_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";

    private static final String VCARD_PARAM_ENCODING_BASE64_V21 = "ENCODING=BASE64";
    private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
    private static final String VCARD_PARAM_ENCODING_QP =
            "ENCODING=" + VCardConstants.PARAM_ENCODING_QP;
    private static final String VCARD_PARAM_ENCODING_BASE64_V21 =
            "ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64;
    private static final String VCARD_PARAM_ENCODING_BASE64_V30 =
            "ENCODING=" + VCardConstants.PARAM_ENCODING_B;

    private static final String SHIFT_JIS = "SHIFT_JIS";

+12 −4
Original line number Diff line number Diff line
@@ -109,6 +109,12 @@ public class VCardConstants {
    public static final String PARAM_TYPE_BBS = "BBS";
    public static final String PARAM_TYPE_VIDEO = "VIDEO";

    public static final String PARAM_ENCODING_7BIT = "7BIT";
    public static final String PARAM_ENCODING_8BIT = "8BIT";
    public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE";
    public static final String PARAM_ENCODING_BASE64 = "BASE64";  // Available in vCard 2.1
    public static final String PARAM_ENCODING_B = "B";  // Available in vCard 3.0

    // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1).
    // These types are basically encoded to "X-" parameters when composing vCard.
    // Parser passes these when "X-" is added to the parameter or not.
@@ -130,10 +136,6 @@ public class VCardConstants {
    // Do not use in composer side.
    public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";

    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of SORT-STRING in
    // vCard 3.0.
    public static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";

    public interface ImportOnly {
        public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
        // Some device emits this "X-" parameter for expressing Google Talk,
@@ -142,6 +144,12 @@ public class VCardConstants {
        public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
    }

    //// Mainly for package constants.

    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of
    // SORT-STRING invCard 3.0.
    /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";

    /* package */ static final int MAX_DATA_COLUMN = 15;

    /* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
+62 −67
Original line number Diff line number Diff line
@@ -63,10 +63,7 @@ import java.util.Set;

    private static final String sDefaultEncoding = "8BIT";

    //// Protected members

    protected boolean mCanceled;

    protected VCardInterpreter mInterpreter;

    /**
@@ -109,7 +106,6 @@ import java.util.Set;
     */
    protected final Set<String> mUnknownValueSet = new HashSet<String>();

    // // Private members

    // In some cases, vCard is nested. Currently, we only consider the most
    // interior vCard data.
@@ -121,27 +117,16 @@ import java.util.Set;

    // For measuring performance.
    private long mTimeTotal;

    private long mTimeReadStartRecord;

    private long mTimeReadEndRecord;

    private long mTimeStartProperty;

    private long mTimeEndProperty;

    private long mTimeParseItems;

    private long mTimeParseLineAndHandleGroup;

    private long mTimeParsePropertyValues;

    private long mTimeParseAdrOrgN;

    private long mTimeHandleMiscPropertyValue;

    private long mTimeHandleQuotedPrintable;

    private long mTimeHandleBase64;

    /**
@@ -156,8 +141,7 @@ import java.util.Set;

    /**
     * <p>
     * The constructor which uses the estimated type available from a given
     * detector.
     * The constructor which uses the estimated type available from a given detector.
     * </p>
     */
    public VCardParserImpl_V21(VCardSourceDetector detector) {
@@ -186,15 +170,15 @@ import java.util.Set;
     */
    // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
    protected void parseVCardFile() throws IOException, VCardException {
        boolean firstReading = true;
        boolean firstRead = true;
        while (true) {
            if (mCanceled) {
                break;
            }
            if (!parseOneVCard(firstReading)) {
            if (!parseOneVCard(firstRead)) {
                break;
            }
            firstReading = false;
            firstRead = false;
        }

        if (mNestCount > 0) {
@@ -245,12 +229,13 @@ import java.util.Set;
    }

    /*
     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws]
     * ":" [ws] "VCARD"
     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
     *         items *CRLF
     *         "END" [ws] ":" [ws] "VCARD"
     */
    private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException {
    private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException {
        boolean allowGarbage = false;
        if (firstReading) {
        if (firstRead) {
            if (mNestCount > 0) {
                for (int i = 0; i < mNestCount; i++) {
                    if (!readBeginVCard(allowGarbage)) {
@@ -456,34 +441,29 @@ import java.util.Set;
        throw new VCardException("Unknown property name: \"" + propertyName + "\"");
    }

    static private final int STATE_GROUP_OR_PROPNAME = 0;

    // For performance reason, the states for group and property name are merged into one.
    static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
    static private final int STATE_PARAMS = 1;

    // vCard 3.0 specification allows double-quoted param-value, while vCard 2.1
    // does not.
    // This is just for safety.
    // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
    static private final int STATE_PARAMS_IN_DQUOTE = 2;

    protected String[] separateLineAndHandleGroup(String line) throws VCardException {
        int state = STATE_GROUP_OR_PROPNAME;
        int nameIndex = 0;

        final String[] propertyNameAndValue = new String[2];

        final int length = line.length();
        if (length > 0 && line.charAt(0) == '#') {
            throw new VCardInvalidCommentLineException();
        }

        // This loop is developed so that we don't have to take care of bottle
        // neck here.
        int state = STATE_GROUP_OR_PROPERTY_NAME;
        int nameIndex = 0;

        // This loop is developed so that we don't have to take care of bottle neck here.
        // Refactor carefully when you need to do so.
        for (int i = 0; i < length; i++) {
            final char ch = line.charAt(i);
            switch (state) {
                case STATE_GROUP_OR_PROPNAME: {
                    if (ch == ':') {
                case STATE_GROUP_OR_PROPERTY_NAME: {
                    if (ch == ':') {  // End of a property name.
                        final String propertyName = line.substring(nameIndex, i);
                        if (propertyName.equalsIgnoreCase("END")) {
                            mPreviousLine = line;
@@ -499,14 +479,16 @@ import java.util.Set;
                            propertyNameAndValue[1] = "";
                        }
                        return propertyNameAndValue;
                    } else if (ch == '.') {
                        String groupName = line.substring(nameIndex, i);
                        if (mInterpreter != null) {
                    } else if (ch == '.') {  // Each group is followed by the dot.
                        final String groupName = line.substring(nameIndex, i);
                        if (groupName.length() == 0) {
                            Log.w(LOG_TAG, "Empty group found. Ignoring.");
                        } else if (mInterpreter != null) {
                            mInterpreter.propertyGroup(groupName);
                        }
                        nameIndex = i + 1;
                    } else if (ch == ';') {
                        String propertyName = line.substring(nameIndex, i);
                        nameIndex = i + 1;  // Next should be another group or a property name.
                    } else if (ch == ';') {  // End of property name and beginneng of parameters.  
                        final String propertyName = line.substring(nameIndex, i);
                        if (propertyName.equalsIgnoreCase("END")) {
                            mPreviousLine = line;
                            return null;
@@ -516,17 +498,21 @@ import java.util.Set;
                        }
                        propertyNameAndValue[0] = propertyName;
                        nameIndex = i + 1;
                        state = STATE_PARAMS;
                        state = STATE_PARAMS;  // Start parameter parsing.
                    }
                    break;
                }
                case STATE_PARAMS: {
                    if (ch == '"') {
                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
                                    "Silently allow it");
                        }
                        state = STATE_PARAMS_IN_DQUOTE;
                    } else if (ch == ';') {
                    } else if (ch == ';') {  // Starts another param.
                        handleParams(line.substring(nameIndex, i));
                        nameIndex = i + 1;
                    } else if (ch == ':') {
                    } else if (ch == ':') {  // End of param and beginenning of values.
                        handleParams(line.substring(nameIndex, i));
                        if (i < length - 1) {
                            propertyNameAndValue[1] = line.substring(i + 1);
@@ -539,6 +525,10 @@ import java.util.Set;
                }
                case STATE_PARAMS_IN_DQUOTE: {
                    if (ch == '"') {
                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
                                    "Silently allow it");
                        }
                        state = STATE_PARAMS;
                    }
                    break;
@@ -557,7 +547,7 @@ import java.util.Set;
     * [ws] word / knowntype
     */
    protected void handleParams(String params) throws VCardException {
        String[] strArray = params.split("=", 2);
        final String[] strArray = params.split("=", 2);
        if (strArray.length == 2) {
            final String paramName = strArray[0].trim().toUpperCase();
            String paramValue = strArray[1].trim();
@@ -582,7 +572,7 @@ import java.util.Set;
    }

    /**
     * vCard 3.0 parser may throw VCardException.
     * vCard 3.0 parser implementation may throw VCardException.
     */
    @SuppressWarnings("unused")
    protected void handleParamWithoutName(final String paramValue) throws VCardException {
@@ -593,15 +583,15 @@ import java.util.Set;
     * ptypeval = knowntype / "X-" word
     */
    protected void handleType(final String ptypeval) {
        String upperTypeValue = ptypeval;
        if (!(getKnownTypeSet().contains(upperTypeValue) || upperTypeValue.startsWith("X-"))
        if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
                || ptypeval.startsWith("X-"))
                && !mUnknownTypeSet.contains(ptypeval)) {
            mUnknownTypeSet.add(ptypeval);
            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
        }
        if (mInterpreter != null) {
            mInterpreter.propertyParamType("TYPE");
            mInterpreter.propertyParamValue(upperTypeValue);
            mInterpreter.propertyParamValue(ptypeval);
        }
    }

@@ -609,10 +599,12 @@ import java.util.Set;
     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
     */
    protected void handleValue(final String pvalueval) {
        if (!getKnownValueSet().contains(pvalueval.toUpperCase()) && pvalueval.startsWith("X-")
                && !mUnknownValueSet.contains(pvalueval)) {
        if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
                || pvalueval.startsWith("X-")
                || mUnknownValueSet.contains(pvalueval))) {
            mUnknownValueSet.add(pvalueval);
            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), pvalueval));
            Log.w(LOG_TAG, String.format(
                    "The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
        }
        if (mInterpreter != null) {
            mInterpreter.propertyParamType("VALUE");
@@ -621,8 +613,7 @@ import java.util.Set;
    }

    /*
     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-"
     * word
     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
     */
    protected void handleEncoding(String pencodingval) throws VCardException {
        if (getAvailableEncodingSet().contains(pencodingval) ||
@@ -638,8 +629,11 @@ import java.util.Set;
    }

    /**
     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC
     * 1521), but today's vCard often contains other charset, so we allow them.
     * <p>
     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
     * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
     * We allow any charset.
     * </p>
     */
    protected void handleCharset(String charsetval) {
        if (mInterpreter != null) {
@@ -695,7 +689,8 @@ import java.util.Set;

    protected void handlePropertyValue(String propertyName, String propertyValue)
            throws IOException, VCardException {
        if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
        final String upperEncoding = mCurrentEncoding.toUpperCase();
        if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
            final long start = System.currentTimeMillis();
            final String result = getQuotedPrintable(propertyValue);
            if (mInterpreter != null) {
@@ -704,8 +699,8 @@ import java.util.Set;
                mInterpreter.propertyValues(v);
            }
            mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
        } else if (mCurrentEncoding.equalsIgnoreCase("BASE64")
                || mCurrentEncoding.equalsIgnoreCase("B")) {
        } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
                || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
            final long start = System.currentTimeMillis();
            // It is very rare, but some BASE64 data may be so big that
            // OutOfMemoryError occurs. To ignore such cases, use try-catch.
@@ -724,11 +719,11 @@ import java.util.Set;
            }
            mTimeHandleBase64 += System.currentTimeMillis() - start;
        } else {
            if (!(mCurrentEncoding == null || mCurrentEncoding.equalsIgnoreCase("7BIT")
                    || mCurrentEncoding.equalsIgnoreCase("8BIT") || mCurrentEncoding.toUpperCase()
                    .startsWith("X-"))) {
                Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mCurrentEncoding
                        + "\".");
            if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
                    upperEncoding.startsWith("X-"))) {
                Log.w(LOG_TAG,
                        String.format("The encoding \"%s\" is unsupported by vCard %s",
                                mCurrentEncoding, getVersionString()));
            }

            final long start = System.currentTimeMillis();
+13 −3
Original line number Diff line number Diff line
@@ -21,11 +21,22 @@ import java.util.Set;
import android.pim.vcard.exception.VCardException;
import android.util.Log;

/**
 * <p>
 * Basic implementation achieving vCard 3.0 parsing.
 * </p>
 * <p>
 * This class inherits vCard 2.1 implementation since technically they are similar,
 * while specifically there's logical no relevance between them.
 * So that developers are not confused with the inheritance,
 * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while
 * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}.
 * </p>
 */
/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 {
    private static final String LOG_TAG = "VCardParserImpl_V30";

    private String mPreviousLine;

    private boolean mEmittedAgentWarning = false;

    public VCardParserImpl_V30() {
@@ -239,8 +250,7 @@ import android.util.Log;
        while (true) {
            final String line = getLine();
            if (line == null) {
                throw new VCardException(
                        "File ended during parsing BASE64 binary");
                throw new VCardException("File ended during parsing BASE64 binary");
            }
            if (line.length() == 0) {
                break;
+20 −13
Original line number Diff line number Diff line
@@ -26,14 +26,13 @@ import java.util.Set;

/**
 * </p>
 * vCard parser implementation mostly for vCard 2.1. See the specification for more detail
 * about the spec itself.
 * vCard parser for vCard 2.1. See the specification for more detail about the spec itself.
 * </p>
 * <p>
 * The spec is written in 1996, and currently various types of "vCard 2.1" exist.
 * To handle real the world vCard formats appropriately and effectively, this class does not
 * obey with strict vCard 2.1. In stead, not only vCard spec but also real world
 * vCard is considered.
 * obey with strict vCard 2.1.
 * In stead, not only vCard spec but also real world vCard is considered.
 * </p>
 * e.g. A lot of devices and softwares let vCard importer/exporter to use
 * the PNG format to determine the type of image, while it is not allowed in
@@ -45,7 +44,7 @@ public final class VCardParser_V21 implements VCardParser {
    /**
     * A unmodifiable Set storing the property names available in the vCard 2.1 specification.
     */
    public static final Set<String> sKnownPropertyNameSet =
    /* package */ static final Set<String> sKnownPropertyNameSet =
            Collections.unmodifiableSet(new HashSet<String>(
                    Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
                            "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
@@ -54,7 +53,7 @@ public final class VCardParser_V21 implements VCardParser {
    /**
     * A unmodifiable Set storing the types known in vCard 2.1.
     */
    public static final Set<String> sKnownTypeSet =
    /* package */ static final Set<String> sKnownTypeSet =
            Collections.unmodifiableSet(new HashSet<String>(
                    Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
                            "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
@@ -66,20 +65,28 @@ public final class VCardParser_V21 implements VCardParser {
                            "WAVE", "AIFF", "PCM", "X509", "PGP")));

    /**
     * A unmodifiable Set storing the values available in the vCard 2.1 specification.
     * A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1.
     */
    public static final Set<String> sKnownValueSet =
    /* package */ static final Set<String> sKnownValueSet =
            Collections.unmodifiableSet(new HashSet<String>(
                    Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")));

    /**
     * <p>
     * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1.
     * </p>
     * <p>
     * Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
     * We allow it for safety...
     * We allow it for safety.
     * </p>
     */
    // TODO: move B to another something and make this member public
    /* package */ static final HashSet<String> sAvailableEncoding =
        new HashSet<String>(Arrays.asList(
                "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
    /* package */ static final Set<String> sAvailableEncoding =
        Collections.unmodifiableSet(new HashSet<String>(
                Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT,
                        VCardConstants.PARAM_ENCODING_8BIT,
                        VCardConstants.PARAM_ENCODING_QP,
                        VCardConstants.PARAM_ENCODING_BASE64,
                        VCardConstants.PARAM_ENCODING_B)));

    private final VCardParserImpl_V21 mVCardParserImpl;

Loading