VCardParserImpl_V21.java revision 677ef21613a9d35053ec098444832ce4125a847e
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
18f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawaimport android.text.TextUtils;
194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log;
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardAgentNotSupportedException;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidCommentLineException;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidLineException;
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardNestedException;
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;
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
354560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawaimport java.util.List;
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
40677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * Basic implementation achieving vCard parsing. Based on vCard 2.1.
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V21 {
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String LOG_TAG = "VCardParserImpl_V21";
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    private static final class EmptyInterpreter implements VCardInterpreter {
484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void end() {
504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void endEntry() {
534560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
544560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
554560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void endProperty() {
564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyGroup(String group) {
594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
604560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyName(String name) {
624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
634560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
644560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyParamType(String type) {
654560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
664560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
674560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyParamValue(String value) {
684560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
694560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyValues(List<String> values) {
714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void start() {
744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void startEntry() {
774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
794560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void startProperty() {
804560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
824560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
83147f1ae5371954ae845cb2330b221df6ca1d8831Daisuke Miyakawa    protected static final class CustomBufferedReader extends BufferedReader {
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private long mTime;
854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
86f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
87f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Needed since "next line" may be null due to end of line.
88f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
89f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private boolean mNextLineIsValid;
90f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private String mNextLine;
91f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public CustomBufferedReader(Reader in) {
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            super(in);
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        @Override
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public String readLine() throws IOException {
98f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (mNextLineIsValid) {
99f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String ret = mNextLine;
100f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = null;
101f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = false;
102f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                return ret;
103f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
104f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
105677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long start = System.currentTimeMillis();
106f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String line = super.readLine();
107677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long end = System.currentTimeMillis();
1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTime += end - start;
109f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return line;
110f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        }
111f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
112f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
113f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Read one line, but make this object store it in its queue.
114f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
115f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        public String peekLine() throws IOException {
116f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (!mNextLineIsValid) {
117677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long start = System.currentTimeMillis();
118f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String line = super.readLine();
119677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long end = System.currentTimeMillis();
120f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mTime += end - start;
121f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
122f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = line;
123f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = true;
124f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
125f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
126f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return mNextLine;
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public long getTotalmillisecond() {
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mTime;
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String DEFAULT_ENCODING = "8BIT";
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean mCanceled;
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected VCardInterpreter mInterpreter;
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final String mIntermediateCharset;
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The encoding type for deconding byte streams. This member variable is
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * reset to a default encoding every time when a new item comes.
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "Encoding" in vCard is different from "Charset". It is mainly used for
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * addresses, notes, images. "7BIT", "8BIT", "BASE64", and
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "QUOTED-PRINTABLE" are known examples.
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String mCurrentEncoding;
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The reader object to be used internally.
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Developers should not directly read a line from this object. Use
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * getLine() unless there some reason.
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
163f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected CustomBufferedReader mReader;
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * specification, but happens to be seen in real world vCard.
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
170677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
171677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
172677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownTypeSet = new HashSet<String>();
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn VALUE attributes, which is not acceptable in
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard specification, but happens to be seen in real world vCard.
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
181677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
182677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
183677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownValueSet = new HashSet<String>();
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // In some cases, vCard is nested. Currently, we only consider the most
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // interior vCard data.
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // See v21_foma_1.vcf in test directory for more information.
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // TODO: Don't ignore by using count, but read all of information outside vCard.
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private int mNestCount;
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Used only for parsing END:VCARD.
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String mPreviousLine;
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For measuring performance.
1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeTotal;
1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeReadStartRecord;
2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeReadEndRecord;
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeStartProperty;
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeEndProperty;
2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeParseItems;
2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeParseLineAndHandleGroup;
2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeParsePropertyValues;
2064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeParseAdrOrgN;
2074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeHandleMiscPropertyValue;
2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeHandleQuotedPrintable;
2094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private long mTimeHandleBase64;
2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21() {
2124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(VCardConfig.VCARD_TYPE_DEFAULT);
2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21(int vcardType) {
2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) {
2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mNestCount = 1;
2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mIntermediateCharset =  VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Parses the file at the given position.
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
2294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void parseVCardFile() throws IOException, VCardException {
2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean readingFirstFile = true;
2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mCanceled) {
233b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa                Log.i(LOG_TAG, "Cancel request has come. exitting parse operation.");
2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!parseOneVCard(readingFirstFile)) {
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            readingFirstFile = false;
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mNestCount > 0) {
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            boolean useCache = true;
2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < mNestCount; i++) {
2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                readEndVCard(useCache, true);
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                useCache = false;
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return true when a given property name is a valid property name.
2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean isValidPropertyName(final String propertyName) {
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) ||
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                propertyName.startsWith("X-"))
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(propertyName)) {
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(propertyName);
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
2624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String. It may be null, or its length may be 0
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getLine() throws IOException {
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mReader.readLine();
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
272f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected String peekLine() throws IOException {
273f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        return mReader.peekLine();
274f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    }
275f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String with it's length > 0
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException when the stream reached end of line
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getNonEmptyLine() throws IOException, VCardException {
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            line = getLine();
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Reached end of buffer.");
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.trim().length() > 0) {
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return line;
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         items *CRLF
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         "END" [ws] ":" [ws] "VCARD"
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException {
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean allowGarbage = false;
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (firstRead) {
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mNestCount > 0) {
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                for (int i = 0; i < mNestCount; i++) {
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (!readBeginVCard(allowGarbage)) {
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        return false;
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    allowGarbage = true;
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!readBeginVCard(allowGarbage)) {
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3144560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final long beforeStartEntry = System.currentTimeMillis();
3154560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.startEntry();
3164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mTimeReadStartRecord += System.currentTimeMillis() - beforeStartEntry;
3174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
3184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final long beforeParseItems = System.currentTimeMillis();
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        parseItems();
3204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mTimeParseItems += System.currentTimeMillis() - beforeParseItems;
3214560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        readEndVCard(true, false);
3234560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
3244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final long beforeEndEntry = System.currentTimeMillis();
3254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.endEntry();
3264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mTimeReadEndRecord += System.currentTimeMillis() - beforeEndEntry;
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return True when successful. False when reaching the end of line
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        do {
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (line.trim().length() > 0) {
3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
346f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String[] strArray = line.split(":", 2);
347f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final int length = strArray.length;
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
349f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Although vCard 2.1/3.0 specification does not allow lower cases,
350f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // we found vCard file emitted by some external vCard expoter have such
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // invalid Strings.
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // So we allow it.
353f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // e.g.
354f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // BEGIN:vCard
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return true;
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!allowGarbage) {
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (mNestCount > 0) {
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    mPreviousLine = line;
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            + "(Instead, \"" + line + "\" came)");
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } while (allowGarbage);
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardException("Reached where must not be reached.");
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The arguments useCache and allowGarbase are usually true and false
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * accordingly when this function is called outside this function itself.
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
377677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     *
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param useCache When true, line is obtained from mPreviousline.
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            Otherwise, getLine() is used.
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param allowGarbage When true, ignore non "END:VCARD" line.
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException,
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            VCardException {
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        do {
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (useCache) {
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // Though vCard specification does not allow lower cases,
3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // some data may have them, so we allow it.
3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = mPreviousLine;
3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                while (true) {
3944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    line = getLine();
3954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (line == null) {
3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        throw new VCardException("Expected END:VCARD was not found.");
3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (line.trim().length() > 0) {
3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        break;
3994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String[] strArray = line.split(":", 2);
4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END")
4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return;
4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!allowGarbage) {
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            useCache = false;
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } while (allowGarbage);
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * items = *CRLF item / item
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void parseItems() throws IOException, VCardException {
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean ended = false;
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4204560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final long beforeBeginProperty = System.currentTimeMillis();
4214560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.startProperty();
4224560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mTimeStartProperty += System.currentTimeMillis() - beforeBeginProperty;
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        ended = parseItem();
4244560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        if (!ended) {
4254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final long beforeEndProperty = System.currentTimeMillis();
4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mInterpreter.endProperty();
4274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (!ended) {
4314560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final long beforeStartProperty = System.currentTimeMillis();
4324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.startProperty();
4334560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mTimeStartProperty += System.currentTimeMillis() - beforeStartProperty;
4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                ended = parseItem();
4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (VCardInvalidCommentLineException e) {
4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                ended = false;
4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4404560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
4414560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            if (!ended) {
4424560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                final long beforeEndProperty = System.currentTimeMillis();
4434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                mInterpreter.endProperty();
4444560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR"
4514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."]
4534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "AGENT" [params] ":" vcard CRLF
4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean parseItem() throws IOException, VCardException {
4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCurrentEncoding = DEFAULT_ENCODING;
4574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String line = getNonEmptyLine();
4594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        long start = System.currentTimeMillis();
4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] propertyNameAndValue = separateLineAndHandleGroup(line);
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (propertyNameAndValue == null) {
4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (propertyNameAndValue.length != 2) {
4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardInvalidLineException("Invalid line \"" + line + "\"");
4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String propertyName = propertyNameAndValue[0].toUpperCase();
4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String propertyValue = propertyNameAndValue[1];
4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
4724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) {
4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            start = System.currentTimeMillis();
4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handleMultiplePropertyValue(propertyName, propertyValue);
4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTimeParseAdrOrgN += System.currentTimeMillis() - start;
4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (propertyName.equals("AGENT")) {
4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handleAgent(propertyValue);
4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (isValidPropertyName(propertyName)) {
4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (propertyName.equals("BEGIN")) {
4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (propertyValue.equals("VCARD")) {
4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardNestedException("This vCard has nested vCard data in it.");
4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("Unknown BEGIN type: " + propertyValue);
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersionString())) {
4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardVersionException("Incompatible version: " + propertyValue + " != "
4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        + getVersionString());
4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            start = System.currentTimeMillis();
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handlePropertyValue(propertyName, propertyValue);
4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTimeParsePropertyValues += System.currentTimeMillis() - start;
4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardException("Unknown property name: \"" + propertyName + "\"");
4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For performance reason, the states for group and property name are merged into one.
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS = 1;
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS_IN_DQUOTE = 2;
5064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String[] separateLineAndHandleGroup(String line) throws VCardException {
5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] propertyNameAndValue = new String[2];
5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = line.length();
5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (length > 0 && line.charAt(0) == '#') {
5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardInvalidCommentLineException();
5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int state = STATE_GROUP_OR_PROPERTY_NAME;
5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int nameIndex = 0;
5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // This loop is developed so that we don't have to take care of bottle neck here.
5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Refactor carefully when you need to do so.
5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final char ch = line.charAt(i);
5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            switch (state) {
5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_GROUP_OR_PROPERTY_NAME: {
5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == ':') {  // End of a property name.
5244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String propertyName = line.substring(nameIndex, i);
5254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (propertyName.equalsIgnoreCase("END")) {
5264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            mPreviousLine = line;
5274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            return null;
5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        mInterpreter.propertyName(propertyName);
5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        propertyNameAndValue[0] = propertyName;
5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (i < length - 1) {
5324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            propertyNameAndValue[1] = line.substring(i + 1);
5334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        } else {
5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            propertyNameAndValue[1] = "";
5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        return propertyNameAndValue;
5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == '.') {  // Each group is followed by the dot.
5384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String groupName = line.substring(nameIndex, i);
5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (groupName.length() == 0) {
5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Empty group found. Ignoring.");
5414560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
5424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            mInterpreter.propertyGroup(groupName);
5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;  // Next should be another group or a property name.
545677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                    } else if (ch == ';') {  // End of property name and beginneng of parameters.
5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String propertyName = line.substring(nameIndex, i);
5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (propertyName.equalsIgnoreCase("END")) {
5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            mPreviousLine = line;
5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            return null;
5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        mInterpreter.propertyName(propertyName);
5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        propertyNameAndValue[0] = propertyName;
5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;  // Start parameter parsing.
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
556d5a8fc2a35c69fc34df35fd545ccf83d548ba50cDaisuke Miyakawa                    // TODO: comma support (in vCard 3.0 and 4.0).
5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS: {
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS_IN_DQUOTE;
5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ';') {  // Starts another param.
5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        handleParams(line.substring(nameIndex, i));
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ':') {  // End of param and beginenning of values.
5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        handleParams(line.substring(nameIndex, i));
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (i < length - 1) {
5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            propertyNameAndValue[1] = line.substring(i + 1);
5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        } else {
5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            propertyNameAndValue[1] = "";
5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        return propertyNameAndValue;
5774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS_IN_DQUOTE: {
5814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
5824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
5834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
5844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
5854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;
5874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
5944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
5974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param /
5984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws]
5994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "="
6004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
6014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] word / knowntype
6024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleParams(String params) throws VCardException {
6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] strArray = params.split("=", 2);
6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length == 2) {
6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String paramName = strArray[0].trim().toUpperCase();
6074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String paramValue = strArray[1].trim();
6084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (paramName.equals("TYPE")) {
6094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleType(paramValue);
6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("VALUE")) {
6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleValue(paramValue);
6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("ENCODING")) {
6134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleEncoding(paramValue);
6144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("CHARSET")) {
6154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleCharset(paramValue);
6164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("LANGUAGE")) {
6174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleLanguage(paramValue);
6184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.startsWith("X-")) {
6194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                handleAnyParam(paramName, paramValue);
6204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
6214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Unknown type \"" + paramName + "\"");
6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handleParamWithoutName(strArray[0]);
6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 parser implementation may throw VCardException.
6304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @SuppressWarnings("unused")
6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleParamWithoutName(final String paramValue) throws VCardException {
6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        handleType(paramValue);
6344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ptypeval = knowntype / "X-" word
6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleType(final String ptypeval) {
6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || ptypeval.startsWith("X-"))
6424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(ptypeval)) {
6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(ptypeval);
6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType("TYPE");
6474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamValue(ptypeval);
6484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleValue(final String pvalueval) {
6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || pvalueval.startsWith("X-")
6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || mUnknownValueSet.contains(pvalueval))) {
6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownValueSet.add(pvalueval);
6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format(
6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    "The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType("VALUE");
6624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamValue(pvalueval);
6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
6674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleEncoding(String pencodingval) throws VCardException {
6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (getAvailableEncodingSet().contains(pencodingval) ||
6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                pencodingval.startsWith("X-")) {
6714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyParamType("ENCODING");
6724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyParamValue(pencodingval);
6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mCurrentEncoding = pencodingval;
6744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * We allow any charset.
6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
6854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleCharset(String charsetval) {
6874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType("CHARSET");
6884560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamValue(charsetval);
6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * See also Section 7.1 of RFC 1521
6934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleLanguage(String langval) throws VCardException {
6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] strArray = langval.split("-");
6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length != 2) {
6974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Invalid Language: \"" + langval + "\"");
6984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String tmp = strArray[0];
7004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int length = tmp.length();
7014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
7024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
7034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
7044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        tmp = strArray[1];
7074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        length = tmp.length();
7084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
7094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
7104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
7114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7134560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType(VCardConstants.PARAM_LANGUAGE);
7144560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamValue(langval);
7154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean isAsciiLetter(char ch) {
7184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
7194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
7204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return false;
7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
7254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Mainly for "X-" type. This accepts any kind of type without check.
7264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
7274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAnyParam(String paramName, String paramValue) {
7284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamType(paramName);
7294560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyParamValue(paramValue);
7304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handlePropertyValue(String propertyName, String propertyValue)
7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String upperEncoding = mCurrentEncoding.toUpperCase();
7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final long start = System.currentTimeMillis();
7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String result = getQuotedPrintable(propertyValue);
7384560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final ArrayList<String> v = new ArrayList<String>();
7394560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            v.add(result);
7404560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyValues(v);
7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final long start = System.currentTimeMillis();
7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // It is very rare, but some BASE64 data may be so big that
7464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // OutOfMemoryError occurs. To ignore such cases, use try-catch.
7474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
7484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                final ArrayList<String> arrayList = new ArrayList<String>();
7494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                arrayList.add(getBase64(propertyValue));
7504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mInterpreter.propertyValues(arrayList);
7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (OutOfMemoryError error) {
7524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
7534560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mInterpreter.propertyValues(null);
7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTimeHandleBase64 += System.currentTimeMillis() - start;
7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    upperEncoding.startsWith("X-"))) {
7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.w(LOG_TAG,
7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        String.format("The encoding \"%s\" is unsupported by vCard %s",
7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                mCurrentEncoding, getVersionString()));
7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
764f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Some device uses line folding defined in RFC 2425, which is not allowed
765f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // in vCard 2.1 (while needed in vCard 3.0).
766f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
767f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // e.g.
768f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // BEGIN:VCARD
769f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // VERSION:2.1
770f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // N:;Omega;;;
771f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // EMAIL;INTERNET:"Omega"
772f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //   <omega@example.com>
773f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // FN:Omega
774f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // END:VCARD
775f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
776f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // The vCard above assumes that email address should become:
777f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // "Omega" <omega@example.com>
778f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
779f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // But vCard 2.1 requires Quote-Printable when a line contains line break(s).
780f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
781f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // For more information about line folding,
782f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // see "5.8.1. Line delimiting and folding" in RFC 2425.
783f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
784f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // We take care of this case more formally in vCard 3.0, so we only need to
785f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // do this in vCard 2.1.
786be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            if (getVersion() == VCardConfig.VERSION_21) {
787f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                StringBuilder builder = null;
788f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                while (true) {
789f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    final String nextLine = peekLine();
790f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // We don't need to care too much about this exceptional case,
791f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // but we should not wrongly eat up "END:VCARD", since it critically
792f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // breaks this parser's state machine.
793f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // Thus we roughly look over the next line and confirm it is at least not
794f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // "END:VCARD". This extra fee is worth paying. This is exceptional
795f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // anyway.
796f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    if (!TextUtils.isEmpty(nextLine) &&
797f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            nextLine.charAt(0) == ' ' &&
798f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            !"END:VCARD".contains(nextLine.toUpperCase())) {
799f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        getLine();  // Drop the next line.
800f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
801f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        if (builder == null) {
802f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            builder = new StringBuilder();
803f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            builder.append(propertyValue);
804f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        }
805f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        builder.append(nextLine.substring(1));
806f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    } else {
807f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        break;
808f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    }
809f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
810f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                if (builder != null) {
811f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    propertyValue = builder.toString();
812f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
813f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
814f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
8154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final long start = System.currentTimeMillis();
8164560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            ArrayList<String> v = new ArrayList<String>();
8174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            v.add(maybeUnescapeText(propertyValue));
8184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyValues(v);
8194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
8204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
8214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
8244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
8254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Parses and returns Quoted-Printable.
8264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
8274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
8284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param firstString The string following a parameter name and attributes.
8294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            Example: "string" in
8304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r".
8314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return whole Quoted-Printable string, including a given argument and
8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         following lines. Excludes the last empty line following to Quoted
8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         Printable lines.
8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
8354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
8364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
8374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String getQuotedPrintable(String firstString) throws IOException, VCardException {
8384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Specifically, there may be some padding between = and CRLF.
8394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // See the following:
8404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
8414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-line := *(qp-segment transport-padding CRLF)
8424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-part transport-padding
8434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-segment := qp-section *(SPACE / TAB) "="
8444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // ; Maximum length of 76 characters
8454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
8464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g. (from RFC 2045)
8474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Now's the time =
8484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // for all folk to come=
8494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // to the aid of their country.
8504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (firstString.trim().endsWith("=")) {
8514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // remove "transport-padding"
8524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            int pos = firstString.length() - 1;
8534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (firstString.charAt(pos) != '=') {
8544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            StringBuilder builder = new StringBuilder();
8564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(firstString.substring(0, pos + 1));
8574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append("\r\n");
8584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String line;
8594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
8604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
8614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("File ended during parsing a Quoted-Printable String");
8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
8644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line.trim().endsWith("=")) {
8654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // remove "transport-padding"
8664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    pos = line.length() - 1;
8674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    while (line.charAt(pos) != '=') {
8684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
8694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line.substring(0, pos + 1));
8704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\r\n");
8714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
8724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line);
8734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
8744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
8754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return builder.toString();
8774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
8784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return firstString;
8794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
8804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(String firstString) throws IOException, VCardException {
883c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa        final StringBuilder builder = new StringBuilder();
8844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
8854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
887c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            final String line = peekLine();
8884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
8894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
8904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
891c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
892c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't
893c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // have them. We try to detect those cases using semi-colon, given BASE64 doesn't
894c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // contain it. Specifically BASE64 doesn't have semi-colon in it, so we should be able
895c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // to detect the case safely.
896c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            if (line.contains(":")) {
897c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                if (getKnownPropertyNameSet().contains(
898c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                        line.substring(0, line.indexOf(":")).toUpperCase())) {
899c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " +
900c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                            "which must not contain semi-colon. Treat the line as next property.");
901c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    Log.w(LOG_TAG, "Problematic line: " + line.trim());
902c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    break;
903c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                }
904c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            }
905c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
906c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // Consume the line.
907c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            getLine();
908c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
9094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
9104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
9114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
9134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
9204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Mainly for "ADR", "ORG", and "N"
9214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
9244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr,
9254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Street, Locality, Region, Postal Code, Country Name orgparts =
9264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are
9274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family,
9284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III,
9294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a
9304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * semicolon in this string, it must be escaped ; with a "\" character. We
9314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * do not care the number of "strnosemi" here. We are not sure whether we
9324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * should add "\" CRLF to each value. We exclude them for now.
9334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
9354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
9364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some
9374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // softwares/devices
9384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // emit such data.
9394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
9404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            propertyValue = getQuotedPrintable(propertyValue);
9414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9434560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue,
9444560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                getVersion()));
9454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
9484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an
9494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * error toward the AGENT property.
9504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * // TODO: Support AGENT property.
9514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item =
9524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws]
9534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD"
9544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAgent(final String propertyValue) throws VCardException {
9564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) {
9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
9584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return;
9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
9614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * For vCard 3.0.
9664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
9684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return text;
9694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns unescaped String if the character should be unescaped. Return
9734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";"
9744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * while "\x" should not be.
9754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
9774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeCharacter(ch);
9784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /* package */ static String unescapeCharacter(final char ch) {
9814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Original vCard 2.1 specification does not allow transformation
9824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous
9834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // implementation of
9844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // this class allowed them, so keep it as is.
9854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
9864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
9874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
9894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private void showPerformanceInfo() {
9934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Total parsing time:  " + mTimeTotal + " ms");
994f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        Log.d(LOG_TAG, "Total readLine time: " + mReader.getTotalmillisecond() + " ms");
9954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord
9964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                + " ms");
9974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms");
9984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup
9994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                + " ms");
10004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
10014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
10024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue
10034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                + " ms");
10044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms");
10054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
10064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1009be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_21}
10104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
10114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected int getVersion() {
1012be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        return VCardConfig.VERSION_21;
10134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1016be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_30}
10174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
10184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getVersionString() {
10194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardConstants.VERSION_V21;
10204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
10234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownPropertyNameSet;
10244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownTypeSet() {
10274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownTypeSet;
10284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownValueSet() {
10314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownValueSet;
10324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getAvailableEncodingSet() {
10354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sAvailableEncoding;
10364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getDefaultEncoding() {
10394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return DEFAULT_ENCODING;
10404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public void parse(InputStream is, VCardInterpreter interpreter)
10444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
10454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (is == null) {
10464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new NullPointerException("InputStream must not be null.");
10474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset);
1050f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        mReader = new CustomBufferedReader(tmpReader);
10514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter = (interpreter != null ? interpreter : new EmptyInterpreter());
10534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final long start = System.currentTimeMillis();
10554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mInterpreter != null) {
10564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mInterpreter.start();
10574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        parseVCardFile();
10594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mInterpreter != null) {
10604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mInterpreter.end();
10614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mTimeTotal += System.currentTimeMillis() - start;
10634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (VCardConfig.showPerformanceLog()) {
10654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            showPerformanceInfo();
10664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public final void cancel() {
1070b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa        Log.i(LOG_TAG, "ParserImpl received cancel operation.");
10714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCanceled = true;
10724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
1074