14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/*
24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project
34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License.
64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at
74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and
144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License.
154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard;
174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.text.TextUtils;
19be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawaimport android.util.Log;
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Arrays;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.List;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The class which tries to detects the source of a vCard file from its contents.
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The specification of vCard (including both 2.1 and 3.0) is not so strict as to
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * guess its format just by reading beginning few lines (usually we can, but in
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * some most pessimistic case, we cannot until at almost the end of the file).
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Also we cannot store all vCard entries in memory, while there's no specification
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * how big the vCard entry would become after the parse.
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p>
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * This class is usually used for the "first scan", in which we can understand which vCard
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * version is used (and how many entries exist in a file).
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p>
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class VCardSourceDetector implements VCardInterpreter {
4302117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    private static final String LOG_TAG = VCardConstants.LOG_TAG;
44be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-ABADR", "X-ABUID"));
48677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-GNO", "X-GN", "X-REDUCTION"));
51677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
54677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Note: these signes appears before the signs of the other type (e.g. "X-GN").
564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            "X-SD-DESCRIPTION"));
604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6258610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa    /**
6358610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa     * Represents that no estimation is available. Users of this class is able to this
6458610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa     * constant when you don't want to let a vCard parser rely on estimation for parse type.
6558610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa     */
6658610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa    public static final int PARSE_TYPE_UNKNOWN = 0;
674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For Apple's software, which does not mean this type is effective for all its products.
694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // We confirmed they usually use UTF-8, but not sure about vCard type.
704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final int PARSE_TYPE_APPLE = 1;
714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // For Japanese mobile phones, which are usually using Shift_JIS as a charset.
724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
7348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    // For some of mobile phones released from DoCoMo.
7448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa    private static final int PARSE_TYPE_DOCOMO_FOMA = 3;
751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    // For Japanese Windows Mobile phones. It's version is supposed to be 6.5.
764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    private int mParseType = PARSE_TYPE_UNKNOWN;
794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
80be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    private int mVersion = -1;  // -1 == unknown
81be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Some mobile phones (like FOMA) tells us the charset of the data.
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private String mSpecifiedCharset;
84677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
85677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    @Override
861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void onVCardStarted() {
874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
88677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
89677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    @Override
901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void onVCardEnded() {
914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
93677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    @Override
941de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void onEntryStarted() {
95677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    }
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
97677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    @Override
981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void onEntryEnded() {
994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
100677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
101677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa    @Override
1021de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public void onPropertyCreated(VCardProperty property) {
1031de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final String propertyName = property.getName();
1041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final List<String> valueList = property.getValueList();
105677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa
1061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (propertyName.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)
1071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                && valueList.size() > 0) {
1081de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            final String versionString = valueList.get(0);
109be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            if (versionString.equals(VCardConstants.VERSION_V21)) {
110be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                mVersion = VCardConfig.VERSION_21;
111be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            } else if (versionString.equals(VCardConstants.VERSION_V30)) {
112be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                mVersion = VCardConfig.VERSION_30;
113be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            } else if (versionString.equals(VCardConstants.VERSION_V40)) {
114be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                mVersion = VCardConfig.VERSION_40;
115be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            } else {
116be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                Log.w(LOG_TAG, "Invalid version string: " + versionString);
117be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            }
1181de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        } else if (propertyName.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
1191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            mParseType = PARSE_TYPE_DOCOMO_FOMA;
1201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            if (valueList.size() > 0) {
1211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa                mSpecifiedCharset = valueList.get(0);
1221de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            }
1231de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
1241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (mParseType != PARSE_TYPE_UNKNOWN) {
1251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            return;
1261de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        }
1271de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        if (WINDOWS_MOBILE_PHONE_SIGNS.contains(propertyName)) {
1281de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
1291de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        } else if (FOMA_SIGNS.contains(propertyName)) {
1301de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            mParseType = PARSE_TYPE_DOCOMO_FOMA;
1311de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(propertyName)) {
1321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
1331de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        } else if (APPLE_SIGNS.contains(propertyName)) {
1341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa            mParseType = PARSE_TYPE_APPLE;
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return The available type can be used with vCard parser. You probably need to
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * use {{@link #getEstimatedCharset()} to understand the charset to be used.
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public int getEstimatedType() {
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        switch (mParseType) {
14448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            case PARSE_TYPE_DOCOMO_FOMA:
14548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa                return VCardConfig.VCARD_TYPE_DOCOMO;
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_MOBILE_PHONE_JP:
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_APPLE:
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
150be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            default: {
151be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                if (mVersion == VCardConfig.VERSION_21) {
152be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                    return VCardConfig.VCARD_TYPE_V21_GENERIC;
153be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                } else if (mVersion == VCardConfig.VERSION_30) {
154be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                    return VCardConfig.VCARD_TYPE_V30_GENERIC;
155be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                } else if (mVersion == VCardConfig.VERSION_40) {
156be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                    return VCardConfig.VCARD_TYPE_V40_GENERIC;
157be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                } else {
158be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                    return VCardConfig.VCARD_TYPE_UNKNOWN;
159be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                }
160be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            }
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns charset String guessed from the source's properties.
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * This method must be called after parsing target file(s).
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return Charset String. Null is returned if guessing the source fails.
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public String getEstimatedCharset() {
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(mSpecifiedCharset)) {
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return mSpecifiedCharset;
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        switch (mParseType) {
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
17748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa            case PARSE_TYPE_DOCOMO_FOMA:
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_MOBILE_PHONE_JP:
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return "SHIFT_JIS";
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case PARSE_TYPE_APPLE:
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return "UTF-8";
1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            default:
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return null;
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
187