VCardSourceDetector.java revision be378d5b188f51cf717e5309e3c39180e85833a8
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 public void start() { 88 } 89 90 public void end() { 91 } 92 93 public void startEntry() { 94 } 95 96 public void startProperty() { 97 mNeedToParseCharset = false; 98 mNeedToParseVersion = false; 99 } 100 101 public void endProperty() { 102 } 103 104 public void endEntry() { 105 } 106 107 public void propertyGroup(String group) { 108 } 109 110 public void propertyName(String name) { 111 if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) { 112 mNeedToParseVersion = true; 113 return; 114 } else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { 115 mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; 116 // Probably Shift_JIS is used, but we should double confirm. 117 mNeedToParseCharset = true; 118 return; 119 } 120 if (mParseType != PARSE_TYPE_UNKNOWN) { 121 return; 122 } 123 if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { 124 mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; 125 } else if (FOMA_SIGNS.contains(name)) { 126 mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; 127 } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { 128 mParseType = PARSE_TYPE_MOBILE_PHONE_JP; 129 } else if (APPLE_SIGNS.contains(name)) { 130 mParseType = PARSE_TYPE_APPLE; 131 } 132 } 133 134 public void propertyParamType(String type) { 135 } 136 137 public void propertyParamValue(String value) { 138 } 139 140 public void propertyValues(List<String> values) { 141 if (mNeedToParseVersion && values.size() > 0) { 142 final String versionString = values.get(0); 143 if (versionString.equals(VCardConstants.VERSION_V21)) { 144 mVersion = VCardConfig.VERSION_21; 145 } else if (versionString.equals(VCardConstants.VERSION_V30)) { 146 mVersion = VCardConfig.VERSION_30; 147 } else if (versionString.equals(VCardConstants.VERSION_V40)) { 148 mVersion = VCardConfig.VERSION_40; 149 } else { 150 Log.w(LOG_TAG, "Invalid version string: " + versionString); 151 } 152 } else if (mNeedToParseCharset && values.size() > 0) { 153 mSpecifiedCharset = values.get(0); 154 } 155 } 156 157 /** 158 * @return The available type can be used with vCard parser. You probably need to 159 * use {{@link #getEstimatedCharset()} to understand the charset to be used. 160 */ 161 public int getEstimatedType() { 162 switch (mParseType) { 163 case PARSE_TYPE_DOCOMO_TORELATE_NEST: 164 return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST; 165 case PARSE_TYPE_MOBILE_PHONE_JP: 166 return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; 167 case PARSE_TYPE_APPLE: 168 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 169 default: { 170 if (mVersion == VCardConfig.VERSION_21) { 171 return VCardConfig.VCARD_TYPE_V21_GENERIC; 172 } else if (mVersion == VCardConfig.VERSION_30) { 173 return VCardConfig.VCARD_TYPE_V30_GENERIC; 174 } else if (mVersion == VCardConfig.VERSION_40) { 175 return VCardConfig.VCARD_TYPE_V40_GENERIC; 176 } else { 177 return VCardConfig.VCARD_TYPE_UNKNOWN; 178 } 179 } 180 } 181 } 182 183 /** 184 * <p> 185 * Returns charset String guessed from the source's properties. 186 * This method must be called after parsing target file(s). 187 * </p> 188 * @return Charset String. Null is returned if guessing the source fails. 189 */ 190 public String getEstimatedCharset() { 191 if (TextUtils.isEmpty(mSpecifiedCharset)) { 192 return mSpecifiedCharset; 193 } 194 switch (mParseType) { 195 case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: 196 case PARSE_TYPE_DOCOMO_TORELATE_NEST: 197 case PARSE_TYPE_MOBILE_PHONE_JP: 198 return "SHIFT_JIS"; 199 case PARSE_TYPE_APPLE: 200 return "UTF-8"; 201 default: 202 return null; 203 } 204 } 205} 206