VCardSourceDetector.java revision 4199c54c527330ac01699b176e7bca186a3aa3a4
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;
19
20import java.util.Arrays;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Set;
24
25/**
26 * <p>
27 * The class which tries to detects the source of a vCard file from its contents.
28 * </p>
29 * <p>
30 * The specification of vCard (including both 2.1 and 3.0) is not so strict as to
31 * guess its format just by reading beginning few lines (usually we can, but in
32 * some most pessimistic case, we cannot until at almost the end of the file).
33 * Also we cannot store all vCard entries in memory, while there's no specification
34 * how big the vCard entry would become after the parse.
35 * </p>
36 * <p>
37 * This class is usually used for the "first scan", in which we can understand which vCard
38 * version is used (and how many entries exist in a file).
39 * </p>
40 */
41public class VCardSourceDetector implements VCardInterpreter {
42    private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
43            "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
44            "X-ABADR", "X-ABUID"));
45
46    private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
47            "X-GNO", "X-GN", "X-REDUCTION"));
48
49    private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
50            "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
51
52    // Note: these signes appears before the signs of the other type (e.g. "X-GN").
53    // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
54    private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
55            "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
56            "X-SD-DESCRIPTION"));
57    private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
58
59
60    // TODO: Should replace this with types in VCardConfig
61    private static final int PARSE_TYPE_UNKNOWN = 0;
62    // For Apple's software, which does not mean this type is effective for all its products.
63    // We confirmed they usually use UTF-8, but not sure about vCard type.
64    private static final int PARSE_TYPE_APPLE = 1;
65    // For Japanese mobile phones, which are usually using Shift_JIS as a charset.
66    private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
67    // For some of mobile phones released from DoCoMo, which use nested vCard.
68    private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3;
69    // For Japanese Windows Mobel phones. It's version is supposed to be 6.5.
70    private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
71
72    private int mParseType = 0;  // Not sure.
73
74    // Some mobile phones (like FOMA) tells us the charset of the data.
75    private boolean mNeedParseSpecifiedCharset;
76    private String mSpecifiedCharset;
77
78    public void start() {
79    }
80
81    public void end() {
82    }
83
84    public void startEntry() {
85    }
86
87    public void startProperty() {
88        mNeedParseSpecifiedCharset = false;
89    }
90
91    public void endProperty() {
92    }
93
94    public void endEntry() {
95    }
96
97    public void propertyGroup(String group) {
98    }
99
100    public void propertyName(String name) {
101        if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
102            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
103            // Probably Shift_JIS is used, but we should double confirm.
104            mNeedParseSpecifiedCharset = true;
105            return;
106        }
107        if (mParseType != PARSE_TYPE_UNKNOWN) {
108            return;
109        }
110        if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
111            mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
112        } else if (FOMA_SIGNS.contains(name)) {
113            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
114        } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
115            mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
116        } else if (APPLE_SIGNS.contains(name)) {
117            mParseType = PARSE_TYPE_APPLE;
118        }
119    }
120
121    public void propertyParamType(String type) {
122    }
123
124    public void propertyParamValue(String value) {
125    }
126
127    public void propertyValues(List<String> values) {
128        if (mNeedParseSpecifiedCharset && values.size() > 0) {
129            mSpecifiedCharset = values.get(0);
130        }
131    }
132
133    /**
134     * @return The available type can be used with vCard parser. You probably need to
135     * use {{@link #getEstimatedCharset()} to understand the charset to be used.
136     */
137    public int getEstimatedType() {
138        switch (mParseType) {
139            case PARSE_TYPE_DOCOMO_TORELATE_NEST:
140                return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST;
141            case PARSE_TYPE_MOBILE_PHONE_JP:
142                return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
143            case PARSE_TYPE_APPLE:
144            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
145            default:
146                return VCardConfig.VCARD_TYPE_UNKNOWN;
147        }
148    }
149
150    /**
151     * <p>
152     * Returns charset String guessed from the source's properties.
153     * This method must be called after parsing target file(s).
154     * </p>
155     * @return Charset String. Null is returned if guessing the source fails.
156     */
157    public String getEstimatedCharset() {
158        if (TextUtils.isEmpty(mSpecifiedCharset)) {
159            return mSpecifiedCharset;
160        }
161        switch (mParseType) {
162            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
163            case PARSE_TYPE_DOCOMO_TORELATE_NEST:
164            case PARSE_TYPE_MOBILE_PHONE_JP:
165                return "SHIFT_JIS";
166            case PARSE_TYPE_APPLE:
167                return "UTF-8";
168            default:
169                return null;
170        }
171    }
172}
173