VCardParserImpl_V30.java revision a76f41e328f31c2e9e9006160d8f65fe651eeb6a
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) {
1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        super.handleAnyParam(paramName, paramValue);
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleParamWithoutName(final String paramValue) throws VCardException {
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        super.handleParamWithoutName(paramValue);
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  vCard 3.0 defines
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param         = param-name "=" param-value *("," param-value)
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-name    = iana-token / x-name
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
197a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
198a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
199a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *
200a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
203a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa    protected void handleType(final String paramvalues) {
204a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa        if (mInterpreter != null) {
205a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            mInterpreter.propertyParamType("TYPE");
206a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa
207a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // "comma,separated:inside.dquote",pref
208a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            //   -->
209a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // - comma,separated:inside.dquote
210a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // - pref
211a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            //
212a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // Note: Though there's a code, we don't need to take much care of
213a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // wrongly-added quotes like the example above, as they induce
214a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            // parse errors at the top level (when splitting a line into parts).
215a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            StringBuilder builder = null;  // Delay initialization.
216a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            boolean insideDquote = false;
217a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            final int length = paramvalues.length();
218a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            for (int i = 0; i < length; i = paramvalues.offsetByCodePoints(i, 1)) {
219a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                final int codePoint = paramvalues.codePointAt(i);
220a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                if (codePoint == '"') {
221a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    if (insideDquote) {
222a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        // End of Dquote.
223a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        mInterpreter.propertyParamValue(builder.toString());
224a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        builder = null;
225a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        insideDquote = false;
226a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    } else {
227a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        if (builder != null) {
228a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                            if (builder.length() > 0) {
229a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                // e.g.
230a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                // pref"quoted"
231a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                Log.w(LOG_TAG, "Unexpected Dquote inside property.");
232a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                            } else {
233a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                // e.g.
234a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                // pref,"quoted"
235a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                // "quoted",pref
236a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                mInterpreter.propertyParamValue(builder.toString());
237a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                            }
238a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        }
239a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        insideDquote = true;
240a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
241a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else if (codePoint == ',' && !insideDquote) {
242a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    if (builder == null) {
243a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
244a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                                paramvalues + ")");
245a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    } else {
246a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        mInterpreter.propertyParamValue(builder.toString());
247a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        builder = null;
248a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
249a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
250a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    // To stop creating empty StringBuffer at the end of parameter,
251a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    // we delay creating this object until this point.
252a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    if (builder == null) {
253a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                        builder = new StringBuilder();
254a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
255a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    builder.appendCodePoint(codePoint);
256a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
257a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            }
258a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            if (insideDquote) {
259a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                // e.g.
260a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                // "non-quote-at-end
261a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                Log.d(LOG_TAG, "Dangling Dquote.");
262a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            }
263a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa            if (builder != null) {
264a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                if (builder.length() == 0) {
265a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
266a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                            "at the end of parameter value parsing.");
267a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
268a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    mInterpreter.propertyParamValue(builder.toString());
269a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAgent(final String propertyValue) {
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g.
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  ET:jfriday@host.com\nEND:VCARD\n
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: fix this.
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // issue:
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  vCard 3.0 also allows this as an example.
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT;VALUE=uri:
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This is not vCard. Should we support this?
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Just ignore the line for now, since we cannot know how to handle it...
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mEmittedAgentWarning) {
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mEmittedAgentWarning = true;
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 does not require two CRLF at the last of BASE64 data.
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * It only requires that data should be MIME-encoded.
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(final String firstString)
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String line = getLine();
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mPreviousLine = line;
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \\ encodes \, \n or \N encodes newline
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \; encodes ;, \, encodes ,
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: Apple escapes ':' into '\:' while does not escape '\'
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeText(text);
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String unescapeText(final String text) {
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = text.length();
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = text.charAt(i);
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final char next_ch = text.charAt(++i);
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (next_ch == 'n' || next_ch == 'N') {
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\n");
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(next_ch);
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
359be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    protected String maybeEscapeCharacter(final char ch) {
360be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        return escapeCharacter(ch);
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
363be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    public static String escapeCharacter(final char ch) {
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == 'n' || ch == 'N') {
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "\n";
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V30.sKnownPropertyNameSet;
3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
376