1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.vcard; 17 18 import android.text.TextUtils; 19 import android.util.Log; 20 21 import java.util.Arrays; 22 import java.util.HashSet; 23 import java.util.List; 24 import java.util.Set; 25 26 /** 27 * <p> 28 * The class which tries to detects the source of a vCard file from its contents. 29 * </p> 30 * <p> 31 * The specification of vCard (including both 2.1 and 3.0) is not so strict as to 32 * guess its format just by reading beginning few lines (usually we can, but in 33 * some most pessimistic case, we cannot until at almost the end of the file). 34 * Also we cannot store all vCard entries in memory, while there's no specification 35 * how big the vCard entry would become after the parse. 36 * </p> 37 * <p> 38 * This class is usually used for the "first scan", in which we can understand which vCard 39 * version is used (and how many entries exist in a file). 40 * </p> 41 */ 42 public class VCardSourceDetector implements VCardInterpreter { 43 private static final String LOG_TAG = VCardConstants.LOG_TAG; 44 45 private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList( 46 "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", 47 "X-ABADR", "X-ABUID")); 48 49 private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( 50 "X-GNO", "X-GN", "X-REDUCTION")); 51 52 private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList( 53 "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); 54 55 // Note: these signes appears before the signs of the other type (e.g. "X-GN"). 56 // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. 57 private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList( 58 "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", 59 "X-SD-DESCRIPTION")); 60 private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; 61 62 /** 63 * Represents that no estimation is available. Users of this class is able to this 64 * constant when you don't want to let a vCard parser rely on estimation for parse type. 65 */ 66 public static final int PARSE_TYPE_UNKNOWN = 0; 67 68 // For Apple's software, which does not mean this type is effective for all its products. 69 // We confirmed they usually use UTF-8, but not sure about vCard type. 70 private static final int PARSE_TYPE_APPLE = 1; 71 // For Japanese mobile phones, which are usually using Shift_JIS as a charset. 72 private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; 73 // For some of mobile phones released from DoCoMo. 74 private static final int PARSE_TYPE_DOCOMO_FOMA = 3; 75 // For Japanese Windows Mobile phones. It's version is supposed to be 6.5. 76 private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4; 77 78 private int mParseType = PARSE_TYPE_UNKNOWN; 79 80 private int mVersion = -1; // -1 == unknown 81 82 // Some mobile phones (like FOMA) tells us the charset of the data. 83 private String mSpecifiedCharset; 84 85 @Override onVCardStarted()86 public void onVCardStarted() { 87 } 88 89 @Override onVCardEnded()90 public void onVCardEnded() { 91 } 92 93 @Override onEntryStarted()94 public void onEntryStarted() { 95 } 96 97 @Override onEntryEnded()98 public void onEntryEnded() { 99 } 100 101 @Override onPropertyCreated(VCardProperty property)102 public void onPropertyCreated(VCardProperty property) { 103 final String propertyName = property.getName(); 104 final List<String> valueList = property.getValueList(); 105 106 if (propertyName.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION) 107 && valueList.size() > 0) { 108 final String versionString = valueList.get(0); 109 if (versionString.equals(VCardConstants.VERSION_V21)) { 110 mVersion = VCardConfig.VERSION_21; 111 } else if (versionString.equals(VCardConstants.VERSION_V30)) { 112 mVersion = VCardConfig.VERSION_30; 113 } else if (versionString.equals(VCardConstants.VERSION_V40)) { 114 mVersion = VCardConfig.VERSION_40; 115 } else { 116 Log.w(LOG_TAG, "Invalid version string: " + versionString); 117 } 118 } else if (propertyName.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { 119 mParseType = PARSE_TYPE_DOCOMO_FOMA; 120 if (valueList.size() > 0) { 121 mSpecifiedCharset = valueList.get(0); 122 } 123 } 124 if (mParseType != PARSE_TYPE_UNKNOWN) { 125 return; 126 } 127 if (WINDOWS_MOBILE_PHONE_SIGNS.contains(propertyName)) { 128 mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; 129 } else if (FOMA_SIGNS.contains(propertyName)) { 130 mParseType = PARSE_TYPE_DOCOMO_FOMA; 131 } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(propertyName)) { 132 mParseType = PARSE_TYPE_MOBILE_PHONE_JP; 133 } else if (APPLE_SIGNS.contains(propertyName)) { 134 mParseType = PARSE_TYPE_APPLE; 135 } 136 } 137 138 /** 139 * @return The available type can be used with vCard parser. You probably need to 140 * use {{@link #getEstimatedCharset()} to understand the charset to be used. 141 */ getEstimatedType()142 public int getEstimatedType() { 143 switch (mParseType) { 144 case PARSE_TYPE_DOCOMO_FOMA: 145 return VCardConfig.VCARD_TYPE_DOCOMO; 146 case PARSE_TYPE_MOBILE_PHONE_JP: 147 return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; 148 case PARSE_TYPE_APPLE: 149 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 150 default: { 151 if (mVersion == VCardConfig.VERSION_21) { 152 return VCardConfig.VCARD_TYPE_V21_GENERIC; 153 } else if (mVersion == VCardConfig.VERSION_30) { 154 return VCardConfig.VCARD_TYPE_V30_GENERIC; 155 } else if (mVersion == VCardConfig.VERSION_40) { 156 return VCardConfig.VCARD_TYPE_V40_GENERIC; 157 } else { 158 return VCardConfig.VCARD_TYPE_UNKNOWN; 159 } 160 } 161 } 162 } 163 164 /** 165 * <p> 166 * Returns charset String guessed from the source's properties. 167 * This method must be called after parsing target file(s). 168 * </p> 169 * @return Charset String. Null is returned if guessing the source fails. 170 */ getEstimatedCharset()171 public String getEstimatedCharset() { 172 if (TextUtils.isEmpty(mSpecifiedCharset)) { 173 return mSpecifiedCharset; 174 } 175 switch (mParseType) { 176 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 177 case PARSE_TYPE_DOCOMO_FOMA: 178 case PARSE_TYPE_MOBILE_PHONE_JP: 179 return "SHIFT_JIS"; 180 case PARSE_TYPE_APPLE: 181 return "UTF-8"; 182 default: 183 return null; 184 } 185 } 186 } 187