/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.vcard; import android.text.TextUtils; import android.util.Log; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** *

* The class which tries to detects the source of a vCard file from its contents. *

*

* The specification of vCard (including both 2.1 and 3.0) is not so strict as to * guess its format just by reading beginning few lines (usually we can, but in * some most pessimistic case, we cannot until at almost the end of the file). * Also we cannot store all vCard entries in memory, while there's no specification * how big the vCard entry would become after the parse. *

*

* This class is usually used for the "first scan", in which we can understand which vCard * version is used (and how many entries exist in a file). *

*/ public class VCardSourceDetector implements VCardInterpreter { private static final String LOG_TAG = VCardConstants.LOG_TAG; private static Set APPLE_SIGNS = new HashSet(Arrays.asList( "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", "X-ABADR", "X-ABUID")); private static Set JAPANESE_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( "X-GNO", "X-GN", "X-REDUCTION")); private static Set WINDOWS_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); // Note: these signes appears before the signs of the other type (e.g. "X-GN"). // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. private static Set FOMA_SIGNS = new HashSet(Arrays.asList( "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", "X-SD-DESCRIPTION")); private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; /** * Represents that no estimation is available. Users of this class is able to this * constant when you don't want to let a vCard parser rely on estimation for parse type. */ public static final int PARSE_TYPE_UNKNOWN = 0; // For Apple's software, which does not mean this type is effective for all its products. // We confirmed they usually use UTF-8, but not sure about vCard type. private static final int PARSE_TYPE_APPLE = 1; // For Japanese mobile phones, which are usually using Shift_JIS as a charset. private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; // For some of mobile phones released from DoCoMo. private static final int PARSE_TYPE_DOCOMO_FOMA = 3; // For Japanese Windows Mobile phones. It's version is supposed to be 6.5. private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4; private int mParseType = PARSE_TYPE_UNKNOWN; private int mVersion = -1; // -1 == unknown // Some mobile phones (like FOMA) tells us the charset of the data. private String mSpecifiedCharset; @Override public void onVCardStarted() { } @Override public void onVCardEnded() { } @Override public void onEntryStarted() { } @Override public void onEntryEnded() { } @Override public void onPropertyCreated(VCardProperty property) { final String propertyName = property.getName(); final List valueList = property.getValueList(); if (propertyName.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION) && valueList.size() > 0) { final String versionString = valueList.get(0); if (versionString.equals(VCardConstants.VERSION_V21)) { mVersion = VCardConfig.VERSION_21; } else if (versionString.equals(VCardConstants.VERSION_V30)) { mVersion = VCardConfig.VERSION_30; } else if (versionString.equals(VCardConstants.VERSION_V40)) { mVersion = VCardConfig.VERSION_40; } else { Log.w(LOG_TAG, "Invalid version string: " + versionString); } } else if (propertyName.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { mParseType = PARSE_TYPE_DOCOMO_FOMA; if (valueList.size() > 0) { mSpecifiedCharset = valueList.get(0); } } if (mParseType != PARSE_TYPE_UNKNOWN) { return; } if (WINDOWS_MOBILE_PHONE_SIGNS.contains(propertyName)) { mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; } else if (FOMA_SIGNS.contains(propertyName)) { mParseType = PARSE_TYPE_DOCOMO_FOMA; } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(propertyName)) { mParseType = PARSE_TYPE_MOBILE_PHONE_JP; } else if (APPLE_SIGNS.contains(propertyName)) { mParseType = PARSE_TYPE_APPLE; } } /** * @return The available type can be used with vCard parser. You probably need to * use {{@link #getEstimatedCharset()} to understand the charset to be used. */ public int getEstimatedType() { switch (mParseType) { case PARSE_TYPE_DOCOMO_FOMA: return VCardConfig.VCARD_TYPE_DOCOMO; case PARSE_TYPE_MOBILE_PHONE_JP: return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; case PARSE_TYPE_APPLE: case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: default: { if (mVersion == VCardConfig.VERSION_21) { return VCardConfig.VCARD_TYPE_V21_GENERIC; } else if (mVersion == VCardConfig.VERSION_30) { return VCardConfig.VCARD_TYPE_V30_GENERIC; } else if (mVersion == VCardConfig.VERSION_40) { return VCardConfig.VCARD_TYPE_V40_GENERIC; } else { return VCardConfig.VCARD_TYPE_UNKNOWN; } } } } /** *

* Returns charset String guessed from the source's properties. * This method must be called after parsing target file(s). *

* @return Charset String. Null is returned if guessing the source fails. */ public String getEstimatedCharset() { if (TextUtils.isEmpty(mSpecifiedCharset)) { return mSpecifiedCharset; } switch (mParseType) { case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: case PARSE_TYPE_DOCOMO_FOMA: case PARSE_TYPE_MOBILE_PHONE_JP: return "SHIFT_JIS"; case PARSE_TYPE_APPLE: return "UTF-8"; default: return null; } } }