13f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/*
23f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Copyright (C) 2015 The Android Open Source Project
33f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
43f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Licensed under the Apache License, Version 2.0 (the "License");
53f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * you may not use this file except in compliance with the License.
63f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * You may obtain a copy of the License at
73f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
83f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *      http://www.apache.org/licenses/LICENSE-2.0
93f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Unless required by applicable law or agreed to in writing, software
113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * distributed under the License is distributed on an "AS IS" BASIS,
123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * See the License for the specific language governing permissions and
143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * limitations under the License.
153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */
163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpackage com.android.phone.common.mail;
173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.os.Parcel;
193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.os.Parcelable;
203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.Html;
213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.TextUtils;
223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.util.Rfc822Token;
233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.util.Rfc822Tokenizer;
243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.internal.annotations.VisibleForTesting;
263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.utils.LogUtils;
273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.codec.EncoderUtil;
293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.decoder.DecoderUtil;
303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.ArrayList;
323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.regex.Pattern;
333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/**
353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This class represent email address.
363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * RFC822 email address may have following format.
383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *   "name" <address> (comment)
393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *   "name" <address>
403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *   name <address>
413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *   address
423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Name and comment part should be MIME/base64 encoded in header if necessary.
433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */
453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpublic class Address implements Parcelable {
463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static final String ADDRESS_DELIMETER = ",";
473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *  Address part, in the form local_part@domain_part. No surrounding angle brackets.
493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private String mAddress;
513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Name part. No surrounding double quote, and no MIME/base64 encoding.
543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * This must be null if Address has no name part.
553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private String mPersonal;
573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * When personal is set, it will return the first token of the personal
603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * string. Otherwise, it will return the e-mail address up to the '@' sign.
613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private String mSimplifiedName;
633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$'
653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$");
663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$'
673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern REMOVE_OPTIONAL_DQUOTE = Pattern.compile("^\"?([^\"]*)\"?$");
683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // Regex that matches escaped character '\\([\\"])'
693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])");
703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // TODO: LOCAL_PART and DOMAIN_PART_PART are too permissive and can be improved.
723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // TODO: Fix this to better constrain comments.
733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /** Regex for the local part of an email address. */
743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final String LOCAL_PART = "[^@]+";
753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /** Regex for each part of the domain part, i.e. the thing between the dots. */
763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final String DOMAIN_PART_PART = "[[\\w][\\d]\\-\\(\\)\\[\\]]+";
773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /** Regex for the domain part, which is two or more {@link #DOMAIN_PART_PART} separated by . */
783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final String DOMAIN_PART =
793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            "(" + DOMAIN_PART_PART + "\\.)+" + DOMAIN_PART_PART;
803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /** Pattern to check if an email address is valid. */
823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern EMAIL_ADDRESS =
833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            Pattern.compile("\\A" + LOCAL_PART + "@" + DOMAIN_PART + "\\z");
843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0];
863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // delimiters are chars that do not appear in an email address, used by fromHeader
883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final char LIST_DELIMITER_EMAIL = '\1';
893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final char LIST_DELIMITER_PERSONAL = '\2';
903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final String LOG_TAG = "Email Address";
923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address(String address) {
953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setAddress(address);
963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address(String address, String personal) {
993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setPersonal(personal);
1003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setAddress(address);
1013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns a simplified string for this e-mail address.
1053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * When a name is known, it will return the first token of that name. Otherwise, it will
1063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * return the e-mail address up to the '@' sign.
1073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getSimplifiedName() {
1093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mSimplifiedName == null) {
1103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (TextUtils.isEmpty(mPersonal) && !TextUtils.isEmpty(mAddress)) {
1113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                int atSign = mAddress.indexOf('@');
1123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mSimplifiedName = (atSign != -1) ? mAddress.substring(0, atSign) : "";
1133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else if (!TextUtils.isEmpty(mPersonal)) {
1143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // TODO: use Contacts' NameSplitter for more reliable first-name extraction
1163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                int end = mPersonal.indexOf(' ');
1183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                while (end > 0 && mPersonal.charAt(end - 1) == ',') {
1193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    end--;
1203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                }
1213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mSimplifiedName = (end < 1) ? mPersonal : mPersonal.substring(0, end);
1223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
1243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                LogUtils.w(LOG_TAG, "Unable to get a simplified name");
1253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mSimplifiedName = "";
1263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
1273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mSimplifiedName;
1293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static synchronized Address getEmailAddress(String rawAddress) {
1323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (TextUtils.isEmpty(rawAddress)) {
1333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
1343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        String name, address;
1363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(rawAddress);
1373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (tokens.length > 0) {
1383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final String tokenizedName = tokens[0].getName();
1393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            name = tokenizedName != null ? Html.fromHtml(tokenizedName.trim()).toString()
1403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    : "";
1413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            address = Html.fromHtml(tokens[0].getAddress()).toString();
1423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
1433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            name = "";
1443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            address = rawAddress == null ?
1453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    "" : Html.fromHtml(rawAddress).toString();
1463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return new Address(address, name);
1483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getAddress() {
1513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mAddress;
1523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setAddress(String address) {
1553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mAddress = REMOVE_OPTIONAL_BRACKET.matcher(address).replaceAll("$1");
1563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding.
1603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
1613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return Name part of email address. Returns null if it is omitted.
1623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getPersonal() {
1643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mPersonal;
1653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Set personal part from UTF-16 string. Optional surrounding double quote will be removed.
1693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * It will be also unquoted and MIME/base64 decoded.
1703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
1713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param personal name part of email address as UTF-16 string. Null is acceptable.
1723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setPersonal(String personal) {
1743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mPersonal = decodeAddressPersonal(personal);
1753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Decodes name from UTF-16 string. Optional surrounding double quote will be removed.
1793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * It will be also unquoted and MIME/base64 decoded.
1803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
1813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param personal name part of email address as UTF-16 string. Null is acceptable.
1823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String decodeAddressPersonal(String personal) {
1843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (personal != null) {
1853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            personal = REMOVE_OPTIONAL_DQUOTE.matcher(personal).replaceAll("$1");
1863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            personal = UNQUOTE.matcher(personal).replaceAll("$1");
1873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            personal = DecoderUtil.decodeEncodedWords(personal);
1883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (personal.length() == 0) {
1893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                personal = null;
1903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
1913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return personal;
1933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * This method is used to check that all the addresses that the user
1973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * entered in a list (e.g. To:) are valid, so that none is dropped.
1983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
2003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static boolean isAllValid(String addressList) {
2013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // This code mimics the parse() method below.
2023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // I don't know how to better avoid the code-duplication.
2033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addressList != null && addressList.length() > 0) {
2043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
2053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            for (int i = 0, length = tokens.length; i < length; ++i) {
2063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                Rfc822Token token = tokens[i];
2073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                String address = token.getAddress();
2083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                if (!TextUtils.isEmpty(address) && !isValidAddress(address)) {
2093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    return false;
2103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                }
2113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return true;
2143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
2173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Parse a comma-delimited list of addresses in RFC822 format and return an
2183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * array of Address objects.
2193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
2203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addressList Address list in comma-delimited string.
2213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return An array of 0 or more Addresses.
2223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
2233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static Address[] parse(String addressList) {
2243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addressList == null || addressList.length() == 0) {
2253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return EMPTY_ADDRESS_ARRAY;
2263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
2283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        ArrayList<Address> addresses = new ArrayList<Address>();
2293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        for (int i = 0, length = tokens.length; i < length; ++i) {
2303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            Rfc822Token token = tokens[i];
2313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            String address = token.getAddress();
2323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (!TextUtils.isEmpty(address)) {
2333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                if (isValidAddress(address)) {
2343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    String name = token.getName();
2353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    if (TextUtils.isEmpty(name)) {
2363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                        name = null;
2373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    }
2383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    addresses.add(new Address(address, name));
2393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                }
2403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return addresses.toArray(new Address[addresses.size()]);
2433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
2463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Checks whether a string email address is valid.
2473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * E.g. name@domain.com is valid.
2483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
2493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
2503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    static boolean isValidAddress(final String address) {
2513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return EMAIL_ADDRESS.matcher(address).find();
2523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public boolean equals(Object o) {
2563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (o instanceof Address) {
2573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // It seems that the spec says that the "user" part is case-sensitive,
2583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // while the domain part in case-insesitive.
2593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // So foo@yahoo.com and Foo@yahoo.com are different.
2603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // This may seem non-intuitive from the user POV, so we
2613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // may re-consider it if it creates UI trouble.
2623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // A problem case is "replyAll" sending to both
2633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // a@b.c and to A@b.c, which turn out to be the same on the server.
2643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // Leave unchanged for now (i.e. case-sensitive).
2653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return getAddress().equals(((Address) o).getAddress());
2663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return super.equals(o);
2683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public int hashCode() {
2723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return getAddress().hashCode();
2733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
2763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get human readable address string.
2773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Do not use this for email header.
2783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
2793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return Human readable address string.  Not quoted and not encoded.
2803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
2813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String toString() {
2833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mPersonal != null && !mPersonal.equals(mAddress)) {
2843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
2853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                return ensureQuotedString(mPersonal) + " <" + mAddress + ">";
2863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
2873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                return mPersonal + " <" + mAddress + ">";
2883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
2903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mAddress;
2913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
2953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Ensures that the given string starts and ends with the double quote character. The string is
2963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * not modified in any way except to add the double quote character to start and end if it's not
2973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * already there.
2983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
2993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * sample -> "sample"
3003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * "sample" -> "sample"
3013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * ""sample"" -> "sample"
3023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * "sample"" -> "sample"
3033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * sa"mp"le -> "sa"mp"le"
3043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * "sa"mp"le" -> "sa"mp"le"
3053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * (empty string) -> ""
3063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * " -> ""
3073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static String ensureQuotedString(String s) {
3093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (s == null) {
3103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
3113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (!s.matches("^\".*\"$")) {
3133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return "\"" + s + "\"";
3143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
3153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return s;
3163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get human readable comma-delimited address string.
3213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
3223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addresses Address array
3233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return Human readable comma-delimited address string.
3243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
3263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String toString(Address[] addresses) {
3273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return toString(addresses, ADDRESS_DELIMETER);
3283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get human readable address strings joined with the specified separator.
3323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
3333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addresses Address array
3343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param separator Separator
3353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return Human readable comma-delimited address string.
3363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String toString(Address[] addresses, String separator) {
3383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses == null || addresses.length == 0) {
3393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
3403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses.length == 1) {
3423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return addresses[0].toString();
3433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        StringBuilder sb = new StringBuilder(addresses[0].toString());
3453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        for (int i = 1; i < addresses.length; i++) {
3463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(separator);
3473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // TODO: investigate why this .trim() is needed.
3483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(addresses[i].toString().trim());
3493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return sb.toString();
3513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get RFC822/MIME compatible address string.
3553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
3563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return RFC822/MIME compatible address string.
3573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary.
3583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String toHeader() {
3603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mPersonal != null) {
3613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return EncoderUtil.encodeAddressDisplayName(mPersonal) + " <" + mAddress + ">";
3623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
3633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mAddress;
3643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get RFC822/MIME compatible comma-delimited address string.
3693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
3703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addresses Address array
3713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return RFC822/MIME compatible comma-delimited address string.
3723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary.
3733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String toHeader(Address[] addresses) {
3753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses == null || addresses.length == 0) {
3763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
3773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses.length == 1) {
3793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return addresses[0].toHeader();
3803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        StringBuilder sb = new StringBuilder(addresses[0].toHeader());
3823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        for (int i = 1; i < addresses.length; i++) {
3833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // We need space character to be able to fold line.
3843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(", ");
3853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(addresses[i].toHeader());
3863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return sb.toString();
3883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get Human friendly address string.
3923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
3933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return the personal part of this Address, or the address part if the
3943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * personal part is not available
3953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
3973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String toFriendly() {
3983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mPersonal != null && mPersonal.length() > 0) {
3993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mPersonal;
4003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
4013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mAddress;
4023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for
4073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * details on the per-address conversion).
4083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
4093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addresses Array of Address[] values
4103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return A comma-delimited string listing all of the addresses supplied.  Null if source
4113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * was null or empty.
4123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
4143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String toFriendly(Address[] addresses) {
4153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses == null || addresses.length == 0) {
4163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
4173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addresses.length == 1) {
4193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return addresses[0].toFriendly();
4203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        StringBuilder sb = new StringBuilder(addresses[0].toFriendly());
4223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        for (int i = 1; i < addresses.length; i++) {
4233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(", ");
4243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(addresses[i].toFriendly());
4253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return sb.toString();
4273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns exactly the same result as Address.toString(Address.fromHeader(addressList)).
4313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
4333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String fromHeaderToString(String addressList) {
4343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return toString(fromHeader(addressList));
4353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns exactly the same result as Address.toHeader(Address.parse(addressList)).
4393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
4413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String parseToHeader(String addressList) {
4423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return Address.toHeader(Address.parse(addressList));
4433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns null if the addressList has 0 addresses, otherwise returns the first address.
4473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * The same as Address.fromHeader(addressList)[0] for non-empty list.
4483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * This is an utility method that offers some performance optimization opportunities.
4493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
4513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static Address firstAddress(String addressList) {
4523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        Address[] array = fromHeader(addressList);
4533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return array.length > 0 ? array[0] : null;
4543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * This method exists to convert an address list formatted in a deprecated legacy format to the
4583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * standard RFC822 header format. {@link #fromHeader(String)} is capable of reading the legacy
4593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * format and the RFC822 format. {@link #toHeader()} always produces the RFC822 format.
4603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
4613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * This implementation is brute-force, and could be replaced with a more efficient version
4623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * if desired.
4633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static String reformatToHeader(String addressList) {
4653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return toHeader(fromHeader(addressList));
4663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param addressList a CSV of RFC822 addresses or the deprecated legacy string format
4703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return array of addresses parsed from <code>addressList</code>
4713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @VisibleForTesting
4733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static Address[] fromHeader(String addressList) {
4743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (addressList == null || addressList.length() == 0) {
4753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return EMPTY_ADDRESS_ARRAY;
4763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // IF we're CSV, just parse
4783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if ((addressList.indexOf(LIST_DELIMITER_PERSONAL) == -1) &&
4793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                (addressList.indexOf(LIST_DELIMITER_EMAIL) == -1)) {
4803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return Address.parse(addressList);
4813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // Otherwise, do backward-compatible unpack
4833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        ArrayList<Address> addresses = new ArrayList<Address>();
4843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        int length = addressList.length();
4853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        int pairStartIndex = 0;
4863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        int pairEndIndex;
4873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        /* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL
4893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen           is used, not for every email address; i.e. not for every iteration of the while().
4903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen           This reduces the theoretical complexity from quadratic to linear,
4913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen           and provides some speed-up in practice by removing redundant scans of the string.
4923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        */
4933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        int addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL);
4943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        while (pairStartIndex < length) {
4963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            pairEndIndex = addressList.indexOf(LIST_DELIMITER_EMAIL, pairStartIndex);
4973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (pairEndIndex == -1) {
4983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                pairEndIndex = length;
4993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
5003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            Address address;
5013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (addressEndIndex == -1 || pairEndIndex <= addressEndIndex) {
5023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // in this case the DELIMITER_PERSONAL is in a future pair,
5033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // so don't use personal, and don't update addressEndIndex
5043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                address = new Address(addressList.substring(pairStartIndex, pairEndIndex), null);
5053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
5063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                address = new Address(addressList.substring(pairStartIndex, addressEndIndex),
5073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                        addressList.substring(addressEndIndex + 1, pairEndIndex));
5083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL
5093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1);
5103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
5113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            addresses.add(address);
5123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            pairStartIndex = pairEndIndex + 1;
5133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return addresses.toArray(new Address[addresses.size()]);
5153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public static final Creator<Address> CREATOR = new Creator<Address>() {
5183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public Address createFromParcel(Parcel parcel) {
5203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return new Address(parcel);
5213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public Address[] newArray(int size) {
5253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return new Address[size];
5263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    };
5283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address(Parcel in) {
5303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setPersonal(in.readString());
5313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setAddress(in.readString());
5323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
5353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public int describeContents() {
5363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return 0;
5373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
5403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void writeToParcel(Parcel out, int flags) {
5413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        out.writeString(mPersonal);
5423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        out.writeString(mAddress);
5433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen}
545