Home | History | Annotate | Download | only in vcard
      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 = "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