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
18a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.text.TextUtils;
19a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.util.Base64;
20a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.util.Log;
21a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardAgentNotSupportedException;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidCommentLineException;
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidLineException;
264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardVersionException;
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.BufferedReader;
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException;
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStream;
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStreamReader;
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.Reader;
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList;
341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawaimport java.util.Collection;
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawaimport java.util.List;
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
41677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * Basic implementation achieving vCard parsing. Based on vCard 2.1.
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide
444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V21 {
4602117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    private static final String LOG_TAG = VCardConstants.LOG_TAG;
474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
48147f1ae5371954ae845cb2330b221df6ca1d8831Daisuke Miyakawa    protected static final class CustomBufferedReader extends BufferedReader {
494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private long mTime;
504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
51f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
52f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Needed since "next line" may be null due to end of line.
53f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
54f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private boolean mNextLineIsValid;
55f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private String mNextLine;
56f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public CustomBufferedReader(Reader in) {
584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            super(in);
594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        @Override
624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public String readLine() throws IOException {
63f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (mNextLineIsValid) {
64f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String ret = mNextLine;
65f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = null;
66f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = false;
67f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                return ret;
68f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
69f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
70677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long start = System.currentTimeMillis();
71f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String line = super.readLine();
72677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long end = System.currentTimeMillis();
734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTime += end - start;
74f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return line;
75f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        }
76f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
77f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
78f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Read one line, but make this object store it in its queue.
79f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
80f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        public String peekLine() throws IOException {
81f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (!mNextLineIsValid) {
82677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long start = System.currentTimeMillis();
83f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String line = super.readLine();
84677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long end = System.currentTimeMillis();
85f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mTime += end - start;
86f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
87f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = line;
88f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = true;
89f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
90f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
91f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return mNextLine;
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public long getTotalmillisecond() {
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mTime;
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String DEFAULT_ENCODING = "8BIT";
1001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private static final String DEFAULT_CHARSET = "UTF-8";
1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final String mIntermediateCharset;
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private final List<VCardInterpreter> mInterpreterList = new ArrayList<VCardInterpreter>();
1051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private boolean mCanceled;
10648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The encoding type for deconding byte streams. This member variable is
1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * reset to a default encoding every time when a new item comes.
1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "Encoding" in vCard is different from "Charset". It is mainly used for
1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * addresses, notes, images. "7BIT", "8BIT", "BASE64", and
1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "QUOTED-PRINTABLE" are known examples.
1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String mCurrentEncoding;
1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String mCurrentCharset;
1211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The reader object to be used internally.
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Developers should not directly read a line from this object. Use
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * getLine() unless there some reason.
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
131f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected CustomBufferedReader mReader;
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * specification, but happens to be seen in real world vCard.
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
138677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
139677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
140677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownTypeSet = new HashSet<String>();
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn VALUE attributes, which is not acceptable in
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard specification, but happens to be seen in real world vCard.
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
149677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
150677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
151677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownValueSet = new HashSet<String>();
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21() {
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(VCardConfig.VCARD_TYPE_DEFAULT);
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21(int vcardType) {
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mIntermediateCharset =  VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return true when a given property name is a valid property name.
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean isValidPropertyName(final String propertyName) {
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) ||
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                propertyName.startsWith("X-"))
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(propertyName)) {
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(propertyName);
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String. It may be null, or its length may be 0
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getLine() throws IOException {
1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mReader.readLine();
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
185f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected String peekLine() throws IOException {
186f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        return mReader.peekLine();
187f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    }
188f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String with it's length > 0
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException when the stream reached end of line
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getNonEmptyLine() throws IOException, VCardException {
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            line = getLine();
1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Reached end of buffer.");
2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.trim().length() > 0) {
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return line;
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
20648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    /**
20748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * <code>
2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
2094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         items *CRLF
2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         "END" [ws] ":" [ws] "VCARD"
21148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * </code>
21256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa     * @return False when reaching end of file.
2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
21448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private boolean parseOneVCard() throws IOException, VCardException {
21548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // reset for this entire vCard.
21648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        mCurrentEncoding = DEFAULT_ENCODING;
2171de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        mCurrentCharset = DEFAULT_CHARSET;
2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
21948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        boolean allowGarbage = false;
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!readBeginVCard(allowGarbage)) {
2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2231de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
2241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onEntryStarted();
2251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        parseItems();
2271de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
2281de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onEntryEnded();
2291de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return True when successful. False when reaching the end of line
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
23948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // TODO: use consructPropertyLine().
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        do {
2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (line.trim().length() > 0) {
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
250f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String[] strArray = line.split(":", 2);
251f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final int length = strArray.length;
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
253f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Although vCard 2.1/3.0 specification does not allow lower cases,
254f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // we found vCard file emitted by some external vCard expoter have such
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // invalid Strings.
25648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            // e.g. BEGIN:vCard
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return true;
2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!allowGarbage) {
26148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
26248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        + "(Instead, \"" + line + "\" came)");
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } while (allowGarbage);
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardException("Reached where must not be reached.");
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
27048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Parses lines other than the first "BEGIN:VCARD". Takes care of "END:VCARD"n and
27148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * "BEGIN:VCARD" in nested vCard.
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * items = *CRLF item / item
27548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     *
27648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Note: BEGIN/END aren't include in the original spec while this method handles them.
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void parseItems() throws IOException, VCardException {
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean ended = false;
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
28148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        try {
28248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            ended = parseItem();
28348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } catch (VCardInvalidCommentLineException e) {
28448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (!ended) {
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                ended = parseItem();
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (VCardInvalidCommentLineException e) {
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR"
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."]
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "AGENT" [params] ":" vcard CRLF
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean parseItem() throws IOException, VCardException {
30348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // Reset for an item.
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCurrentEncoding = DEFAULT_ENCODING;
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String line = getNonEmptyLine();
3071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final VCardProperty propertyData = constructPropertyData(line);
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
30948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final String propertyNameUpper = propertyData.getName().toUpperCase();
3101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final String propertyRawValue = propertyData.getRawValue();
31148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
31248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        if (propertyNameUpper.equals(VCardConstants.PROPERTY_BEGIN)) {
3131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            if (propertyRawValue.equalsIgnoreCase("VCARD")) {
31448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleNest();
31548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            } else {
3161de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                throw new VCardException("Unknown BEGIN type: " + propertyRawValue);
31748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
31848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_END)) {
3191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            if (propertyRawValue.equalsIgnoreCase("VCARD")) {
32048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                return true;  // Ended.
32148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            } else {
3221de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                throw new VCardException("Unknown END type: " + propertyRawValue);
32348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
32448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else {
3251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            parseItemInter(propertyData, propertyNameUpper);
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
32748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        return false;
32848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
32948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
3301de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private void parseItemInter(VCardProperty property, String propertyNameUpper)
33148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws IOException, VCardException {
3321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        String propertyRawValue = property.getRawValue();
3331de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (propertyNameUpper.equals(VCardConstants.PROPERTY_AGENT)) {
3341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            handleAgent(property);
33548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else if (isValidPropertyName(propertyNameUpper)) {
33648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (propertyNameUpper.equals(VCardConstants.PROPERTY_VERSION) &&
3371de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    !propertyRawValue.equals(getVersionString())) {
3381de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                throw new VCardVersionException(
3391de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                        "Incompatible version: " + propertyRawValue + " != " + getVersionString());
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3411de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            handlePropertyValue(property, propertyNameUpper);
34248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else {
34348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throw new VCardException("Unknown property name: \"" + propertyNameUpper + "\"");
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
34548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
34748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private void handleNest() throws IOException, VCardException {
3481de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
3491de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onEntryStarted();
3501de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
35148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        parseItems();
3521de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
3531de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onEntryEnded();
3541de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For performance reason, the states for group and property name are merged into one.
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS = 1;
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS_IN_DQUOTE = 2;
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3631de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected VCardProperty constructPropertyData(String line) throws VCardException {
3641de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final VCardProperty propertyData = new VCardProperty();
36548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = line.length();
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (length > 0 && line.charAt(0) == '#') {
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardInvalidCommentLineException();
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int state = STATE_GROUP_OR_PROPERTY_NAME;
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int nameIndex = 0;
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This loop is developed so that we don't have to take care of bottle neck here.
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Refactor carefully when you need to do so.
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final char ch = line.charAt(i);
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            switch (state) {
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_GROUP_OR_PROPERTY_NAME: {
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == ':') {  // End of a property name.
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String propertyName = line.substring(nameIndex, i);
38248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setName(propertyName);
38348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setRawValue( i < length - 1 ? line.substring(i + 1) : "");
38448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        return propertyData;
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == '.') {  // Each group is followed by the dot.
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String groupName = line.substring(nameIndex, i);
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (groupName.length() == 0) {
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Empty group found. Ignoring.");
3894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
39048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                            propertyData.addGroup(groupName);
3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;  // Next should be another group or a property name.
393677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                    } else if (ch == ';') {  // End of property name and beginneng of parameters.
3944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String propertyName = line.substring(nameIndex, i);
39548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setName(propertyName);
3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;  // Start parameter parsing.
3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
399d5a8fc2a35c69fc34df35fd545ccf83d548ba50cDaisuke Miyakawa                    // TODO: comma support (in vCard 3.0 and 4.0).
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS: {
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS_IN_DQUOTE;
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ';') {  // Starts another param.
41048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        handleParams(propertyData, line.substring(nameIndex, i));
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ':') {  // End of param and beginenning of values.
41348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        handleParams(propertyData, line.substring(nameIndex, i));
41448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setRawValue(i < length - 1 ? line.substring(i + 1) : "");
41548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        return propertyData;
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS_IN_DQUOTE: {
4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;
4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param /
4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws]
4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "="
4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] word / knowntype
4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4421de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParams(VCardProperty propertyData, String params)
44348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] strArray = params.split("=", 2);
4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length == 2) {
4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String paramName = strArray[0].trim().toUpperCase();
4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String paramValue = strArray[1].trim();
4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (paramName.equals("TYPE")) {
44948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleType(propertyData, paramValue);
4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("VALUE")) {
45148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleValue(propertyData, paramValue);
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("ENCODING")) {
45348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleEncoding(propertyData, paramValue);
4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("CHARSET")) {
45548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleCharset(propertyData, paramValue);
4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("LANGUAGE")) {
45748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleLanguage(propertyData, paramValue);
4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.startsWith("X-")) {
45948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleAnyParam(propertyData, paramName, paramValue);
4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Unknown type \"" + paramName + "\"");
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
46448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            handleParamWithoutName(propertyData, strArray[0]);
4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 parser implementation may throw VCardException.
4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4711de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleParamWithoutName(VCardProperty propertyData, final String paramValue) {
47248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        handleType(propertyData, paramValue);
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ptypeval = knowntype / "X-" word
4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleType(VCardProperty propertyData, final String ptypeval) {
4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || ptypeval.startsWith("X-"))
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(ptypeval)) {
4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(ptypeval);
4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        propertyData.addParameter(VCardConstants.PARAM_TYPE, ptypeval);
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleValue(VCardProperty propertyData, final String pvalueval) {
4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || pvalueval.startsWith("X-")
4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || mUnknownValueSet.contains(pvalueval))) {
4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownValueSet.add(pvalueval);
4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format(
4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    "The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        propertyData.addParameter(VCardConstants.PARAM_VALUE, pvalueval);
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleEncoding(VCardProperty propertyData, String pencodingval)
50648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (getAvailableEncodingSet().contains(pencodingval) ||
5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                pencodingval.startsWith("X-")) {
5091de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            propertyData.addParameter(VCardConstants.PARAM_ENCODING, pencodingval);
51048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            // Update encoding right away, as this is needed to understanding other params.
5117a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng            mCurrentEncoding = pencodingval.toUpperCase();
5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * We allow any charset.
5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleCharset(VCardProperty propertyData, String charsetval) {
5251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        mCurrentCharset = charsetval;
5261de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        propertyData.addParameter(VCardConstants.PARAM_CHARSET, charsetval);
5274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * See also Section 7.1 of RFC 1521
5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleLanguage(VCardProperty propertyData, String langval)
53348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] strArray = langval.split("-");
5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length != 2) {
5364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Invalid Language: \"" + langval + "\"");
5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String tmp = strArray[0];
5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int length = tmp.length();
5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
5424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        tmp = strArray[1];
5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        length = tmp.length();
5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5521de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        propertyData.addParameter(VCardConstants.PARAM_LANGUAGE, langval);
5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean isAsciiLetter(char ch) {
5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return false;
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Mainly for "X-" type. This accepts any kind of type without check.
5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
56548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleAnyParam(
5661de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            VCardProperty propertyData, String paramName, String paramValue) {
5671de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        propertyData.addParameter(paramName, paramValue);
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5701de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handlePropertyValue(VCardProperty property, String propertyName)
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
5721de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final String propertyNameUpper = property.getName().toUpperCase();
5731de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        String propertyRawValue = property.getRawValue();
5741de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final String sourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
5751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final Collection<String> charsetCollection =
5761de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                property.getParameters(VCardConstants.PARAM_CHARSET);
5771de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        String targetCharset =
5781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                ((charsetCollection != null) ? charsetCollection.iterator().next() : null);
5791de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (TextUtils.isEmpty(targetCharset)) {
5801de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
5811de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
5821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
5831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        // TODO: have "separableProperty" which reflects vCard spec..
5841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (propertyNameUpper.equals(VCardConstants.PROPERTY_ADR)
5851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                || propertyNameUpper.equals(VCardConstants.PROPERTY_ORG)
5861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                || propertyNameUpper.equals(VCardConstants.PROPERTY_N)) {
5879e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            handleAdrOrgN(property, propertyRawValue, sourceCharset, targetCharset);
5881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            return;
5891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
5901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
5917a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng        if (mCurrentEncoding.equals(VCardConstants.PARAM_ENCODING_QP) ||
5928aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                // If encoding attribute is missing, then attempt to detect QP encoding.
5938aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                // This is to handle a bug where the android exporter was creating FN properties
5948aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                // with missing encoding.  b/7292017
5958aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                (propertyNameUpper.equals(VCardConstants.PROPERTY_FN) &&
5968aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                        property.getParameters(VCardConstants.PARAM_ENCODING) == null &&
5978aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                        VCardUtils.appearsLikeAndroidVCardQuotedPrintable(propertyRawValue))
5988aa2ab8a2f4284cdbc9392e8597ab6d4fd302c50Chiao Cheng                ) {
5991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue);
6001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            final String propertyEncodedValue =
6011de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    VCardUtils.parseQuotedPrintable(quotedPrintablePart,
6021de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                            false, sourceCharset, targetCharset);
6031de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            property.setRawValue(quotedPrintablePart);
6041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            property.setValues(propertyEncodedValue);
6051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            for (VCardInterpreter interpreter : mInterpreterList) {
6061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                interpreter.onPropertyCreated(property);
6071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            }
6087a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng        } else if (mCurrentEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
6097a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng                || mCurrentEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // It is very rare, but some BASE64 data may be so big that
6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // OutOfMemoryError occurs. To ignore such cases, use try-catch.
6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
6132c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                final String base64Property = getBase64(propertyRawValue);
6142c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                try {
6152c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                    property.setByteValue(Base64.decode(base64Property, Base64.DEFAULT));
6162c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                } catch (IllegalArgumentException e) {
6172c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                    throw new VCardException("Decode error on base64 photo: " + propertyRawValue);
6182c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng                }
6191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                for (VCardInterpreter interpreter : mInterpreterList) {
6201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    interpreter.onPropertyCreated(property);
6211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                }
6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (OutOfMemoryError error) {
6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
6241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                for (VCardInterpreter interpreter : mInterpreterList) {
6251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    interpreter.onPropertyCreated(property);
6261de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                }
6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6297a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng            if (!(mCurrentEncoding.equals("7BIT") || mCurrentEncoding.equals("8BIT") ||
6307a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng                    mCurrentEncoding.startsWith("X-"))) {
6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.w(LOG_TAG,
6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        String.format("The encoding \"%s\" is unsupported by vCard %s",
6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                mCurrentEncoding, getVersionString()));
6344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
636f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Some device uses line folding defined in RFC 2425, which is not allowed
637f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // in vCard 2.1 (while needed in vCard 3.0).
638f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
639f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // e.g.
640f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // BEGIN:VCARD
641f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // VERSION:2.1
642f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // N:;Omega;;;
643f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // EMAIL;INTERNET:"Omega"
644f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //   <omega@example.com>
645f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // FN:Omega
646f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // END:VCARD
647f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
648f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // The vCard above assumes that email address should become:
649f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // "Omega" <omega@example.com>
650f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
651f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // But vCard 2.1 requires Quote-Printable when a line contains line break(s).
652f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
653f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // For more information about line folding,
654f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // see "5.8.1. Line delimiting and folding" in RFC 2425.
655f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
656f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // We take care of this case more formally in vCard 3.0, so we only need to
657f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // do this in vCard 2.1.
658be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            if (getVersion() == VCardConfig.VERSION_21) {
659f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                StringBuilder builder = null;
660f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                while (true) {
661f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    final String nextLine = peekLine();
662f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // We don't need to care too much about this exceptional case,
663f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // but we should not wrongly eat up "END:VCARD", since it critically
664f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // breaks this parser's state machine.
665f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // Thus we roughly look over the next line and confirm it is at least not
666f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // "END:VCARD". This extra fee is worth paying. This is exceptional
667f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // anyway.
668f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    if (!TextUtils.isEmpty(nextLine) &&
669f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            nextLine.charAt(0) == ' ' &&
670f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            !"END:VCARD".contains(nextLine.toUpperCase())) {
671f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        getLine();  // Drop the next line.
672f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
673f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        if (builder == null) {
674f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            builder = new StringBuilder();
6751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                            builder.append(propertyRawValue);
676f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        }
677f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        builder.append(nextLine.substring(1));
678f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    } else {
679f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        break;
680f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    }
681f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
682f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                if (builder != null) {
6831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    propertyRawValue = builder.toString();
684f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
685f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
686f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
6871de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            ArrayList<String> propertyValueList = new ArrayList<String>();
6881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            String value = VCardUtils.convertStringCharset(
6891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                    maybeUnescapeText(propertyRawValue), sourceCharset, targetCharset);
6901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            propertyValueList.add(value);
6911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            property.setValues(propertyValueList);
6921de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            for (VCardInterpreter interpreter : mInterpreterList) {
6931de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                interpreter.onPropertyCreated(property);
6941de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            }
6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6989e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    private void handleAdrOrgN(VCardProperty property, String propertyRawValue,
6999e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            String sourceCharset, String targetCharset) throws VCardException, IOException {
7009e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        List<String> encodedValueList = new ArrayList<String>();
7019e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
7029e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some softwares/devices emit
7039e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        // such data.
7047a2ac0405decebde94bb4e3a139d62162e6e15a0Chiao Cheng        if (mCurrentEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
7059e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // First we retrieve Quoted-Printable String from vCard entry, which may include
7069e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // multiple lines.
7079e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue);
7089e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
7099e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // "Raw value" from the view of users should contain all part of QP string.
7109e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // TODO: add test for this handling
7119e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            property.setRawValue(quotedPrintablePart);
7129e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
7139e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // We split Quoted-Printable String using semi-colon before decoding it, as
7149e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // the Quoted-Printable may have semi-colon, which confuses splitter.
7159e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final List<String> quotedPrintableValueList =
7169e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                    VCardUtils.constructListFromValue(quotedPrintablePart, getVersion());
7179e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            for (String quotedPrintableValue : quotedPrintableValueList) {
7189e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                String encoded = VCardUtils.parseQuotedPrintable(quotedPrintableValue,
7199e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                        false, sourceCharset, targetCharset);
7209e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                encodedValueList.add(encoded);
7219e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            }
7229e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        } else {
7239e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final String propertyValue = getPotentialMultiline(propertyRawValue);
7249e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final List<String> rawValueList =
7259e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                    VCardUtils.constructListFromValue(propertyValue, getVersion());
7269e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            for (String rawValue : rawValueList) {
7279e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                encodedValueList.add(VCardUtils.convertStringCharset(
7289e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                        rawValue, sourceCharset, targetCharset));
7299e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            }
7309e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        }
7319e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
7329e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        property.setValues(encodedValueList);
7339e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        for (VCardInterpreter interpreter : mInterpreterList) {
7349e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            interpreter.onPropertyCreated(property);
7359e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        }
7369e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    }
7379e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
7404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Parses and returns Quoted-Printable.
7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param firstString The string following a parameter name and attributes.
7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            Example: "string" in
7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r".
7464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return whole Quoted-Printable string, including a given argument and
7474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         following lines. Excludes the last empty line following to Quoted
7484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         Printable lines.
7494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
7504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
7521de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private String getQuotedPrintablePart(String firstString)
7531de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            throws IOException, VCardException {
7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Specifically, there may be some padding between = and CRLF.
7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // See the following:
7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-line := *(qp-segment transport-padding CRLF)
7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-part transport-padding
7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-segment := qp-section *(SPACE / TAB) "="
7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // ; Maximum length of 76 characters
7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g. (from RFC 2045)
7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Now's the time =
7644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // for all folk to come=
7654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // to the aid of their country.
7664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (firstString.trim().endsWith("=")) {
7674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // remove "transport-padding"
7684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            int pos = firstString.length() - 1;
7694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (firstString.charAt(pos) != '=') {
7704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            StringBuilder builder = new StringBuilder();
7724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(firstString.substring(0, pos + 1));
7734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append("\r\n");
7744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String line;
7754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
7764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
7774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
7784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("File ended during parsing a Quoted-Printable String");
7794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
7804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line.trim().endsWith("=")) {
7814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // remove "transport-padding"
7824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    pos = line.length() - 1;
7834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    while (line.charAt(pos) != '=') {
7844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
7854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line.substring(0, pos + 1));
7864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\r\n");
7874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
7884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line);
7894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
7904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
7914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return builder.toString();
7934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
7944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return firstString;
7954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7989e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    /**
7999e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * Given the first line of a property, checks consecutive lines after it and builds a new
8009e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * multi-line value if it exists.
8019e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     *
8029e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * @param firstString The first line of the property.
8039e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * @return A new property, potentially built from multiple lines.
8049e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * @throws IOException
8059e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     */
8069e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    private String getPotentialMultiline(String firstString) throws IOException {
8079e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        final StringBuilder builder = new StringBuilder();
8089e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        builder.append(firstString);
8099e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8109e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        while (true) {
8119e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final String line = peekLine();
8129e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            if (line == null || line.length() == 0) {
8139e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                break;
8149e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            }
8159e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8169e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final String propertyName = getPropertyNameUpperCase(line);
8179e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            if (propertyName != null) {
8189e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                break;
8199e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            }
8209e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8219e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // vCard 2.1 does not allow multi-line of adr but microsoft vcards may have it.
8229e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // We will consider the next line to be a part of a multi-line value if it does not
8239e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // contain a property name (i.e. a colon or semi-colon).
8249e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // Consume the line.
8259e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            getLine();
8269e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            builder.append(" ").append(line);
8279e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        }
8289e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8299e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        return builder.toString();
8309e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    }
8319e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(String firstString) throws IOException, VCardException {
833c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa        final StringBuilder builder = new StringBuilder();
8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
8354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
837c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            final String line = peekLine();
8384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
8394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
8404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
841c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
842c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't
843a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            // have them. We try to detect those cases using colon and semi-colon, given BASE64
844a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            // does not contain it.
845a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            // E.g.
846a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            //      TEL;TYPE=WORK:+5555555
847a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            // or
848a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng            //      END:VCARD
8499e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            String propertyName = getPropertyNameUpperCase(line);
8509e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            if (getKnownPropertyNameSet().contains(propertyName)) {
8519e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " +
8529e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                        "which must not contain semi-colon or colon. Treat the line as next "
8539e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                        + "property.");
8549e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                Log.w(LOG_TAG, "Problematic line: " + line.trim());
8559e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                break;
856c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            }
857c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
858c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // Consume the line.
859c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            getLine();
860c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
8614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
8654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
8664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
8684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8709e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    /**
8719e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * Extracts the property name portion of a given vCard line.
8729e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * <p>
8739e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * Properties must contain a colon.
8749e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * <p>
8759e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * E.g.
8769e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     *      TEL;TYPE=WORK:+5555555  // returns "TEL"
8779e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     *      END:VCARD // returns "END"
8789e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     *      TEL; // returns null
8799e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     *
8809e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * @param line The vCard line.
8819e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     * @return The property name portion. {@literal null} if no property name found.
8829e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng     */
8839e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    private String getPropertyNameUpperCase(String line) {
8849e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        final int colonIndex = line.indexOf(":");
8859e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        if (colonIndex > -1) {
8869e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final int semiColonIndex = line.indexOf(";");
8879e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
8889e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            // Find the minimum index that is greater than -1.
8899e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            final int minIndex;
8909e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            if (colonIndex == -1) {
8919e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                minIndex = semiColonIndex;
8929e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            } else if (semiColonIndex == -1) {
8939e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                minIndex = colonIndex;
8949e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            } else {
8959e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng                minIndex = Math.min(colonIndex, semiColonIndex);
8969e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            }
8979e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng            return line.substring(0, minIndex).toUpperCase();
8989e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        }
8999e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng        return null;
9009e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng    }
9019e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng
9024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
9034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an
9044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * error toward the AGENT property.
9054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * // TODO: Support AGENT property.
9064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item =
9074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws]
9084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD"
9094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected void handleAgent(final VCardProperty property) throws VCardException {
9111de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (!property.getRawValue().toUpperCase().contains("BEGIN:VCARD")) {
9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
9131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            for (VCardInterpreter interpreter : mInterpreterList) {
9141de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                interpreter.onPropertyCreated(property);
9151de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            }
9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return;
9174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * For vCard 3.0.
9244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
9264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return text;
9274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns unescaped String if the character should be unescaped. Return
9314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";"
9324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * while "\x" should not be.
9334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
9354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeCharacter(ch);
9364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /* package */ static String unescapeCharacter(final char ch) {
9394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Original vCard 2.1 specification does not allow transformation
9404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous
9414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // implementation of
9424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // this class allowed them, so keep it as is.
9434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
9444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
9454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
9474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
951be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_21}
9524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected int getVersion() {
954be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        return VCardConfig.VERSION_21;
9554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
958be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_30}
9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getVersionString() {
9614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardConstants.VERSION_V21;
9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
9654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownPropertyNameSet;
9664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownTypeSet() {
9694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownTypeSet;
9704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownValueSet() {
9734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownValueSet;
9744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getAvailableEncodingSet() {
9774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sAvailableEncoding;
9784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getDefaultEncoding() {
9814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return DEFAULT_ENCODING;
9824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String getDefaultCharset() {
9851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return DEFAULT_CHARSET;
9861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
9874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    protected String getCurrentCharset() {
9891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return mCurrentCharset;
9901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
9911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
9921de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void addInterpreter(VCardInterpreter interpreter) {
9931de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        mInterpreterList.add(interpreter);
9941de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    }
9951de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa
9961de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void parse(InputStream is) throws IOException, VCardException {
9974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (is == null) {
9984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new NullPointerException("InputStream must not be null.");
9994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset);
1002f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        mReader = new CustomBufferedReader(tmpReader);
10034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final long start = System.currentTimeMillis();
10051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
10061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onVCardStarted();
10074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
100856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa
100956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        // vcard_file = [wsls] vcard [wsls]
101056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        while (true) {
101156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            synchronized (this) {
101256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa                if (mCanceled) {
101356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa                    Log.i(LOG_TAG, "Cancel request has come. exitting parse operation.");
101456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa                    break;
101556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa                }
101656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            }
101756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            if (!parseOneVCard()) {
101856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa                break;
101956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            }
102056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        }
102156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa
102256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
102356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            interpreter.onVCardEnded();
102456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        }
102556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa    }
102656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa
102756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa    public void parseOne(InputStream is) throws IOException, VCardException {
102856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        if (is == null) {
102956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            throw new NullPointerException("InputStream must not be null.");
103056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        }
103156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa
103256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset);
103356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        mReader = new CustomBufferedReader(tmpReader);
103456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa
103556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        final long start = System.currentTimeMillis();
103656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
103756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa            interpreter.onVCardStarted();
103856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        }
103956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa        parseOneVCard();
10401de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        for (VCardInterpreter interpreter : mInterpreterList) {
10411de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            interpreter.onVCardEnded();
10424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10451de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public final synchronized void cancel() {
1046b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa        Log.i(LOG_TAG, "ParserImpl received cancel operation.");
10474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCanceled = true;
10484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
1050