blob: 4f50103a84d7ac03c1529140405a04287661b90f [file] [log] [blame]
Daisuke Miyakawaf4ddea72009-08-23 10:01:32 +09001/*
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 android.pim.vcard;
17
18import android.content.ContentProviderOperation;
19import android.content.ContentValues;
20import android.provider.ContactsContract.Data;
21import android.provider.ContactsContract.CommonDataKinds.Phone;
22import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
23import android.text.TextUtils;
24
25import java.util.Collection;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.Map;
29import java.util.Set;
30
31/**
32 * Utilities for VCard handling codes.
33 */
34public class VCardUtils {
35 /*
36 * TODO: some of methods in this class should be placed to the more appropriate place...
37 */
38
39 // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
40 // converted to two attribute Strings. These only contain some minor fields valid in both
41 // vCard and current (as of 2009-08-07) Contacts structure.
42 private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
43 private static final Set<String> sPhoneTypesSetUnknownToContacts;
44
45 private static final Map<String, Integer> sKnownPhoneTypesMap_StoI;
46
47 static {
48 sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
49 sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>();
50
51 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR);
52 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
53 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER);
54 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
55 sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN);
56 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
57
58 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
59 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
60 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
61
62 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER);
63 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK);
64 sKnownPhoneTypesMap_StoI.put(
65 Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
66 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO);
67 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX);
68 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD);
69 sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT);
70
71 sPhoneTypesSetUnknownToContacts = new HashSet<String>();
72 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM);
73 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG);
74 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS);
75 sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO);
76 }
77
Jeff Sharkey6ba23c52009-09-09 22:57:39 -070078 public static String getPhoneAttributeString(Integer type) {
Daisuke Miyakawaf4ddea72009-08-23 10:01:32 +090079 return sKnownPhoneTypesMap_ItoS.get(type);
80 }
81
82 /**
83 * Returns Interger when the given types can be parsed as known type. Returns String object
84 * when not, which should be set to label.
85 */
86 public static Object getPhoneTypeFromStrings(Collection<String> types) {
87 int type = -1;
88 String label = null;
89 boolean isFax = false;
90 boolean hasPref = false;
91
92 if (types != null) {
93 for (String typeString : types) {
94 typeString = typeString.toUpperCase();
95 if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
96 hasPref = true;
97 } else if (typeString.equals(Constants.ATTR_TYPE_FAX)) {
98 isFax = true;
99 } else {
100 if (typeString.startsWith("X-") && type < 0) {
101 typeString = typeString.substring(2);
102 }
103 Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString);
104 if (tmp != null) {
105 type = tmp;
106 } else if (type < 0) {
107 type = Phone.TYPE_CUSTOM;
108 label = typeString;
109 }
110 }
111 }
112 }
113 if (type < 0) {
114 if (hasPref) {
115 type = Phone.TYPE_MAIN;
116 } else {
117 // default to TYPE_HOME
118 type = Phone.TYPE_HOME;
119 }
120 }
121 if (isFax) {
122 if (type == Phone.TYPE_HOME) {
123 type = Phone.TYPE_FAX_HOME;
124 } else if (type == Phone.TYPE_WORK) {
125 type = Phone.TYPE_FAX_WORK;
126 } else if (type == Phone.TYPE_OTHER) {
127 type = Phone.TYPE_OTHER_FAX;
128 }
129 }
130 if (type == Phone.TYPE_CUSTOM) {
131 return label;
132 } else {
133 return type;
134 }
135 }
136
137 public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) {
138 // TODO: check the following.
139 // - it may violate vCard spec
140 // - it may contain non-ASCII characters
141 //
142 // TODO: use vcardType
143 return (phoneAttribute.startsWith("X-") || phoneAttribute.startsWith("x-") ||
144 sPhoneTypesSetUnknownToContacts.contains(phoneAttribute));
145 }
146
147 public static String[] sortNameElements(int vcardType,
148 String familyName, String middleName, String givenName) {
149 String[] list = new String[3];
150 switch (VCardConfig.getNameOrderType(vcardType)) {
151 case VCardConfig.NAME_ORDER_JAPANESE:
152 // TODO: Should handle Ascii case?
153 list[0] = familyName;
154 list[1] = middleName;
155 list[2] = givenName;
156 break;
157 case VCardConfig.NAME_ORDER_EUROPE:
158 list[0] = middleName;
159 list[1] = givenName;
160 list[2] = familyName;
161 break;
162 default:
163 list[0] = givenName;
164 list[1] = middleName;
165 list[2] = familyName;
166 break;
167 }
168 return list;
169 }
170
171 /**
172 * Inserts postal data into the builder object.
173 *
174 * Note that the data structure of ContactsContract is different from that defined in vCard.
175 * So some conversion may be performed in this method. See also
176 * {{@link #getVCardPostalElements(ContentValues)}
177 */
178 public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
179 final ContentProviderOperation.Builder builder,
180 final ContactStruct.PostalData postalData) {
181 builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
182 builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
183
184 builder.withValue(StructuredPostal.TYPE, postalData.type);
185 if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
186 builder.withValue(StructuredPostal.LABEL, postalData.label);
187 }
188
189 builder.withValue(StructuredPostal.POBOX, postalData.pobox);
190 // Extended address is dropped since there's no relevant entry in ContactsContract.
191 builder.withValue(StructuredPostal.STREET, postalData.street);
192 builder.withValue(StructuredPostal.CITY, postalData.localty);
193 builder.withValue(StructuredPostal.REGION, postalData.region);
194 builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
195 builder.withValue(StructuredPostal.COUNTRY, postalData.country);
196
197 builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
198 postalData.getFormattedAddress(vcardType));
199 if (postalData.isPrimary) {
200 builder.withValue(Data.IS_PRIMARY, 1);
201 }
202 }
203
204 /**
205 * Returns String[] containing address information based on vCard spec
206 * (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name).
207 * All String objects are non-null ("" is used when the relevant data is empty).
208 *
209 * Note that the data structure of ContactsContract is different from that defined in vCard.
210 * So some conversion may be performed in this method. See also
211 * {{@link #insertStructuredPostalDataUsingContactsStruct(int,
212 * android.content.ContentProviderOperation.Builder,
213 * android.pim.vcard.ContactStruct.PostalData)}
214 */
215 public static String[] getVCardPostalElements(ContentValues contentValues) {
216 String[] dataArray = new String[7];
217 dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX);
218 if (dataArray[0] == null) {
219 dataArray[0] = "";
220 }
221 // Extended addr. There's no relevant data in ContactsContract.
222 dataArray[1] = "";
223 dataArray[2] = contentValues.getAsString(StructuredPostal.STREET);
224 if (dataArray[2] == null) {
225 dataArray[2] = "";
226 }
227 // Assume that localty == city
228 dataArray[3] = contentValues.getAsString(StructuredPostal.CITY);
229 if (dataArray[3] == null) {
230 dataArray[3] = "";
231 }
232 String region = contentValues.getAsString(StructuredPostal.REGION);
233 if (!TextUtils.isEmpty(region)) {
234 dataArray[4] = region;
235 } else {
236 dataArray[4] = "";
237 }
238 dataArray[5] = contentValues.getAsString(StructuredPostal.POSTCODE);
239 if (dataArray[5] == null) {
240 dataArray[5] = "";
241 }
242 dataArray[6] = contentValues.getAsString(StructuredPostal.COUNTRY);
243 if (dataArray[6] == null) {
244 dataArray[6] = "";
245 }
246
247 return dataArray;
248 }
249
250 public static String constructNameFromElements(int nameOrderType,
251 String familyName, String middleName, String givenName) {
252 return constructNameFromElements(nameOrderType, familyName, middleName, givenName,
253 null, null);
254 }
255
256 public static String constructNameFromElements(int nameOrderType,
257 String familyName, String middleName, String givenName,
258 String prefix, String suffix) {
259 StringBuilder builder = new StringBuilder();
260 String[] nameList = sortNameElements(nameOrderType,
261 familyName, middleName, givenName);
262 boolean first = true;
263 if (!TextUtils.isEmpty(prefix)) {
264 first = false;
265 builder.append(prefix);
266 }
267 for (String namePart : nameList) {
268 if (!TextUtils.isEmpty(namePart)) {
269 if (first) {
270 first = false;
271 } else {
272 builder.append(' ');
273 }
274 builder.append(namePart);
275 }
276 }
277 if (!TextUtils.isEmpty(suffix)) {
278 if (!first) {
279 builder.append(' ');
280 }
281 builder.append(suffix);
282 }
283 return builder.toString();
284 }
285
Daisuke Miyakawaa2ba9a12009-09-29 12:43:19 -0700286 public static boolean containsOnlyPrintableAscii(String str) {
Daisuke Miyakawaf4ddea72009-08-23 10:01:32 +0900287 if (TextUtils.isEmpty(str)) {
288 return true;
289 }
290
291 final int length = str.length();
292 final int asciiFirst = 0x20;
293 final int asciiLast = 0x126;
294 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
295 int c = str.codePointAt(i);
296 if (c < asciiFirst || asciiLast < c) {
297 return false;
298 }
299 }
300 return true;
301 }
Daisuke Miyakawaa2ba9a12009-09-29 12:43:19 -0700302
303 /**
304 * This is useful when checking the string should be encoded into quoted-printable
305 * or not, which is required by vCard 2.1.
306 * See the definition of "7bit" in vCard 2.1 spec for more information.
307 */
308 public static boolean containsOnlyNonCrLfPrintableAscii(String str) {
309 if (TextUtils.isEmpty(str)) {
310 return true;
311 }
312
313 final int length = str.length();
314 final int asciiFirst = 0x20;
315 final int asciiLast = 0x126;
316 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
317 int c = str.codePointAt(i);
318 if (c < asciiFirst || asciiLast < c || c == '\n' || c == '\r') {
319 return false;
320 }
321 }
322 return true;
323 }
324
Daisuke Miyakawaf4ddea72009-08-23 10:01:32 +0900325 /**
326 * This is useful since vCard 3.0 often requires the ("X-") properties and groups
327 * should contain only alphabets, digits, and hyphen.
328 *
329 * Note: It is already known some devices (wrongly) outputs properties with characters
Daisuke Miyakawaa2ba9a12009-09-29 12:43:19 -0700330 * which should not be in the field. One example is "X-GOOGLE TALK". We accept
Daisuke Miyakawaf4ddea72009-08-23 10:01:32 +0900331 * such kind of input but must never output it unless the target is very specific
332 * to the device which is able to parse the malformed input.
333 */
334 public static boolean containsOnlyAlphaDigitHyphen(String str) {
335 if (TextUtils.isEmpty(str)) {
336 return true;
337 }
338
339 final int lowerAlphabetFirst = 0x41; // included ('A')
340 final int lowerAlphabetLast = 0x5b; // not included ('[')
341 final int upperAlphabetFirst = 0x61; // included ('a')
342 final int upperAlphabetLast = 0x7b; // included ('{')
343 final int digitFirst = 0x30; // included ('0')
344 final int digitLast = 0x39; // included ('9')
345 final int hyphen = '-';
346 final int length = str.length();
347 for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
348 int codepoint = str.codePointAt(i);
349 if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetLast) ||
350 (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetLast) ||
351 (digitFirst <= codepoint && codepoint < digitLast) ||
352 (codepoint == hyphen))) {
353 return false;
354 }
355 }
356 return true;
357 }
358
359 // TODO: Replace wth the method in Base64 class.
360 private static char PAD = '=';
361 private static final char[] ENCODE64 = {
362 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
363 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
364 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
365 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
366 };
367
368 static public String encodeBase64(byte[] data) {
369 if (data == null) {
370 return "";
371 }
372
373 char[] charBuffer = new char[(data.length + 2) / 3 * 4];
374 int position = 0;
375 int _3byte = 0;
376 for (int i=0; i<data.length-2; i+=3) {
377 _3byte = ((data[i] & 0xFF) << 16) + ((data[i+1] & 0xFF) << 8) + (data[i+2] & 0xFF);
378 charBuffer[position++] = ENCODE64[_3byte >> 18];
379 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
380 charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
381 charBuffer[position++] = ENCODE64[_3byte & 0x3F];
382 }
383 switch(data.length % 3) {
384 case 1: // [111111][11 0000][0000 00][000000]
385 _3byte = ((data[data.length-1] & 0xFF) << 16);
386 charBuffer[position++] = ENCODE64[_3byte >> 18];
387 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
388 charBuffer[position++] = PAD;
389 charBuffer[position++] = PAD;
390 break;
391 case 2: // [111111][11 1111][1111 00][000000]
392 _3byte = ((data[data.length-2] & 0xFF) << 16) + ((data[data.length-1] & 0xFF) << 8);
393 charBuffer[position++] = ENCODE64[_3byte >> 18];
394 charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
395 charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
396 charBuffer[position++] = PAD;
397 break;
398 }
399
400 return new String(charBuffer);
401 }
402
403 static public String toHalfWidthString(String orgString) {
404 if (TextUtils.isEmpty(orgString)) {
405 return null;
406 }
407 StringBuilder builder = new StringBuilder();
408 int length = orgString.length();
409 for (int i = 0; i < length; i++) {
410 // All Japanese character is able to be expressed by char.
411 // Do not need to use String#codepPointAt().
412 char ch = orgString.charAt(i);
413 CharSequence halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
414 if (halfWidthText != null) {
415 builder.append(halfWidthText);
416 } else {
417 builder.append(ch);
418 }
419 }
420 return builder.toString();
421 }
422
423 private VCardUtils() {
424 }
425}
426
427/**
428 * TextUtils especially for Japanese.
429 * TODO: make this in android.text in the future
430 */
431class JapaneseUtils {
432 static private final Map<Character, String> sHalfWidthMap =
433 new HashMap<Character, String>();
434
435 static {
436 // There's no logical mapping rule in Unicode. Sigh.
437 sHalfWidthMap.put('\u3001', "\uFF64");
438 sHalfWidthMap.put('\u3002', "\uFF61");
439 sHalfWidthMap.put('\u300C', "\uFF62");
440 sHalfWidthMap.put('\u300D', "\uFF63");
441 sHalfWidthMap.put('\u301C', "~");
442 sHalfWidthMap.put('\u3041', "\uFF67");
443 sHalfWidthMap.put('\u3042', "\uFF71");
444 sHalfWidthMap.put('\u3043', "\uFF68");
445 sHalfWidthMap.put('\u3044', "\uFF72");
446 sHalfWidthMap.put('\u3045', "\uFF69");
447 sHalfWidthMap.put('\u3046', "\uFF73");
448 sHalfWidthMap.put('\u3047', "\uFF6A");
449 sHalfWidthMap.put('\u3048', "\uFF74");
450 sHalfWidthMap.put('\u3049', "\uFF6B");
451 sHalfWidthMap.put('\u304A', "\uFF75");
452 sHalfWidthMap.put('\u304B', "\uFF76");
453 sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
454 sHalfWidthMap.put('\u304D', "\uFF77");
455 sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
456 sHalfWidthMap.put('\u304F', "\uFF78");
457 sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
458 sHalfWidthMap.put('\u3051', "\uFF79");
459 sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
460 sHalfWidthMap.put('\u3053', "\uFF7A");
461 sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
462 sHalfWidthMap.put('\u3055', "\uFF7B");
463 sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
464 sHalfWidthMap.put('\u3057', "\uFF7C");
465 sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
466 sHalfWidthMap.put('\u3059', "\uFF7D");
467 sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
468 sHalfWidthMap.put('\u305B', "\uFF7E");
469 sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
470 sHalfWidthMap.put('\u305D', "\uFF7F");
471 sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
472 sHalfWidthMap.put('\u305F', "\uFF80");
473 sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
474 sHalfWidthMap.put('\u3061', "\uFF81");
475 sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
476 sHalfWidthMap.put('\u3063', "\uFF6F");
477 sHalfWidthMap.put('\u3064', "\uFF82");
478 sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
479 sHalfWidthMap.put('\u3066', "\uFF83");
480 sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
481 sHalfWidthMap.put('\u3068', "\uFF84");
482 sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
483 sHalfWidthMap.put('\u306A', "\uFF85");
484 sHalfWidthMap.put('\u306B', "\uFF86");
485 sHalfWidthMap.put('\u306C', "\uFF87");
486 sHalfWidthMap.put('\u306D', "\uFF88");
487 sHalfWidthMap.put('\u306E', "\uFF89");
488 sHalfWidthMap.put('\u306F', "\uFF8A");
489 sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
490 sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
491 sHalfWidthMap.put('\u3072', "\uFF8B");
492 sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
493 sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
494 sHalfWidthMap.put('\u3075', "\uFF8C");
495 sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
496 sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
497 sHalfWidthMap.put('\u3078', "\uFF8D");
498 sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
499 sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
500 sHalfWidthMap.put('\u307B', "\uFF8E");
501 sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
502 sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
503 sHalfWidthMap.put('\u307E', "\uFF8F");
504 sHalfWidthMap.put('\u307F', "\uFF90");
505 sHalfWidthMap.put('\u3080', "\uFF91");
506 sHalfWidthMap.put('\u3081', "\uFF92");
507 sHalfWidthMap.put('\u3082', "\uFF93");
508 sHalfWidthMap.put('\u3083', "\uFF6C");
509 sHalfWidthMap.put('\u3084', "\uFF94");
510 sHalfWidthMap.put('\u3085', "\uFF6D");
511 sHalfWidthMap.put('\u3086', "\uFF95");
512 sHalfWidthMap.put('\u3087', "\uFF6E");
513 sHalfWidthMap.put('\u3088', "\uFF96");
514 sHalfWidthMap.put('\u3089', "\uFF97");
515 sHalfWidthMap.put('\u308A', "\uFF98");
516 sHalfWidthMap.put('\u308B', "\uFF99");
517 sHalfWidthMap.put('\u308C', "\uFF9A");
518 sHalfWidthMap.put('\u308D', "\uFF9B");
519 sHalfWidthMap.put('\u308E', "\uFF9C");
520 sHalfWidthMap.put('\u308F', "\uFF9C");
521 sHalfWidthMap.put('\u3090', "\uFF72");
522 sHalfWidthMap.put('\u3091', "\uFF74");
523 sHalfWidthMap.put('\u3092', "\uFF66");
524 sHalfWidthMap.put('\u3093', "\uFF9D");
525 sHalfWidthMap.put('\u309B', "\uFF9E");
526 sHalfWidthMap.put('\u309C', "\uFF9F");
527 sHalfWidthMap.put('\u30A1', "\uFF67");
528 sHalfWidthMap.put('\u30A2', "\uFF71");
529 sHalfWidthMap.put('\u30A3', "\uFF68");
530 sHalfWidthMap.put('\u30A4', "\uFF72");
531 sHalfWidthMap.put('\u30A5', "\uFF69");
532 sHalfWidthMap.put('\u30A6', "\uFF73");
533 sHalfWidthMap.put('\u30A7', "\uFF6A");
534 sHalfWidthMap.put('\u30A8', "\uFF74");
535 sHalfWidthMap.put('\u30A9', "\uFF6B");
536 sHalfWidthMap.put('\u30AA', "\uFF75");
537 sHalfWidthMap.put('\u30AB', "\uFF76");
538 sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
539 sHalfWidthMap.put('\u30AD', "\uFF77");
540 sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
541 sHalfWidthMap.put('\u30AF', "\uFF78");
542 sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
543 sHalfWidthMap.put('\u30B1', "\uFF79");
544 sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
545 sHalfWidthMap.put('\u30B3', "\uFF7A");
546 sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
547 sHalfWidthMap.put('\u30B5', "\uFF7B");
548 sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
549 sHalfWidthMap.put('\u30B7', "\uFF7C");
550 sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
551 sHalfWidthMap.put('\u30B9', "\uFF7D");
552 sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
553 sHalfWidthMap.put('\u30BB', "\uFF7E");
554 sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
555 sHalfWidthMap.put('\u30BD', "\uFF7F");
556 sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
557 sHalfWidthMap.put('\u30BF', "\uFF80");
558 sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
559 sHalfWidthMap.put('\u30C1', "\uFF81");
560 sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
561 sHalfWidthMap.put('\u30C3', "\uFF6F");
562 sHalfWidthMap.put('\u30C4', "\uFF82");
563 sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
564 sHalfWidthMap.put('\u30C6', "\uFF83");
565 sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
566 sHalfWidthMap.put('\u30C8', "\uFF84");
567 sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
568 sHalfWidthMap.put('\u30CA', "\uFF85");
569 sHalfWidthMap.put('\u30CB', "\uFF86");
570 sHalfWidthMap.put('\u30CC', "\uFF87");
571 sHalfWidthMap.put('\u30CD', "\uFF88");
572 sHalfWidthMap.put('\u30CE', "\uFF89");
573 sHalfWidthMap.put('\u30CF', "\uFF8A");
574 sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
575 sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
576 sHalfWidthMap.put('\u30D2', "\uFF8B");
577 sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
578 sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
579 sHalfWidthMap.put('\u30D5', "\uFF8C");
580 sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
581 sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
582 sHalfWidthMap.put('\u30D8', "\uFF8D");
583 sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
584 sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
585 sHalfWidthMap.put('\u30DB', "\uFF8E");
586 sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
587 sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
588 sHalfWidthMap.put('\u30DE', "\uFF8F");
589 sHalfWidthMap.put('\u30DF', "\uFF90");
590 sHalfWidthMap.put('\u30E0', "\uFF91");
591 sHalfWidthMap.put('\u30E1', "\uFF92");
592 sHalfWidthMap.put('\u30E2', "\uFF93");
593 sHalfWidthMap.put('\u30E3', "\uFF6C");
594 sHalfWidthMap.put('\u30E4', "\uFF94");
595 sHalfWidthMap.put('\u30E5', "\uFF6D");
596 sHalfWidthMap.put('\u30E6', "\uFF95");
597 sHalfWidthMap.put('\u30E7', "\uFF6E");
598 sHalfWidthMap.put('\u30E8', "\uFF96");
599 sHalfWidthMap.put('\u30E9', "\uFF97");
600 sHalfWidthMap.put('\u30EA', "\uFF98");
601 sHalfWidthMap.put('\u30EB', "\uFF99");
602 sHalfWidthMap.put('\u30EC', "\uFF9A");
603 sHalfWidthMap.put('\u30ED', "\uFF9B");
604 sHalfWidthMap.put('\u30EE', "\uFF9C");
605 sHalfWidthMap.put('\u30EF', "\uFF9C");
606 sHalfWidthMap.put('\u30F0', "\uFF72");
607 sHalfWidthMap.put('\u30F1', "\uFF74");
608 sHalfWidthMap.put('\u30F2', "\uFF66");
609 sHalfWidthMap.put('\u30F3', "\uFF9D");
610 sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
611 sHalfWidthMap.put('\u30F5', "\uFF76");
612 sHalfWidthMap.put('\u30F6', "\uFF79");
613 sHalfWidthMap.put('\u30FB', "\uFF65");
614 sHalfWidthMap.put('\u30FC', "\uFF70");
615 sHalfWidthMap.put('\uFF01', "!");
616 sHalfWidthMap.put('\uFF02', "\"");
617 sHalfWidthMap.put('\uFF03', "#");
618 sHalfWidthMap.put('\uFF04', "$");
619 sHalfWidthMap.put('\uFF05', "%");
620 sHalfWidthMap.put('\uFF06', "&");
621 sHalfWidthMap.put('\uFF07', "'");
622 sHalfWidthMap.put('\uFF08', "(");
623 sHalfWidthMap.put('\uFF09', ")");
624 sHalfWidthMap.put('\uFF0A', "*");
625 sHalfWidthMap.put('\uFF0B', "+");
626 sHalfWidthMap.put('\uFF0C', ",");
627 sHalfWidthMap.put('\uFF0D', "-");
628 sHalfWidthMap.put('\uFF0E', ".");
629 sHalfWidthMap.put('\uFF0F', "/");
630 sHalfWidthMap.put('\uFF10', "0");
631 sHalfWidthMap.put('\uFF11', "1");
632 sHalfWidthMap.put('\uFF12', "2");
633 sHalfWidthMap.put('\uFF13', "3");
634 sHalfWidthMap.put('\uFF14', "4");
635 sHalfWidthMap.put('\uFF15', "5");
636 sHalfWidthMap.put('\uFF16', "6");
637 sHalfWidthMap.put('\uFF17', "7");
638 sHalfWidthMap.put('\uFF18', "8");
639 sHalfWidthMap.put('\uFF19', "9");
640 sHalfWidthMap.put('\uFF1A', ":");
641 sHalfWidthMap.put('\uFF1B', ";");
642 sHalfWidthMap.put('\uFF1C', "<");
643 sHalfWidthMap.put('\uFF1D', "=");
644 sHalfWidthMap.put('\uFF1E', ">");
645 sHalfWidthMap.put('\uFF1F', "?");
646 sHalfWidthMap.put('\uFF20', "@");
647 sHalfWidthMap.put('\uFF21', "A");
648 sHalfWidthMap.put('\uFF22', "B");
649 sHalfWidthMap.put('\uFF23', "C");
650 sHalfWidthMap.put('\uFF24', "D");
651 sHalfWidthMap.put('\uFF25', "E");
652 sHalfWidthMap.put('\uFF26', "F");
653 sHalfWidthMap.put('\uFF27', "G");
654 sHalfWidthMap.put('\uFF28', "H");
655 sHalfWidthMap.put('\uFF29', "I");
656 sHalfWidthMap.put('\uFF2A', "J");
657 sHalfWidthMap.put('\uFF2B', "K");
658 sHalfWidthMap.put('\uFF2C', "L");
659 sHalfWidthMap.put('\uFF2D', "M");
660 sHalfWidthMap.put('\uFF2E', "N");
661 sHalfWidthMap.put('\uFF2F', "O");
662 sHalfWidthMap.put('\uFF30', "P");
663 sHalfWidthMap.put('\uFF31', "Q");
664 sHalfWidthMap.put('\uFF32', "R");
665 sHalfWidthMap.put('\uFF33', "S");
666 sHalfWidthMap.put('\uFF34', "T");
667 sHalfWidthMap.put('\uFF35', "U");
668 sHalfWidthMap.put('\uFF36', "V");
669 sHalfWidthMap.put('\uFF37', "W");
670 sHalfWidthMap.put('\uFF38', "X");
671 sHalfWidthMap.put('\uFF39', "Y");
672 sHalfWidthMap.put('\uFF3A', "Z");
673 sHalfWidthMap.put('\uFF3B', "[");
674 sHalfWidthMap.put('\uFF3C', "\\");
675 sHalfWidthMap.put('\uFF3D', "]");
676 sHalfWidthMap.put('\uFF3E', "^");
677 sHalfWidthMap.put('\uFF3F', "_");
678 sHalfWidthMap.put('\uFF41', "a");
679 sHalfWidthMap.put('\uFF42', "b");
680 sHalfWidthMap.put('\uFF43', "c");
681 sHalfWidthMap.put('\uFF44', "d");
682 sHalfWidthMap.put('\uFF45', "e");
683 sHalfWidthMap.put('\uFF46', "f");
684 sHalfWidthMap.put('\uFF47', "g");
685 sHalfWidthMap.put('\uFF48', "h");
686 sHalfWidthMap.put('\uFF49', "i");
687 sHalfWidthMap.put('\uFF4A', "j");
688 sHalfWidthMap.put('\uFF4B', "k");
689 sHalfWidthMap.put('\uFF4C', "l");
690 sHalfWidthMap.put('\uFF4D', "m");
691 sHalfWidthMap.put('\uFF4E', "n");
692 sHalfWidthMap.put('\uFF4F', "o");
693 sHalfWidthMap.put('\uFF50', "p");
694 sHalfWidthMap.put('\uFF51', "q");
695 sHalfWidthMap.put('\uFF52', "r");
696 sHalfWidthMap.put('\uFF53', "s");
697 sHalfWidthMap.put('\uFF54', "t");
698 sHalfWidthMap.put('\uFF55', "u");
699 sHalfWidthMap.put('\uFF56', "v");
700 sHalfWidthMap.put('\uFF57', "w");
701 sHalfWidthMap.put('\uFF58', "x");
702 sHalfWidthMap.put('\uFF59', "y");
703 sHalfWidthMap.put('\uFF5A', "z");
704 sHalfWidthMap.put('\uFF5B', "{");
705 sHalfWidthMap.put('\uFF5C', "|");
706 sHalfWidthMap.put('\uFF5D', "}");
707 sHalfWidthMap.put('\uFF5E', "~");
708 sHalfWidthMap.put('\uFF61', "\uFF61");
709 sHalfWidthMap.put('\uFF62', "\uFF62");
710 sHalfWidthMap.put('\uFF63', "\uFF63");
711 sHalfWidthMap.put('\uFF64', "\uFF64");
712 sHalfWidthMap.put('\uFF65', "\uFF65");
713 sHalfWidthMap.put('\uFF66', "\uFF66");
714 sHalfWidthMap.put('\uFF67', "\uFF67");
715 sHalfWidthMap.put('\uFF68', "\uFF68");
716 sHalfWidthMap.put('\uFF69', "\uFF69");
717 sHalfWidthMap.put('\uFF6A', "\uFF6A");
718 sHalfWidthMap.put('\uFF6B', "\uFF6B");
719 sHalfWidthMap.put('\uFF6C', "\uFF6C");
720 sHalfWidthMap.put('\uFF6D', "\uFF6D");
721 sHalfWidthMap.put('\uFF6E', "\uFF6E");
722 sHalfWidthMap.put('\uFF6F', "\uFF6F");
723 sHalfWidthMap.put('\uFF70', "\uFF70");
724 sHalfWidthMap.put('\uFF71', "\uFF71");
725 sHalfWidthMap.put('\uFF72', "\uFF72");
726 sHalfWidthMap.put('\uFF73', "\uFF73");
727 sHalfWidthMap.put('\uFF74', "\uFF74");
728 sHalfWidthMap.put('\uFF75', "\uFF75");
729 sHalfWidthMap.put('\uFF76', "\uFF76");
730 sHalfWidthMap.put('\uFF77', "\uFF77");
731 sHalfWidthMap.put('\uFF78', "\uFF78");
732 sHalfWidthMap.put('\uFF79', "\uFF79");
733 sHalfWidthMap.put('\uFF7A', "\uFF7A");
734 sHalfWidthMap.put('\uFF7B', "\uFF7B");
735 sHalfWidthMap.put('\uFF7C', "\uFF7C");
736 sHalfWidthMap.put('\uFF7D', "\uFF7D");
737 sHalfWidthMap.put('\uFF7E', "\uFF7E");
738 sHalfWidthMap.put('\uFF7F', "\uFF7F");
739 sHalfWidthMap.put('\uFF80', "\uFF80");
740 sHalfWidthMap.put('\uFF81', "\uFF81");
741 sHalfWidthMap.put('\uFF82', "\uFF82");
742 sHalfWidthMap.put('\uFF83', "\uFF83");
743 sHalfWidthMap.put('\uFF84', "\uFF84");
744 sHalfWidthMap.put('\uFF85', "\uFF85");
745 sHalfWidthMap.put('\uFF86', "\uFF86");
746 sHalfWidthMap.put('\uFF87', "\uFF87");
747 sHalfWidthMap.put('\uFF88', "\uFF88");
748 sHalfWidthMap.put('\uFF89', "\uFF89");
749 sHalfWidthMap.put('\uFF8A', "\uFF8A");
750 sHalfWidthMap.put('\uFF8B', "\uFF8B");
751 sHalfWidthMap.put('\uFF8C', "\uFF8C");
752 sHalfWidthMap.put('\uFF8D', "\uFF8D");
753 sHalfWidthMap.put('\uFF8E', "\uFF8E");
754 sHalfWidthMap.put('\uFF8F', "\uFF8F");
755 sHalfWidthMap.put('\uFF90', "\uFF90");
756 sHalfWidthMap.put('\uFF91', "\uFF91");
757 sHalfWidthMap.put('\uFF92', "\uFF92");
758 sHalfWidthMap.put('\uFF93', "\uFF93");
759 sHalfWidthMap.put('\uFF94', "\uFF94");
760 sHalfWidthMap.put('\uFF95', "\uFF95");
761 sHalfWidthMap.put('\uFF96', "\uFF96");
762 sHalfWidthMap.put('\uFF97', "\uFF97");
763 sHalfWidthMap.put('\uFF98', "\uFF98");
764 sHalfWidthMap.put('\uFF99', "\uFF99");
765 sHalfWidthMap.put('\uFF9A', "\uFF9A");
766 sHalfWidthMap.put('\uFF9B', "\uFF9B");
767 sHalfWidthMap.put('\uFF9C', "\uFF9C");
768 sHalfWidthMap.put('\uFF9D', "\uFF9D");
769 sHalfWidthMap.put('\uFF9E', "\uFF9E");
770 sHalfWidthMap.put('\uFF9F', "\uFF9F");
771 sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
772 }
773
774 /**
775 * Return half-width version of that character if possible. Return null if not possible
776 * @param ch input character
777 * @return CharSequence object if the mapping for ch exists. Return null otherwise.
778 */
779 public static CharSequence tryGetHalfWidthText(char ch) {
780 if (sHalfWidthMap.containsKey(ch)) {
781 return sHalfWidthMap.get(ch);
782 } else {
783 return null;
784 }
785 }
786}