VCardUtils.java revision be378d5b188f51cf717e5309e3c39180e85833a8
14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/*
24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project
34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License.
64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at
74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa *
104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and
144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License.
154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard;
174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
18be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
19be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport org.apache.commons.codec.DecoderException;
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport org.apache.commons.codec.net.QuotedPrintableCodec;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.ContentProviderOperation;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.Data;
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Im;
264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone;
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.telephony.PhoneNumberUtils;
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.text.TextUtils;
304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log;
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.UnsupportedEncodingException;
334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList;
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Arrays;
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Collection;
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashMap;
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.List;
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Map;
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Utilities for VCard handling codes.
444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class VCardUtils {
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final String LOG_TAG = "VCardUtils";
474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // converted to two parameter Strings. These only contain some minor fields valid in both
504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // vCard and current (as of 2009-08-07) Contacts structure.
514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<String> sPhoneTypesUnknownToContactsSet;
534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<String> sMobilePhoneLabelSet;
564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static {
584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR);
624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR);
634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER);
644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER);
654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN);
664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN);
674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME);
694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK);
704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE);
714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK,
744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_CALLBACK);
754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(
764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD,
794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_TTY_TDD);
804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT,
814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_ASSISTANT);
824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet = new HashSet<String>();
844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM);
854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG);
864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS);
874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO);
884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK,
954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                VCardConstants.PROPERTY_X_GOOGLE_TALK);
964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ);
994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING);
1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone)
1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone)
1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone)
1044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone)
1054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList(
1064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4",
1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                "\uFF79\uFF72\uFF80\uFF72"));
1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String getPhoneTypeString(Integer type) {
1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sKnownPhoneTypesMap_ItoS.get(type);
1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns Interger when the given types can be parsed as known type. Returns String object
1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * when not, which should be set to label.
1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static Object getPhoneTypeFromStrings(Collection<String> types,
1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String number) {
1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (number == null) {
1214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            number = "";
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int type = -1;
1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String label = null;
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean isFax = false;
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean hasPref = false;
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (types != null) {
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (String typeString : types) {
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (typeString == null) {
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    continue;
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                typeString = typeString.toUpperCase();
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    hasPref = true;
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) {
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    isFax = true;
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (typeString.startsWith("X-") && type < 0) {
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        typeString = typeString.substring(2);
1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (typeString.length() == 0) {
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        continue;
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (tmp != null) {
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final int typeCandidate = tmp;
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // TYPE_PAGER is prefered when the number contains @ surronded by
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // a pager number and a domain name.
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // e.g.
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // o 1111@domain.com
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // x @domain.com
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // x 1111@
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final int indexOfAt = number.indexOf("@");
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if ((typeCandidate == Phone.TYPE_PAGER
1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                && 0 < indexOfAt && indexOfAt < number.length() - 1)
1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                || type < 0
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                || type == Phone.TYPE_CUSTOM) {
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            type = tmp;
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (type < 0) {
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        type = Phone.TYPE_CUSTOM;
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        label = typeString;
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (type < 0) {
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (hasPref) {
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_MAIN;
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // default to TYPE_HOME
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_HOME;
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (isFax) {
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (type == Phone.TYPE_HOME) {
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_FAX_HOME;
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (type == Phone.TYPE_WORK) {
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_FAX_WORK;
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (type == Phone.TYPE_OTHER) {
1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_OTHER_FAX;
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (type == Phone.TYPE_CUSTOM) {
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return label;
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return type;
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @SuppressWarnings("deprecation")
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isMobilePhoneLabel(final String label) {
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // For backward compatibility.
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //         To support mobile type at that time, this custom label had been used.
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sPhoneTypesUnknownToContactsSet.contains(label);
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String getPropertyNameForIm(final int protocol) {
2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sKnownImPropNameMap_ItoS.get(protocol);
2064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String[] sortNameElements(final int vcardType,
2094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName) {
2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] list = new String[3];
2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int nameOrderType = VCardConfig.getNameOrderType(vcardType);
2124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        switch (nameOrderType) {
2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case VCardConfig.NAME_ORDER_JAPANESE: {
2144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (containsOnlyPrintableAscii(familyName) &&
2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        containsOnlyPrintableAscii(givenName)) {
2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[0] = givenName;
2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[1] = middleName;
2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[2] = familyName;
2194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[0] = familyName;
2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[1] = middleName;
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[2] = givenName;
2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case VCardConfig.NAME_ORDER_EUROPE: {
2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[0] = middleName;
2284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[1] = givenName;
2294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[2] = familyName;
2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            default: {
2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[0] = givenName;
2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[1] = middleName;
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[2] = familyName;
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return list;
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static int getPhoneNumberFormat(final int vcardType) {
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (VCardConfig.isJapaneseDevice(vcardType)) {
2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return PhoneNumberUtils.FORMAT_JAPAN;
2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return PhoneNumberUtils.FORMAT_NANP;
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
2514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Inserts postal data into the builder object.
2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note that the data structure of ContactsContract is different from that defined in vCard.
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * So some conversion may be performed in this method.
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final ContentProviderOperation.Builder builder,
2614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final VCardEntry.PostalData postalData) {
2624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.TYPE, postalData.type);
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.withValue(StructuredPostal.LABEL, postalData.label);
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String streetString;
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(postalData.street)) {
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(postalData.extendedAddress)) {
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                streetString = null;
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                streetString = postalData.extendedAddress;
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(postalData.extendedAddress)) {
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                streetString = postalData.street;
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                streetString = postalData.street + " " + postalData.extendedAddress;
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.POBOX, postalData.pobox);
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.STREET, streetString);
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.CITY, postalData.localty);
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.REGION, postalData.region);
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.COUNTRY, postalData.country);
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                postalData.getFormattedAddress(vcardType));
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (postalData.isPrimary) {
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.withValue(Data.IS_PRIMARY, 1);
2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String constructNameFromElements(final int vcardType,
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName) {
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return constructNameFromElements(vcardType, familyName, middleName, givenName,
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                null, null);
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String constructNameFromElements(final int vcardType,
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName,
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String prefix, final String suffix) {
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName);
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean first = true;
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!TextUtils.isEmpty(prefix)) {
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            first = false;
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(prefix);
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String namePart : nameList) {
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!TextUtils.isEmpty(namePart)) {
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (first) {
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    first = false;
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(' ');
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(namePart);
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!TextUtils.isEmpty(suffix)) {
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!first) {
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(' ');
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(suffix);
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static List<String> constructListFromValue(final String value,
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final boolean isV30) {
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final List<String> list = new ArrayList<String>();
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int length = value.length();
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = value.charAt(i);
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char nextCh = value.charAt(i + 1);
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final String unescapedString =
343be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa                    (isV30 ? VCardParserImpl_V30.escapeCharacter(nextCh) :
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        VCardParserImpl_V21.unescapeCharacter(nextCh));
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (unescapedString != null) {
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(unescapedString);
3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    i++;
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(ch);
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (ch == ';') {
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list.add(builder.toString());
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder = new StringBuilder();
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        list.add(builder.toString());
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return list;
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyPrintableAscii(final String...values) {
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyPrintableAscii(Arrays.asList(values));
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyPrintableAscii(final Collection<String> values) {
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(value)) {
3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!TextUtils.isPrintableAsciiOnly(value)) {
3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * This is useful when checking the string should be encoded into quoted-printable
3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * or not, which is required by vCard 2.1.
3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * See the definition of "7bit" in vCard 2.1 spec for more information.
3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
3934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
3944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
3954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values));
3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) {
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiFirst = 0x20;
4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(value)) {
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = value.length();
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final int c = value.codePointAt(i);
4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!(asciiFirst <= c && c <= asciiLast)) {
4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<Character> sUnAcceptableAsciiInV21WordSet =
4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * This is useful since vCard 3.0 often requires the ("X-") properties and groups
4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * should contain only alphabets, digits, and hyphen.
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: It is already known some devices (wrongly) outputs properties with characters
4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       which should not be in the field. One example is "X-GOOGLE TALK". We accept
4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       such kind of input but must never output it unless the target is very specific
4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       to the device which is able to parse the malformed input.
4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyAlphaDigitHyphen(Arrays.asList(values));
4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) {
4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int upperAlphabetFirst = 0x41;  // A
4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int upperAlphabetAfterLast = 0x5b;  // [
4494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int lowerAlphabetFirst = 0x61;  // a
4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int lowerAlphabetAfterLast = 0x7b;  // {
4514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int digitFirst = 0x30;  // 0
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int digitAfterLast = 0x3A;  // :
4534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int hyphen = '-';
4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String str : values) {
4554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(str)) {
4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
4574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = str.length();
4594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                int codepoint = str.codePointAt(i);
4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) ||
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) ||
4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (digitFirst <= codepoint && codepoint < digitAfterLast) ||
4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (codepoint == hyphen))) {
4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specifies:<br />
4784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * word = &lt;any printable 7bit us-ascii except []=:., &gt;
4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isV21Word(final String value) {
4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(value)) {
4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiFirst = 0x20;
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = value.length();
4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int c = value.codePointAt(i);
4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!(asciiFirst <= c && c <= asciiLast) ||
4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String toHalfWidthString(final String orgString) {
4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(orgString)) {
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = orgString.length();
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) {
5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // All Japanese character is able to be expressed by char.
5064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Do not need to use String#codepPointAt().
5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final char ch = orgString.charAt(i);
5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (halfWidthText != null) {
5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(halfWidthText);
5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Guesses the format of input image. Currently just the first few bytes are used.
5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when
5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * the guess failed.
5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param input Image as byte array.
5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return The image type or null when the type cannot be determined.
5244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String guessImageType(final byte[] input) {
5264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (input == null) {
5274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') {
5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "GIF";
5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (input.length >= 4 && input[0] == (byte) 0x89
5324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') {
5334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Note: vCard 2.1 officially does not support PNG, but we may have it and
5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            //       using X- word like "X-PNG" may not let importers know it is PNG.
5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            //       So we use the String "PNG" as is...
5364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "PNG";
5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (input.length >= 2 && input[0] == (byte) 0xff
5384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && input[1] == (byte) 0xd8) {
5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "JPEG";
5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
5424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return True when all the given values are null or empty Strings.
5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean areAllEmpty(final String...values) {
5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!TextUtils.isEmpty(value)) {
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    //// The methods bellow may be used by unit test.
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
56458610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa     * Unquotes given Quoted-Printable value. value must not be null.
5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
56658610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa    public static String parseQuotedPrintable(
56758610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa            final String value, boolean strictLineBreaking,
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String sourceCharset, String targetCharset) {
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // "= " -> " ", "=\t" -> "\t".
5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Previous code had done this replacement. Keep on the safe side.
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String quotedPrintable;
5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        {
5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final StringBuilder builder = new StringBuilder();
5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = value.length();
5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i++) {
5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char ch = value.charAt(i);
5774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (ch == '=' && i < length - 1) {
5784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    char nextCh = value.charAt(i + 1);
5794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (nextCh == ' ' || nextCh == '\t') {
5804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        builder.append(nextCh);
5814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        i++;
5824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        continue;
5834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
5844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
5864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            quotedPrintable = builder.toString();
5884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] lines;
5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strictLineBreaking) {
5924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            lines = quotedPrintable.split("\r\n");
5934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
5944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            StringBuilder builder = new StringBuilder();
5954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = quotedPrintable.length();
5964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            ArrayList<String> list = new ArrayList<String>();
5974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i++) {
5984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char ch = quotedPrintable.charAt(i);
5994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (ch == '\n') {
6004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list.add(builder.toString());
6014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
6024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (ch == '\r') {
6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list.add(builder.toString());
6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (i < length - 1) {
6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        char nextCh = quotedPrintable.charAt(i + 1);
6074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (nextCh == '\n') {
6084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            i++;
6094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(ch);
6134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
6144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String lastLine = builder.toString();
6164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (lastLine.length() > 0) {
6174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list.add(lastLine);
6184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            lines = list.toArray(new String[0]);
6204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (String line : lines) {
6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.endsWith("=")) {
6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = line.substring(0, line.length() - 1);
6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
6284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String rawString = builder.toString();
6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(rawString)) {
6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Given raw string is empty.");
6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        byte[] rawBytes = null;
6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            rawBytes = rawString.getBytes(sourceCharset);
6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (UnsupportedEncodingException e) {
6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Failed to decode: " + sourceCharset);
6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            rawBytes = rawString.getBytes();
6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        byte[] decodedBytes = null;
6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes);
6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (DecoderException e) {
6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "DecoderException is thrown.");
6484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            decodedBytes = rawBytes;
6494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return new String(decodedBytes, targetCharset);
6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (UnsupportedEncodingException e) {
6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return new String(decodedBytes);
6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
659be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    public static final VCardParser getAppropriateParser(int vcardType)
660be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            throws VCardException {
661be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        if (VCardConfig.isVersion21(vcardType)) {
662be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V21();
663be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else if (VCardConfig.isVersion30(vcardType)) {
664be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V30();
665be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else if (VCardConfig.isVersion40(vcardType)) {
666be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V40();
667be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else {
668be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            throw new VCardException("Version is not specified");
669be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        }
670be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    }
671be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
672be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    // TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean
673be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
6744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private VCardUtils() {
6754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
677