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
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;
81210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        while ((line = mReader.readLine()) != null) {
82210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            // Skip empty lines in order to accomodate implementations that
83210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            // send line termination variations such as \r\r\n.
84210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            if (line.length() == 0) {
85210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                continue;
864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
87210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                // RFC 2425 describes line continuation as \r\n followed by
88210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                // a single ' ' or '\t' whitespace character.
89210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (builder == null) {
904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
91210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                }
92210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (mPreviousLine != null) {
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(mPreviousLine);
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = null;
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
96210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                builder.append(line.substring(1));
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
98210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                if (builder != null || mPreviousLine != null) {
99210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                    break;
1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
101210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner                mPreviousLine = line;
1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
104210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner
105210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        String ret = null;
106210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        if (builder != null) {
107210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            ret = builder.toString();
108210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        } else if (mPreviousLine != null) {
109210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            ret = mPreviousLine;
110210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        }
111210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        mPreviousLine = line;
112210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        if (ret == null) {
113210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner            throw new VCardException("Reached end of buffer.");
114210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        }
115210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        return ret;
1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         1 * (contentline)
1214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         ;A vCard object MUST include the VERSION, FN and N types.
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         [group "."] "END" ":" "VCARD" 1 * CRLF
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: vCard 3.0 supports group.
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return super.readBeginVCard(allowGarbage);
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParams(VCardProperty propertyData, final String params)
13548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
13748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            super.handleParams(propertyData, params);
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (VCardException e) {
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // maybe IANA type
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String[] strArray = params.split("=", 2);
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (strArray.length == 2) {
14248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleAnyParam(propertyData, strArray[0], strArray[1]);
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Must not come here in the current implementation.
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException(
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "Unknown params value: " + params);
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
15248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleAnyParam(
1531de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            VCardProperty propertyData, final String paramName, final String paramValue) {
15448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        splitAndPutParam(propertyData, paramName, paramValue);
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1581de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParamWithoutName(VCardProperty property, final String paramValue) {
1591de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        handleType(property, paramValue);
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  vCard 3.0 defines
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param         = param-name "=" param-value *("," param-value)
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-name    = iana-token / x-name
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
169a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
170a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
171a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *
172a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleType(VCardProperty property, final String paramValue) {
1761de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        splitAndPutParam(property, VCardConstants.PARAM_TYPE, paramValue);
1774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
178a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa
1794560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /**
1804560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Splits parameter values into pieces in accordance with vCard 3.0 specification and
1814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * puts pieces into mInterpreter.
1824560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
1834560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /*
1844560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1854560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
1864560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
1874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
1884560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *
1894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
1904560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
1911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private void splitAndPutParam(VCardProperty property, String paramName, String paramValue) {
1924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // "comma,separated:inside.dquote",pref
1934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //   -->
1944560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - comma,separated:inside.dquote
1954560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - pref
1964560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //
1974560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // Note: Though there's a code, we don't need to take much care of
1984560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // wrongly-added quotes like the example above, as they induce
1994560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // parse errors at the top level (when splitting a line into parts).
2004560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        StringBuilder builder = null;  // Delay initialization.
2014560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        boolean insideDquote = false;
2024560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final int length = paramValue.length();
2034560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        for (int i = 0; i < length; i++) {
2044560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final char ch = paramValue.charAt(i);
2054560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (ch == '"') {
2064560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (insideDquote) {
2074560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    // End of Dquote.
2081de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2094560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = false;
211a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2124560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    if (builder != null) {
2134560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        if (builder.length() > 0) {
2144560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2154560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref"quoted"
2164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            Log.w(LOG_TAG, "Unexpected Dquote inside property.");
2174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
2184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref,"quoted"
2204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // "quoted",pref
2211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                            property.addParameter(paramName, encodeParamValue(builder.toString()));
2224560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        }
223a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
2244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = true;
225a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else if (ch == ',' && !insideDquote) {
2274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
2294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            paramValue + ")");
230a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2311de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2334560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                }
2344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2354560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // To stop creating empty StringBuffer at the end of parameter,
2364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // we delay creating this object until this point.
2374560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = new StringBuilder();
239a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2404560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                builder.append(ch);
2414560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            }
2424560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2434560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (insideDquote) {
2444560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // e.g.
2454560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // "non-quote-at-end
2464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            Log.d(LOG_TAG, "Dangling Dquote.");
2474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (builder != null) {
2494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (builder.length() == 0) {
2504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
2514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        "at the end of parameter value parsing.");
2524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2531de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                property.addParameter(paramName, encodeParamValue(builder.toString()));
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2581de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    /**
2591de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     * Encode a param value using UTF-8.
2601de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     */
2611de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String encodeParamValue(String paramValue) {
2621de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return VCardUtils.convertStringCharset(
2631de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                paramValue, VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, "UTF-8");
2641de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
2651de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2671de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleAgent(VCardProperty property) {
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g.
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  ET:jfriday@host.com\nEND:VCARD\n
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: fix this.
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // issue:
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  vCard 3.0 also allows this as an example.
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT;VALUE=uri:
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This is not vCard. Should we support this?
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Just ignore the line for now, since we cannot know how to handle it...
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mEmittedAgentWarning) {
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mEmittedAgentWarning = true;
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
293210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * This is only called from handlePropertyValue(), which has already
294210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * read the first line of this property. With v3.0, the getNonEmptyLine()
295210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * routine has already concatenated all following continuation lines.
296210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * The routine is implemented in the V21 parser to concatenate v2.1 style
297210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner     * data blocks, but is unnecessary here.
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(final String firstString)
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
302210ebaab315e389ffc66278e28ecd6230e412b5fJay Shrauner        return firstString;
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \\ encodes \, \n or \N encodes newline
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \; encodes ;, \, encodes ,
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: Apple escapes ':' into '\:' while does not escape '\'
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeText(text);
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String unescapeText(final String text) {
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = text.length();
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = text.charAt(i);
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final char next_ch = text.charAt(++i);
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (next_ch == 'n' || next_ch == 'N') {
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\n");
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(next_ch);
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3374560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
3384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        return unescapeCharacter(ch);
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3414560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    public static String unescapeCharacter(final char ch) {
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == 'n' || ch == 'N') {
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "\n";
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V30.sKnownPropertyNameSet;
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
354