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 {
3902117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    private static final String LOG_TAG = VCardConstants.LOG_TAG;
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
631ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo    protected String peekLine() throws IOException {
641ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo        if (mPreviousLine != null) {
651ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo            String ret = mPreviousLine;
661ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo            return ret;
671ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo        } else {
681ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo            return mReader.peekLine();
691ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo        }
701ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo    }
711ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo
721ed757b861ddc297f47b680bffb56b644cbe486bJoseph Pirozzo    @Override
734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getLine() throws IOException {
744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mPreviousLine != null) {
754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String ret = mPreviousLine;
764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mPreviousLine = null;
774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return ret;
784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mReader.readLine();
804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 requires that the line with space at the beginning of the line
854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * must be combined with previous line.
864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getNonEmptyLine() throws IOException, VCardException {
894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = null;
91210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        while ((line = mReader.readLine()) != null) {
92210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            // Skip empty lines in order to accomodate implementations that
93210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            // send line termination variations such as \r\r\n.
94210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            if (line.length() == 0) {
95210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                continue;
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
97210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                // RFC 2425 describes line continuation as \r\n followed by
98210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                // a single ' ' or '\t' whitespace character.
99210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (builder == null) {
1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
101210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                }
102210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (mPreviousLine != null) {
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(mPreviousLine);
1044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = null;
1054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
106210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                builder.append(line.substring(1));
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
108210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (builder != null || mPreviousLine != null) {
109210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                    break;
1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
111210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                mPreviousLine = line;
1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
114210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner
115210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        String ret = null;
116210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        if (builder != null) {
117210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            ret = builder.toString();
118210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        } else if (mPreviousLine != null) {
119210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            ret = mPreviousLine;
120210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        }
121210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        mPreviousLine = line;
122210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        if (ret == null) {
123210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            throw new VCardException("Reached end of buffer.");
124210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        }
125210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        return ret;
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         1 * (contentline)
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         ;A vCard object MUST include the VERSION, FN and N types.
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         [group "."] "END" ":" "VCARD" 1 * CRLF
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: vCard 3.0 supports group.
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return super.readBeginVCard(allowGarbage);
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1441de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParams(VCardProperty propertyData, final String params)
14548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
14748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            super.handleParams(propertyData, params);
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (VCardException e) {
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // maybe IANA type
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String[] strArray = params.split("=", 2);
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (strArray.length == 2) {
15248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleAnyParam(propertyData, strArray[0], strArray[1]);
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Must not come here in the current implementation.
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException(
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "Unknown params value: " + params);
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
16248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleAnyParam(
1631de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            VCardProperty propertyData, final String paramName, final String paramValue) {
16448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        splitAndPutParam(propertyData, paramName, paramValue);
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1681de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParamWithoutName(VCardProperty property, final String paramValue) {
1691de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        handleType(property, paramValue);
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  vCard 3.0 defines
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param         = param-name "=" param-value *("," param-value)
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-name    = iana-token / x-name
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
179a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
180a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
181a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *
182a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleType(VCardProperty property, final String paramValue) {
1861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        splitAndPutParam(property, VCardConstants.PARAM_TYPE, paramValue);
1874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
188a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa
1894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /**
1904560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Splits parameter values into pieces in accordance with vCard 3.0 specification and
1914560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * puts pieces into mInterpreter.
1924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
1934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /*
1944560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1954560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
1964560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
1974560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
1984560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *
1994560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
2004560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
2011de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private void splitAndPutParam(VCardProperty property, String paramName, String paramValue) {
2024560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // "comma,separated:inside.dquote",pref
2034560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //   -->
2044560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - comma,separated:inside.dquote
2054560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - pref
2064560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //
2074560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // Note: Though there's a code, we don't need to take much care of
2084560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // wrongly-added quotes like the example above, as they induce
2094560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // parse errors at the top level (when splitting a line into parts).
2104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        StringBuilder builder = null;  // Delay initialization.
2114560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        boolean insideDquote = false;
2124560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final int length = paramValue.length();
2134560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        for (int i = 0; i < length; i++) {
2144560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final char ch = paramValue.charAt(i);
2154560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (ch == '"') {
2164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (insideDquote) {
2174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    // End of Dquote.
2181de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = false;
221a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2224560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    if (builder != null) {
2234560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        if (builder.length() > 0) {
2244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref"quoted"
2264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            Log.w(LOG_TAG, "Unexpected Dquote inside property.");
2274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
2284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref,"quoted"
2304560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // "quoted",pref
2311de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                            property.addParameter(paramName, encodeParamValue(builder.toString()));
2324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        }
233a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
2344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = true;
235a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else if (ch == ',' && !insideDquote) {
2374560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
2394560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            paramValue + ")");
240a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2411de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2424560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2434560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                }
2444560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2454560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // To stop creating empty StringBuffer at the end of parameter,
2464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // we delay creating this object until this point.
2474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = new StringBuilder();
249a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                builder.append(ch);
2514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            }
2524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2534560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (insideDquote) {
2544560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // e.g.
2554560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // "non-quote-at-end
2564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            Log.d(LOG_TAG, "Dangling Dquote.");
2574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (builder != null) {
2594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (builder.length() == 0) {
2604560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
2614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        "at the end of parameter value parsing.");
2624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2631de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                property.addParameter(paramName, encodeParamValue(builder.toString()));
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2681de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    /**
2691de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     * Encode a param value using UTF-8.
2701de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     */
2711de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String encodeParamValue(String paramValue) {
2721de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return VCardUtils.convertStringCharset(
2731de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                paramValue, VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, "UTF-8");
2741de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
2751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2771de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleAgent(VCardProperty property) {
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g.
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  ET:jfriday@host.com\nEND:VCARD\n
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: fix this.
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // issue:
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  vCard 3.0 also allows this as an example.
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT;VALUE=uri:
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This is not vCard. Should we support this?
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Just ignore the line for now, since we cannot know how to handle it...
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mEmittedAgentWarning) {
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mEmittedAgentWarning = true;
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
303210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * This is only called from handlePropertyValue(), which has already
304210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * read the first line of this property. With v3.0, the getNonEmptyLine()
305210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * routine has already concatenated all following continuation lines.
306210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * The routine is implemented in the V21 parser to concatenate v2.1 style
307210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * data blocks, but is unnecessary here.
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(final String firstString)
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
312210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        return firstString;
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \\ encodes \, \n or \N encodes newline
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \; encodes ;, \, encodes ,
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: Apple escapes ':' into '\:' while does not escape '\'
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeText(text);
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String unescapeText(final String text) {
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = text.length();
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = text.charAt(i);
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final char next_ch = text.charAt(++i);
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (next_ch == 'n' || next_ch == 'N') {
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\n");
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(next_ch);
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
3484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        return unescapeCharacter(ch);
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    public static String unescapeCharacter(final char ch) {
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == 'n' || ch == 'N') {
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "\n";
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V30.sKnownPropertyNameSet;
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
364