VCardParserImpl_V21.java revision 48dd8e86a81d2ab40eb762975c8211c225002bf0
14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/*
24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2010 The Android Open Source Project
34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License.
64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at
74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and
144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License.
154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard;
174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardAgentNotSupportedException;
194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidCommentLineException;
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidLineException;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardVersionException;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawaimport android.text.TextUtils;
2548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawaimport android.util.Log;
2648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.BufferedReader;
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException;
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStream;
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStreamReader;
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.Reader;
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList;
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawaimport java.util.List;
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
39677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * Basic implementation achieving vCard parsing. Based on vCard 2.1.
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V21 {
4402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    private static final String LOG_TAG = VCardConstants.LOG_TAG;
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    private static final class EmptyInterpreter implements VCardInterpreter {
474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void end() {
494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void endEntry() {
524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
534560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
544560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void endProperty() {
554560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyGroup(String group) {
584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
604560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyName(String name) {
614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
634560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyParamType(String type) {
644560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
654560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
664560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyParamValue(String value) {
674560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
684560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
694560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void propertyValues(List<String> values) {
704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void start() {
734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void startEntry() {
764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        @Override
784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        public void startProperty() {
794560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        }
804560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    }
814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
82147f1ae5371954ae845cb2330b221df6ca1d8831Daisuke Miyakawa    protected static final class CustomBufferedReader extends BufferedReader {
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        private long mTime;
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
85f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
86f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Needed since "next line" may be null due to end of line.
87f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
88f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private boolean mNextLineIsValid;
89f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        private String mNextLine;
90f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public CustomBufferedReader(Reader in) {
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            super(in);
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        @Override
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public String readLine() throws IOException {
97f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (mNextLineIsValid) {
98f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String ret = mNextLine;
99f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = null;
100f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = false;
101f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                return ret;
102f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
103f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
104677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long start = System.currentTimeMillis();
105f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String line = super.readLine();
106677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa            final long end = System.currentTimeMillis();
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mTime += end - start;
108f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return line;
109f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        }
110f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
111f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        /**
112f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         * Read one line, but make this object store it in its queue.
113f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa         */
114f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        public String peekLine() throws IOException {
115f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            if (!mNextLineIsValid) {
116677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long start = System.currentTimeMillis();
117f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                final String line = super.readLine();
118677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                final long end = System.currentTimeMillis();
119f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mTime += end - start;
120f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
121f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLine = line;
122f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                mNextLineIsValid = true;
123f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
124f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
125f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            return mNextLine;
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        public long getTotalmillisecond() {
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mTime;
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
13348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    /**
13448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Stores param information for a property. Used with {@link PropertyData}.
13548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     *
13648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * e.g. "LANGUAGE=jp" -> paramName = "LANGUAGE", paramvalue = "jp"
13748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     */
13848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private class ParamPair {
13948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public String paramName;
14048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public String paramValue;
14148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public ParamPair(String paramName, String paramValue) {
14248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            this.paramName = paramName;
14348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            this.paramValue = paramValue;
14448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
14548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        @Override
14648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public String toString() {
14748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            return paramName + ", " + paramValue;
14848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
14948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
15048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
15148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    /**
15248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Intermediate data for storing property.
15348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     *
15448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Name, group and param are property encoded when this object is prepared.
15548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Value isn't encoded yet at this point.
15648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     */
15748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected class PropertyData {
15848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        private String mName;
15948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        private List<String> mGroupList;
16048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        private List<ParamPair> mParamList;
16148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        private String mRawValue;
16248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
16348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public void setName(String name) throws VCardException {
16448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (mName != null) {
16548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException(
16648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        String.format("Property name is re-defined " +
16748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                                "(existing: %s, requested: %s", mName, name));
16848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
16948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mName = name;
17048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
17148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
17248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public void addGroup(String group) {
17348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (mGroupList == null) {
17448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                mGroupList = new ArrayList<String>();
17548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
17648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mGroupList.add(group);
17748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
17848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
17948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public void addParam(String paramName, String paramValue) {
18048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (mParamList == null) {
18148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                mParamList = new ArrayList<ParamPair>();
18248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
18348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mParamList.add(new ParamPair(paramName, paramValue));
18448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
18548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
18648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public void setRawValue(String rawValue) throws VCardException {
18748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (mRawValue != null) {
18848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException(
18948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        String.format("Property value is re-defined " +
19048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                                "(existing: %s, requested: %s", mRawValue, rawValue));
19148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
19248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mRawValue = rawValue;
19348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
19448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
19548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public String getName() {
19648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            return mName;
19748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
19848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
19948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public List<String> getGroupList() {
20048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            return mGroupList;
20148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
20248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
20348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public List<ParamPair> getParamList() {
20448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            return mParamList;
20548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
20648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
20748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        public String getRawValue() {
20848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            return mRawValue;
20948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
21048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     }
21148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
2124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String DEFAULT_ENCODING = "8BIT";
2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
21448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    // TODO: remove this.
2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean mCanceled;
2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final String mIntermediateCharset;
2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
21848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private VCardInterpreter mInterpreter;
21948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The encoding type for deconding byte streams. This member variable is
2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * reset to a default encoding every time when a new item comes.
2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "Encoding" in vCard is different from "Charset". It is mainly used for
2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * addresses, notes, images. "7BIT", "8BIT", "BASE64", and
2284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "QUOTED-PRINTABLE" are known examples.
2294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String mCurrentEncoding;
2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The reader object to be used internally.
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Developers should not directly read a line from this object. Use
2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * getLine() unless there some reason.
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
242f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected CustomBufferedReader mReader;
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * specification, but happens to be seen in real world vCard.
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
249677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
250677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
251677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownTypeSet = new HashSet<String>();
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Set for storing unkonwn VALUE attributes, which is not acceptable in
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard specification, but happens to be seen in real world vCard.
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
260677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * <p>
261677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * We just accept those invalid types after emitting a warning for each of it.
262677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa     * </p>
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected final Set<String> mUnknownValueSet = new HashSet<String>();
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21() {
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        this(VCardConfig.VCARD_TYPE_DEFAULT);
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public VCardParserImpl_V21(int vcardType) {
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mIntermediateCharset =  VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Parses the file at the given position.
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void parseVCardFile() throws IOException, VCardException {
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (mCanceled) {
284b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa                Log.i(LOG_TAG, "Cancel request has come. exitting parse operation.");
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
28748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (!parseOneVCard()) {
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return true when a given property name is a valid property name.
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean isValidPropertyName(final String propertyName) {
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) ||
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                propertyName.startsWith("X-"))
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(propertyName)) {
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(propertyName);
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String. It may be null, or its length may be 0
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getLine() throws IOException {
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return mReader.readLine();
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
314f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    protected String peekLine() throws IOException {
315f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        return mReader.peekLine();
316f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa    }
317f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return String with it's length > 0
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException when the stream reached end of line
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getNonEmptyLine() throws IOException, VCardException {
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            line = getLine();
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Reached end of buffer.");
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (line.trim().length() > 0) {
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return line;
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
33548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    /**
33648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * <code>
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         items *CRLF
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         "END" [ws] ":" [ws] "VCARD"
34048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * </code>
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
34248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private boolean parseOneVCard() throws IOException, VCardException {
34348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // reset for this entire vCard.
34448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        mCurrentEncoding = DEFAULT_ENCODING;
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
34648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        boolean allowGarbage = false;
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!readBeginVCard(allowGarbage)) {
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return false;
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.startEntry();
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        parseItems();
3524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.endEntry();
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return True when successful. False when reaching the end of line
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
36248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // TODO: use consructPropertyLine().
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String line;
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        do {
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (line.trim().length() > 0) {
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
373f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final String[] strArray = line.split(":", 2);
374f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            final int length = strArray.length;
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
376f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Although vCard 2.1/3.0 specification does not allow lower cases,
377f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // we found vCard file emitted by some external vCard expoter have such
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // invalid Strings.
37948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            // e.g. BEGIN:vCard
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return true;
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (!allowGarbage) {
38448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
38548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        + "(Instead, \"" + line + "\" came)");
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } while (allowGarbage);
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardException("Reached where must not be reached.");
3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
39348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Parses lines other than the first "BEGIN:VCARD". Takes care of "END:VCARD"n and
39448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * "BEGIN:VCARD" in nested vCard.
3954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * items = *CRLF item / item
39848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     *
39948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa     * Note: BEGIN/END aren't include in the original spec while this method handles them.
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void parseItems() throws IOException, VCardException {
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean ended = false;
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
40448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        try {
40548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            ended = parseItem();
40648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } catch (VCardInvalidCommentLineException e) {
40748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (!ended) {
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                ended = parseItem();
4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (VCardInvalidCommentLineException e) {
4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR"
4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts
4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."]
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * "AGENT" [params] ":" vcard CRLF
4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected boolean parseItem() throws IOException, VCardException {
42648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        // Reset for an item.
4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCurrentEncoding = DEFAULT_ENCODING;
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String line = getNonEmptyLine();
43048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final PropertyData propertyData = constructPropertyData(line);
4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
43248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final String propertyNameUpper = propertyData.getName().toUpperCase();
43348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final String propertyValue = propertyData.getRawValue();
43448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
43548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        if (propertyNameUpper.equals(VCardConstants.PROPERTY_BEGIN)) {
43648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (propertyValue.equalsIgnoreCase("VCARD")) {
43748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleNest();
43848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            } else {
43948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException("Unknown BEGIN type: " + propertyValue);
44048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
44148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_END)) {
44248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (propertyValue.equalsIgnoreCase("VCARD")) {
44348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                return true;  // Ended.
44448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            } else {
44548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                throw new VCardException("Unknown END type: " + propertyValue);
44648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
44748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else {
44848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mInterpreter.startProperty();
44948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            sendPropertyLineMetaInfo(propertyData);
45048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            parseItemInter(propertyNameUpper, propertyValue);
45148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            mInterpreter.endProperty();
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
45348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        return false;
45448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
45548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
45648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private void sendPropertyLineMetaInfo(PropertyData propertyData) {
45748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final List<String> groupList = propertyData.getGroupList();
45848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        if (groupList != null) {
45948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            for (String group : groupList) {
46048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                mInterpreter.propertyGroup(group);
46148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
46348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        mInterpreter.propertyName(propertyData.getName());
4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
46548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final List<ParamPair> paramList = propertyData.getParamList();
46648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        if (paramList != null) {
46748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            for (ParamPair pair : paramList) {
46848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                mInterpreter.propertyParamType(pair.paramName);
46948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                mInterpreter.propertyParamValue(pair.paramValue);
47048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            }
47148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        }
47248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
47448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private void parseItemInter(String propertyNameUpper, String propertyValue)
47548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws IOException, VCardException {
47648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        if (propertyNameUpper.equals(VCardConstants.PROPERTY_ADR)
47748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                || propertyNameUpper.equals(VCardConstants.PROPERTY_ORG)
47848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                || propertyNameUpper.equals(VCardConstants.PROPERTY_N)) {
47948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            handleMultiplePropertyValue(propertyNameUpper, propertyValue);
48048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_AGENT)) {
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            handleAgent(propertyValue);
48248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else if (isValidPropertyName(propertyNameUpper)) {
48348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            if (propertyNameUpper.equals(VCardConstants.PROPERTY_VERSION) &&
48402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa                    !propertyValue.equals(getVersionString())) {
4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardVersionException("Incompatible version: " + propertyValue + " != "
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        + getVersionString());
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
48848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            handlePropertyValue(propertyNameUpper, propertyValue);
48948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        } else {
49048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throw new VCardException("Unknown property name: \"" + propertyNameUpper + "\"");
4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
49248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    }
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
49448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private void handleNest() throws IOException, VCardException {
49548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        mInterpreter.startEntry();
49648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        parseItems();
49748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        mInterpreter.endEntry();
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For performance reason, the states for group and property name are merged into one.
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS = 1;
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static private final int STATE_PARAMS_IN_DQUOTE = 2;
5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
50648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected PropertyData constructPropertyData(String line) throws VCardException {
50748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        final PropertyData propertyData = new PropertyData();
50848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa
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);
52548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setName(propertyName);
52648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setRawValue( i < length - 1 ? line.substring(i + 1) : "");
52748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        return propertyData;
5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == '.') {  // Each group is followed by the dot.
5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String groupName = line.substring(nameIndex, i);
5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (groupName.length() == 0) {
5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Empty group found. Ignoring.");
5324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        } else {
53348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                            propertyData.addGroup(groupName);
5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;  // Next should be another group or a property name.
536677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa                    } else if (ch == ';') {  // End of property name and beginneng of parameters.
5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final String propertyName = line.substring(nameIndex, i);
53848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setName(propertyName);
5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;  // Start parameter parsing.
5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
542d5a8fc2a35c69fc34df35fd545ccf83d548ba50cDaisuke Miyakawa                    // TODO: comma support (in vCard 3.0 and 4.0).
5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS: {
5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS_IN_DQUOTE;
5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ';') {  // Starts another param.
55348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        handleParams(propertyData, line.substring(nameIndex, i));
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        nameIndex = i + 1;
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (ch == ':') {  // End of param and beginenning of values.
55648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        handleParams(propertyData, line.substring(nameIndex, i));
55748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        propertyData.setRawValue(i < length - 1 ? line.substring(i + 1) : "");
55848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                        return propertyData;
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                case STATE_PARAMS_IN_DQUOTE: {
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (ch == '"') {
5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                    "Silently allow it");
5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        state = STATE_PARAMS;
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
5794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param /
5804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws]
5814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "="
5824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
5834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * [ws] word / knowntype
5844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
58548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleParams(PropertyData propertyData, String params)
58648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
5874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] strArray = params.split("=", 2);
5884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length == 2) {
5894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String paramName = strArray[0].trim().toUpperCase();
5904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String paramValue = strArray[1].trim();
5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (paramName.equals("TYPE")) {
59248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleType(propertyData, paramValue);
5934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("VALUE")) {
59448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleValue(propertyData, paramValue);
5954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("ENCODING")) {
59648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleEncoding(propertyData, paramValue);
5974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("CHARSET")) {
59848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleCharset(propertyData, paramValue);
5994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.equals("LANGUAGE")) {
60048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleLanguage(propertyData, paramValue);
6014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (paramName.startsWith("X-")) {
60248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                handleAnyParam(propertyData, paramName, paramValue);
6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Unknown type \"" + paramName + "\"");
6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
60748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            handleParamWithoutName(propertyData, strArray[0]);
6084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 3.0 parser implementation may throw VCardException.
6134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
61448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleParamWithoutName(PropertyData propertyData, final String paramValue) {
61548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        handleType(propertyData, paramValue);
6164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ptypeval = knowntype / "X-" word
6204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
62148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleType(PropertyData propertyData, final String ptypeval) {
6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || ptypeval.startsWith("X-"))
6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && !mUnknownTypeSet.contains(ptypeval)) {
6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownTypeSet.add(ptypeval);
6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
62848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        propertyData.addParam(VCardConstants.PARAM_TYPE, ptypeval);
6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
63448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleValue(PropertyData propertyData, final String pvalueval) {
6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || pvalueval.startsWith("X-")
6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || mUnknownValueSet.contains(pvalueval))) {
6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mUnknownValueSet.add(pvalueval);
6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, String.format(
6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    "The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
64248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        propertyData.addParam(VCardConstants.PARAM_VALUE, pvalueval);
6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
64848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleEncoding(PropertyData propertyData, String pencodingval)
64948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (getAvailableEncodingSet().contains(pencodingval) ||
6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                pencodingval.startsWith("X-")) {
65248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            propertyData.addParam(VCardConstants.PARAM_ENCODING, pencodingval);
65348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            // Update encoding right away, as this is needed to understanding other params.
6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mCurrentEncoding = pencodingval;
6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
6624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * We allow any charset.
6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
66748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleCharset(PropertyData propertyData, String charsetval) {
66848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        propertyData.addParam(VCardConstants.PARAM_CHARSET, charsetval);
6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * See also Section 7.1 of RFC 1521
6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
67448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleLanguage(PropertyData propertyData, String langval)
67548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            throws VCardException {
6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] strArray = langval.split("-");
6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strArray.length != 2) {
6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardException("Invalid Language: \"" + langval + "\"");
6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String tmp = strArray[0];
6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int length = tmp.length();
6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
6854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        tmp = strArray[1];
6884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        length = tmp.length();
6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!isAsciiLetter(tmp.charAt(i))) {
6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("Invalid Language: \"" + langval + "\"");
6924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
69448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        propertyData.addParam(VCardConstants.PARAM_LANGUAGE, langval);
6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private boolean isAsciiLetter(char ch) {
6984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
6994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
7004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return false;
7024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
7054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Mainly for "X-" type. This accepts any kind of type without check.
7064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
70748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    protected void handleAnyParam(
70848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            PropertyData propertyData, String paramName, String paramValue) {
70948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa        propertyData.addParam(paramName, paramValue);
7104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handlePropertyValue(String propertyName, String propertyValue)
7134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
7144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String upperEncoding = mCurrentEncoding.toUpperCase();
7154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
7164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String result = getQuotedPrintable(propertyValue);
7174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final ArrayList<String> v = new ArrayList<String>();
7184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            v.add(result);
7194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyValues(v);
7204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // It is very rare, but some BASE64 data may be so big that
7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // OutOfMemoryError occurs. To ignore such cases, use try-catch.
7244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            try {
7254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                final ArrayList<String> arrayList = new ArrayList<String>();
7264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                arrayList.add(getBase64(propertyValue));
7274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mInterpreter.propertyValues(arrayList);
7284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } catch (OutOfMemoryError error) {
7294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
7304560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                mInterpreter.propertyValues(null);
7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    upperEncoding.startsWith("X-"))) {
7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Log.w(LOG_TAG,
7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        String.format("The encoding \"%s\" is unsupported by vCard %s",
7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                mCurrentEncoding, getVersionString()));
7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
740f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // Some device uses line folding defined in RFC 2425, which is not allowed
741f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // in vCard 2.1 (while needed in vCard 3.0).
742f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
743f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // e.g.
744f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // BEGIN:VCARD
745f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // VERSION:2.1
746f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // N:;Omega;;;
747f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // EMAIL;INTERNET:"Omega"
748f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //   <omega@example.com>
749f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // FN:Omega
750f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // END:VCARD
751f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
752f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // The vCard above assumes that email address should become:
753f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // "Omega" <omega@example.com>
754f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
755f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // But vCard 2.1 requires Quote-Printable when a line contains line break(s).
756f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
757f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // For more information about line folding,
758f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // see "5.8.1. Line delimiting and folding" in RFC 2425.
759f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            //
760f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // We take care of this case more formally in vCard 3.0, so we only need to
761f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            // do this in vCard 2.1.
762be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            if (getVersion() == VCardConfig.VERSION_21) {
763f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                StringBuilder builder = null;
764f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                while (true) {
765f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    final String nextLine = peekLine();
766f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // We don't need to care too much about this exceptional case,
767f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // but we should not wrongly eat up "END:VCARD", since it critically
768f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // breaks this parser's state machine.
769f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // Thus we roughly look over the next line and confirm it is at least not
770f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // "END:VCARD". This extra fee is worth paying. This is exceptional
771f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    // anyway.
772f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    if (!TextUtils.isEmpty(nextLine) &&
773f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            nextLine.charAt(0) == ' ' &&
774f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            !"END:VCARD".contains(nextLine.toUpperCase())) {
775f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        getLine();  // Drop the next line.
776f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
777f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        if (builder == null) {
778f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            builder = new StringBuilder();
779f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                            builder.append(propertyValue);
780f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        }
781f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        builder.append(nextLine.substring(1));
782f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    } else {
783f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                        break;
784f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    }
785f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
786f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                if (builder != null) {
787f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                    propertyValue = builder.toString();
788f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa                }
789f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa            }
790f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa
7914560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            ArrayList<String> v = new ArrayList<String>();
7924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            v.add(maybeUnescapeText(propertyValue));
7934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            mInterpreter.propertyValues(v);
7944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
7984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
7994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Parses and returns Quoted-Printable.
8004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
8014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *
8024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param firstString The string following a parameter name and attributes.
8034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            Example: "string" in
8044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *            "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r".
8054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return whole Quoted-Printable string, including a given argument and
8064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         following lines. Excludes the last empty line following to Quoted
8074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *         Printable lines.
8084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws IOException
8094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @throws VCardException
8104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
8114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String getQuotedPrintable(String firstString) throws IOException, VCardException {
8124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Specifically, there may be some padding between = and CRLF.
8134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // See the following:
8144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
8154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-line := *(qp-segment transport-padding CRLF)
8164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-part transport-padding
8174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // qp-segment := qp-section *(SPACE / TAB) "="
8184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // ; Maximum length of 76 characters
8194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //
8204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // e.g. (from RFC 2045)
8214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Now's the time =
8224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // for all folk to come=
8234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // to the aid of their country.
8244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (firstString.trim().endsWith("=")) {
8254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // remove "transport-padding"
8264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            int pos = firstString.length() - 1;
8274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (firstString.charAt(pos) != '=') {
8284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            StringBuilder builder = new StringBuilder();
8304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(firstString.substring(0, pos + 1));
8314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append("\r\n");
8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String line;
8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            while (true) {
8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = getLine();
8354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line == null) {
8364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    throw new VCardException("File ended during parsing a Quoted-Printable String");
8374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
8384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (line.trim().endsWith("=")) {
8394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    // remove "transport-padding"
8404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    pos = line.length() - 1;
8414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    while (line.charAt(pos) != '=') {
8424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
8434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line.substring(0, pos + 1));
8444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append("\r\n");
8454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
8464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(line);
8474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    break;
8484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
8494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return builder.toString();
8514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
8524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return firstString;
8534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
8544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getBase64(String firstString) throws IOException, VCardException {
857c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa        final StringBuilder builder = new StringBuilder();
8584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.append(firstString);
8594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        while (true) {
861c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            final String line = peekLine();
8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line == null) {
8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                throw new VCardException("File ended during parsing BASE64 binary");
8644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
865c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
866c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't
867c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // have them. We try to detect those cases using semi-colon, given BASE64 doesn't
868c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // contain it. Specifically BASE64 doesn't have semi-colon in it, so we should be able
869c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // to detect the case safely.
870c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            if (line.contains(":")) {
871c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                if (getKnownPropertyNameSet().contains(
872c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                        line.substring(0, line.indexOf(":")).toUpperCase())) {
873c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " +
874c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                            "which must not contain semi-colon. Treat the line as next property.");
875c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    Log.w(LOG_TAG, "Problematic line: " + line.trim());
876c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                    break;
877c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa                }
878c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            }
879c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
880c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            // Consume the line.
881c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa            getLine();
882c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa
8834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.length() == 0) {
8844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
8854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
8864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
8874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
8884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
8904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
8924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
8934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
8944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Mainly for "ADR", "ORG", and "N"
8954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
8964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
8974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
8984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr,
8994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Street, Locality, Region, Postal Code, Country Name orgparts =
9004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are
9014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family,
9024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III,
9034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a
9044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * semicolon in this string, it must be escaped ; with a "\" character. We
9054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * do not care the number of "strnosemi" here. We are not sure whether we
9064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * should add "\" CRLF to each value. We exclude them for now.
9074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
9094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
9104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some
9114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // softwares/devices
9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // emit such data.
9134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
9144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            propertyValue = getQuotedPrintable(propertyValue);
9154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue,
9184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                getVersion()));
9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /*
9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an
9234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * error toward the AGENT property.
9244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * // TODO: Support AGENT property.
9254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * item =
9264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws]
9274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD"
9284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected void handleAgent(final String propertyValue) throws VCardException {
9304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) {
9314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
9324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return;
9334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
9354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * For vCard 3.0.
9404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String maybeUnescapeText(final String text) {
9424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return text;
9434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
9464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns unescaped String if the character should be unescaped. Return
9474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";"
9484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * while "\x" should not be.
9494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    protected String maybeUnescapeCharacter(final char ch) {
9514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return unescapeCharacter(ch);
9524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /* package */ static String unescapeCharacter(final char ch) {
9554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Original vCard 2.1 specification does not allow transformation
9564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous
9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // implementation of
9584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // this class allowed them, so keep it as is.
9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
9604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return String.valueOf(ch);
9614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
967be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_21}
9684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected int getVersion() {
970be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        return VCardConfig.VERSION_21;
9714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
974be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa     * @return {@link VCardConfig#VERSION_30}
9754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
9764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getVersionString() {
9774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardConstants.VERSION_V21;
9784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownPropertyNameSet() {
9814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownPropertyNameSet;
9824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownTypeSet() {
9854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownTypeSet;
9864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getKnownValueSet() {
9894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sKnownValueSet;
9904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected Set<String> getAvailableEncodingSet() {
9934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return VCardParser_V21.sAvailableEncoding;
9944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
9964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    protected String getDefaultEncoding() {
9974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return DEFAULT_ENCODING;
9984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
9994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public void parse(InputStream is, VCardInterpreter interpreter)
10024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throws IOException, VCardException {
10034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (is == null) {
10044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            throw new NullPointerException("InputStream must not be null.");
10054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset);
1008f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa        mReader = new CustomBufferedReader(tmpReader);
10094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        mInterpreter = (interpreter != null ? interpreter : new EmptyInterpreter());
10114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final long start = System.currentTimeMillis();
10134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mInterpreter != null) {
10144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mInterpreter.start();
10154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        parseVCardFile();
10174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (mInterpreter != null) {
10184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            mInterpreter.end();
10194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
10204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
10224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public final void cancel() {
1023b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa        Log.i(LOG_TAG, "ParserImpl received cancel operation.");
10244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        mCanceled = true;
10254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
10264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
1027