VCardSourceDetector.java revision 677ef21613a9d35053ec098444832ce4125a847e
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 */ 16package com.android.vcard; 17 18import android.text.TextUtils; 19import android.util.Log; 20 21import java.util.Arrays; 22import java.util.HashSet; 23import java.util.List; 24import 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 */ 42public class VCardSourceDetector implements VCardInterpreter { 43 private static final String LOG_TAG = "VCardSourceDetector"; 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, which use nested vCard. 74 private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3; 75 // For Japanese Windows Mobel 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 = 0; // Not sure. 79 80 private boolean mNeedToParseVersion = false; 81 private int mVersion = -1; // -1 == unknown 82 83 // Some mobile phones (like FOMA) tells us the charset of the data. 84 private boolean mNeedToParseCharset; 85 private String mSpecifiedCharset; 86 87 @Override 88 public void start() { 89 } 90 91 @Override 92 public void end() { 93 } 94 95 @Override 96 public void startEntry() { 97 } 98 99 @Override 100 public void startProperty() { 101 mNeedToParseCharset = false; 102 mNeedToParseVersion = false; 103 } 104 105 @Override 106 public void endProperty() { 107 } 108 109 @Override 110 public void endEntry() { 111 } 112 113 @Override 114 public void propertyGroup(String group) { 115 } 116 117 @Override 118 public void propertyName(String name) { 119 if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) { 120 mNeedToParseVersion = true; 121 return; 122 } else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { 123 mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; 124 // Probably Shift_JIS is used, but we should double confirm. 125 mNeedToParseCharset = true; 126 return; 127 } 128 if (mParseType != PARSE_TYPE_UNKNOWN) { 129 return; 130 } 131 if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { 132 mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; 133 } else if (FOMA_SIGNS.contains(name)) { 134 mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; 135 } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { 136 mParseType = PARSE_TYPE_MOBILE_PHONE_JP; 137 } else if (APPLE_SIGNS.contains(name)) { 138 mParseType = PARSE_TYPE_APPLE; 139 } 140 } 141 142 @Override 143 public void propertyParamType(String type) { 144 } 145 146 @Override 147 public void propertyParamValue(String value) { 148 } 149 150 @Override 151 public void propertyValues(List<String> values) { 152 if (mNeedToParseVersion && values.size() > 0) { 153 final String versionString = values.get(0); 154 if (versionString.equals(VCardConstants.VERSION_V21)) { 155 mVersion = VCardConfig.VERSION_21; 156 } else if (versionString.equals(VCardConstants.VERSION_V30)) { 157 mVersion = VCardConfig.VERSION_30; 158 } else if (versionString.equals(VCardConstants.VERSION_V40)) { 159 mVersion = VCardConfig.VERSION_40; 160 } else { 161 Log.w(LOG_TAG, "Invalid version string: " + versionString); 162 } 163 } else if (mNeedToParseCharset && values.size() > 0) { 164 mSpecifiedCharset = values.get(0); 165 } 166 } 167 168 /** 169 * @return The available type can be used with vCard parser. You probably need to 170 * use {{@link #getEstimatedCharset()} to understand the charset to be used. 171 */ 172 public int getEstimatedType() { 173 switch (mParseType) { 174 case PARSE_TYPE_DOCOMO_TORELATE_NEST: 175 return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST; 176 case PARSE_TYPE_MOBILE_PHONE_JP: 177 return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; 178 case PARSE_TYPE_APPLE: 179 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 180 default: { 181 if (mVersion == VCardConfig.VERSION_21) { 182 return VCardConfig.VCARD_TYPE_V21_GENERIC; 183 } else if (mVersion == VCardConfig.VERSION_30) { 184 return VCardConfig.VCARD_TYPE_V30_GENERIC; 185 } else if (mVersion == VCardConfig.VERSION_40) { 186 return VCardConfig.VCARD_TYPE_V40_GENERIC; 187 } else { 188 return VCardConfig.VCARD_TYPE_UNKNOWN; 189 } 190 } 191 } 192 } 193 194 /** 195 * <p> 196 * Returns charset String guessed from the source's properties. 197 * This method must be called after parsing target file(s). 198 * </p> 199 * @return Charset String. Null is returned if guessing the source fails. 200 */ 201 public String getEstimatedCharset() { 202 if (TextUtils.isEmpty(mSpecifiedCharset)) { 203 return mSpecifiedCharset; 204 } 205 switch (mParseType) { 206 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 207 case PARSE_TYPE_DOCOMO_TORELATE_NEST: 208 case PARSE_TYPE_MOBILE_PHONE_JP: 209 return "SHIFT_JIS"; 210 case PARSE_TYPE_APPLE: 211 return "UTF-8"; 212 default: 213 return null; 214 } 215 } 216} 217