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