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;
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    /**
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1571de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParams(VCardProperty propertyData, final String params)
15848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
16048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            super.handleParams(propertyData, params);
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (VCardException e) {
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // maybe IANA type
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String[] strArray = params.split("=", 2);
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (strArray.length == 2) {
16548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleAnyParam(propertyData, strArray[0], strArray[1]);
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Must not come here in the current implementation.
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException(
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        "Unknown params value: " + params);
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
17548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleAnyParam(
1761de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            VCardProperty propertyData, final String paramName, final String paramValue) {
17748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        splitAndPutParam(propertyData, paramName, paramValue);
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1811de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParamWithoutName(VCardProperty property, final String paramValue) {
1821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        handleType(property, paramValue);
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  vCard 3.0 defines
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param         = param-name "=" param-value *("," param-value)
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-name    = iana-token / x-name
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  param-value   = ptext / quoted-string
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
192a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
193a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
194a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *
195a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
1981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleType(VCardProperty property, final String paramValue) {
1991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        splitAndPutParam(property, VCardConstants.PARAM_TYPE, paramValue);
2004560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
201a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa
2024560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /**
2034560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Splits parameter values into pieces in accordance with vCard 3.0 specification and
2044560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * puts pieces into mInterpreter.
2054560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
2064560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /*
2074560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  param-value   = ptext / quoted-string
2084560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
2094560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
2104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *                ; Any character except CTLs, DQUOTE
2114560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *
2124560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
2134560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
2141de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private void splitAndPutParam(VCardProperty property, String paramName, String paramValue) {
2154560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // "comma,separated:inside.dquote",pref
2164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //   -->
2174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - comma,separated:inside.dquote
2184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // - pref
2194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        //
2204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // Note: Though there's a code, we don't need to take much care of
2214560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // wrongly-added quotes like the example above, as they induce
2224560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        // parse errors at the top level (when splitting a line into parts).
2234560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        StringBuilder builder = null;  // Delay initialization.
2244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        boolean insideDquote = false;
2254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final int length = paramValue.length();
2264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        for (int i = 0; i < length; i++) {
2274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final char ch = paramValue.charAt(i);
2284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (ch == '"') {
2294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (insideDquote) {
2304560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    // End of Dquote.
2311de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2334560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = false;
234a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2354560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    if (builder != null) {
2364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        if (builder.length() > 0) {
2374560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref"quoted"
2394560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            Log.w(LOG_TAG, "Unexpected Dquote inside property.");
2404560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
2414560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // e.g.
2424560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // pref,"quoted"
2434560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            // "quoted",pref
2441de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                            property.addParameter(paramName, encodeParamValue(builder.toString()));
2454560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        }
246a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                    }
2474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    insideDquote = true;
248a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else if (ch == ',' && !insideDquote) {
2504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
2524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                            paramValue + ")");
253a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                } else {
2541de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    property.addParameter(paramName, encodeParamValue(builder.toString()));
2554560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = null;
2564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                }
2574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // To stop creating empty StringBuffer at the end of parameter,
2594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                // we delay creating this object until this point.
2604560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (builder == null) {
2614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    builder = new StringBuilder();
262a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa                }
2634560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                builder.append(ch);
2644560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            }
2654560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2664560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (insideDquote) {
2674560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // e.g.
2684560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            // "non-quote-at-end
2694560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            Log.d(LOG_TAG, "Dangling Dquote.");
2704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
2714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (builder != null) {
2724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (builder.length() == 0) {
2734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
2744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        "at the end of parameter value parsing.");
2754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            } else {
2761de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                property.addParameter(paramName, encodeParamValue(builder.toString()));
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2811de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    /**
2821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     * Encode a param value using UTF-8.
2831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa     */
2841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String encodeParamValue(String paramValue) {
2851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return VCardUtils.convertStringCharset(
2861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                paramValue, VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, "UTF-8");
2871de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
2881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
2901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleAgent(VCardProperty property) {
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g.
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  ET:jfriday@host.com\nEND:VCARD\n
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // TODO: fix this.
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // issue:
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  vCard 3.0 also allows this as an example.
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // AGENT;VALUE=uri:
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This is not vCard. Should we support this?
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Just ignore the line for now, since we cannot know how to handle it...
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!mEmittedAgentWarning) {
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mEmittedAgentWarning = true;
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 does not require two CRLF at the last of BASE64 data.
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * It only requires that data should be MIME-encoded.
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(final String firstString)
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String line = getLine();
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mPreviousLine = line;
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \\ encodes \, \n or \N encodes newline
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *              ; \; encodes ;, \, encodes ,
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: Apple escapes ':' into '\:' while does not escape '\'
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeText(text);
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String unescapeText(final String text) {
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = text.length();
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = text.charAt(i);
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final char next_ch = text.charAt(++i);
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (next_ch == 'n' || next_ch == 'N') {
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\n");
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(next_ch);
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
3754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        return unescapeCharacter(ch);
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    public static String unescapeCharacter(final char ch) {
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == 'n' || ch == 'N') {
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "\n";
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @Override
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V30.sKnownPropertyNameSet;
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
391