VCardParserImpl_V30.java revision 4560bdde6dd75cca49fc55b58aafb5d416b88ca3
14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/*
24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2010 The Android Open Source Project
34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License.
64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at
74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and
144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License.
154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard;
174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log;
194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Basic implementation achieving vCard 3.0 parsing.
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * This class inherits vCard 2.1 implementation since technically they are similar,
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * while specifically there's logical no relevance between them.
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * So that developers are not confused with the inheritance,
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}.
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 {
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String LOG_TAG = "VCardParserImpl_V30";
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String mPreviousLine;
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean mEmittedAgentWarning = false;
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V30() {
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        super();
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V30(int vcardType) {
494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        super(vcardType);
504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected int getVersion() {
54be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        return VCardConfig.VERSION_30;
554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getVersionString() {
594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardConstants.VERSION_V30;
604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getLine() throws IOException {
644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mPreviousLine != null) {
654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String ret = mPreviousLine;
664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mPreviousLine = null;
674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return ret;
684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mReader.readLine();
704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 requires that the line with space at the beginning of the line
754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * must be combined with previous line.
764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getNonEmptyLine() throws IOException, VCardException {
794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = null;
814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            line = mReader.readLine();
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (builder != null) {
854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return builder.toString();
864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (mPreviousLine != null) {
874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    String ret = mPreviousLine;
884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = null;
894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return ret;
904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Reached end of buffer.");
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.length() == 0) {
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (builder != null) {
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return builder.toString();
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (mPreviousLine != null) {
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    String ret = mPreviousLine;
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = null;
984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return ret;
994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (builder != null) {
1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // See Section 5.8.1 of RFC 2425 (MIME-DIR document).
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // Following is the excerpts from it.
1044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //
1054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // DESCRIPTION:This is a long description that exists on a long line.
1064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // Can be represented as:
1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //
1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // DESCRIPTION:This is a long description
1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //  that exists on a long line.
1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //
1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // It could also be represented as:
1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //
1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // DESCRIPTION:This is a long descrip
1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //  tion that exists o
1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    //  n a long line.
1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line.substring(1));
1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (mPreviousLine != null) {
1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(mPreviousLine);
1214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = null;
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line.substring(1));
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("Space exists at the beginning of the line");
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (mPreviousLine == null) {
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = line;
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (builder != null) {
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        return builder.toString();
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    String ret = mPreviousLine;
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = line;
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return ret;
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         1 * (contentline)
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         ;A vCard object MUST include the VERSION, FN and N types.
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         [group "."] "END" ":" "VCARD" 1 * CRLF
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: vCard 3.0 supports group.
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return super.readBeginVCard(allowGarbage);
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void readEndVCard(boolean useCache, boolean allowGarbage)
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: vCard 3.0 supports group.
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        super.readEndVCard(useCache, allowGarbage);
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleParams(final String params) throws VCardException {
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            super.handleParams(params);
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (VCardException e) {
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // maybe IANA type
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String[] strArray = params.split("=", 2);
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (strArray.length == 2) {
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleAnyParam(strArray[0], strArray[1]);
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Must not come here in the current implementation.
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException(
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "Unknown params value: " + params);
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAnyParam(final String paramName, final String paramValue) {
1824560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType(paramName);
1834560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        splitAndPutParamValue(paramValue);
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected void handleParamWithoutName(final String paramValue) {
1884560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        handleType(paramValue);
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  vCard 3.0 defines
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param         = param-name "=" param-value *("," param-value)
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-name    = iana-token / x-name
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
198a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
199a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
200a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *
201a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2044560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected void handleType(final String paramValue) {
2054560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType("TYPE");
2064560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        splitAndPutParamValue(paramValue);
2074560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
208a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa
2094560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /**
2104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Splits parameter values into pieces in accordance with vCard 3.0 specification and
2114560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * puts pieces into mInterpreter.
2124560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
2134560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /*
2144560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  param-value   = ptext / quoted-string
2154560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
2164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
2174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
2184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *
2194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
2204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
2214560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    private void splitAndPutParamValue(String paramValue) {
2224560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // "comma,separated:inside.dquote",pref
2234560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //   -->
2244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - comma,separated:inside.dquote
2254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - pref
2264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //
2274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // Note: Though there's a code, we don't need to take much care of
2284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // wrongly-added quotes like the example above, as they induce
2294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // parse errors at the top level (when splitting a line into parts).
2304560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        StringBuilder builder = null;  // Delay initialization.
2314560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        boolean insideDquote = false;
2324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final int length = paramValue.length();
2334560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        for (int i = 0; i < length; i++) {
2344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final char ch = paramValue.charAt(i);
2354560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (ch == '"') {
2364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (insideDquote) {
2374560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    // End of Dquote.
2384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    mInterpreter.propertyParamValue(builder.toString());
2394560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2404560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = false;
241a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2424560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    if (builder != null) {
2434560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        if (builder.length() > 0) {
2444560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2454560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref"quoted"
2464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            Log.w(LOG_TAG, "Unexpected Dquote inside property.");
2474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
2484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref,"quoted"
2504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // "quoted",pref
2514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            mInterpreter.propertyParamValue(builder.toString());
2524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        }
253a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
2544560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = true;
255a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else if (ch == ',' && !insideDquote) {
2574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
2594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            paramValue + ")");
260a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
261a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    mInterpreter.propertyParamValue(builder.toString());
2624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2634560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                }
2644560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2654560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // To stop creating empty StringBuffer at the end of parameter,
2664560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // we delay creating this object until this point.
2674560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2684560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = new StringBuilder();
269a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                builder.append(ch);
2714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            }
2724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (insideDquote) {
2744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // e.g.
2754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // "non-quote-at-end
2764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            Log.d(LOG_TAG, "Dangling Dquote.");
2774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (builder != null) {
2794560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (builder.length() == 0) {
2804560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
2814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        "at the end of parameter value parsing.");
2824560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2834560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mInterpreter.propertyParamValue(builder.toString());
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAgent(final String propertyValue) {
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g.
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  ET:jfriday@host.com\nEND:VCARD\n
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: fix this.
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // issue:
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  vCard 3.0 also allows this as an example.
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT;VALUE=uri:
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This is not vCard. Should we support this?
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Just ignore the line for now, since we cannot know how to handle it...
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mEmittedAgentWarning) {
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mEmittedAgentWarning = true;
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 does not require two CRLF at the last of BASE64 data.
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * It only requires that data should be MIME-encoded.
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(final String firstString)
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String line = getLine();
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mPreviousLine = line;
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \\ encodes \, \n or \N encodes newline
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \; encodes ;, \, encodes ,
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: Apple escapes ':' into '\:' while does not escape '\'
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeText(text);
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String unescapeText(final String text) {
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = text.length();
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = text.charAt(i);
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final char next_ch = text.charAt(++i);
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (next_ch == 'n' || next_ch == 'N') {
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\n");
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(next_ch);
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
3744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        return unescapeCharacter(ch);
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    public static String unescapeCharacter(final char ch) {
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == 'n' || ch == 'N') {
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "\n";
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V30.sKnownPropertyNameSet;
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
390