1345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/* 2345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Copyright (C) 2008 The Android Open Source Project 3345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 4345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 5345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * you may not use this file except in compliance with the License. 6345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * You may obtain a copy of the License at 7345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 8345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 9345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 10345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Unless required by applicable law or agreed to in writing, software 11345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 12345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * See the License for the specific language governing permissions and 14345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * limitations under the License. 15345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 16345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 17345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpackage com.android.emailcommon.mail; 18345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 19345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport android.text.TextUtils; 20345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport android.text.util.Rfc822Token; 21345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport android.text.util.Rfc822Tokenizer; 22345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 23345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.google.common.annotations.VisibleForTesting; 24345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 25345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.codec.EncoderUtil; 26345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.decoder.DecoderUtil; 27345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 28345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.ArrayList; 29345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.regex.Pattern; 30345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 31345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/** 32345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * This class represent email address. 33345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 34345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * RFC822 email address may have following format. 35345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * "name" <address> (comment) 36345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * "name" <address> 37345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * name <address> 38345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * address 39345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Name and comment part should be MIME/base64 encoded in header if necessary. 40345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 41345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 42345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpublic class Address { 43345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 44345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Address part, in the form local_part@domain_part. No surrounding angle brackets. 45345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 46345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private String mAddress; 47345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 48345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 49345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Name part. No surrounding double quote, and no MIME/base64 encoding. 50345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * This must be null if Address has no name part. 51345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 52345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private String mPersonal; 53345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 54345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$' 55345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$"); 56345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$' 57345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final Pattern REMOVE_OPTIONAL_DQUOTE = Pattern.compile("^\"?([^\"]*)\"?$"); 58345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // Regex that matches escaped character '\\([\\"])' 59345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])"); 60345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 61f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu 62f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu // TODO: LOCAL_PART and DOMAIN_PART_PART are too permissive and can be improved. 63f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu // TODO: Fix this to better constrain comments. 64f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu /** Regex for the local part of an email address. */ 65f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu private static final String LOCAL_PART = "[^@]+"; 66f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu /** Regex for each part of the domain part, i.e. the thing between the dots. */ 67f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu private static final String DOMAIN_PART_PART = "[[\\w][\\d]\\-\\(\\)\\[\\]]+"; 68f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu /** Regex for the domain part, which is two or more {@link #DOMAIN_PART_PART} separated by . */ 69f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu private static final String DOMAIN_PART = 70f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu "(" + DOMAIN_PART_PART + "\\.)+" + DOMAIN_PART_PART; 71f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu 72f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu /** Pattern to check if an email address is valid. */ 73f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu private static final Pattern EMAIL_ADDRESS = 74f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu Pattern.compile("\\A" + LOCAL_PART + "@" + DOMAIN_PART + "\\z"); 75f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu 76345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0]; 77345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 78345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // delimiters are chars that do not appear in an email address, used by pack/unpack 79345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final char LIST_DELIMITER_EMAIL = '\1'; 80345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final char LIST_DELIMITER_PERSONAL = '\2'; 81345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 82345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public Address(String address, String personal) { 83345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein setAddress(address); 84345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein setPersonal(personal); 85345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 86345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 87345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public Address(String address) { 88345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein setAddress(address); 89345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 90345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 91345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String getAddress() { 92345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mAddress; 93345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 94345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 95345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public void setAddress(String address) { 96345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mAddress = REMOVE_OPTIONAL_BRACKET.matcher(address).replaceAll("$1"); 97345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 98345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 99345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 100345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding. 101345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 102345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return Name part of email address. Returns null if it is omitted. 103345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 104345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String getPersonal() { 105345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mPersonal; 106345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 107345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 108345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 109345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Set name part from UTF-16 string. Optional surrounding double quote will be removed. 110345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * It will be also unquoted and MIME/base64 decoded. 111345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 112345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param personal name part of email address as UTF-16 string. Null is acceptable. 113345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 114345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public void setPersonal(String personal) { 115345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (personal != null) { 116345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein personal = REMOVE_OPTIONAL_DQUOTE.matcher(personal).replaceAll("$1"); 117345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein personal = UNQUOTE.matcher(personal).replaceAll("$1"); 118345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein personal = DecoderUtil.decodeEncodedWords(personal); 119345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (personal.length() == 0) { 120345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein personal = null; 121345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 122345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 123345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mPersonal = personal; 124345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 125345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 126345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 127345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * This method is used to check that all the addresses that the user 128345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * entered in a list (e.g. To:) are valid, so that none is dropped. 129345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 130345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static boolean isAllValid(String addressList) { 131345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // This code mimics the parse() method below. 132345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // I don't know how to better avoid the code-duplication. 133345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addressList != null && addressList.length() > 0) { 134345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList); 135345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (int i = 0, length = tokens.length; i < length; ++i) { 136345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Rfc822Token token = tokens[i]; 137345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein String address = token.getAddress(); 138345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (!TextUtils.isEmpty(address) && !isValidAddress(address)) { 139345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return false; 140345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 141345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 142345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 143345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return true; 144345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 145345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 146345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 147345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Parse a comma-delimited list of addresses in RFC822 format and return an 148345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * array of Address objects. 149345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 150345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addressList Address list in comma-delimited string. 151345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return An array of 0 or more Addresses. 152345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 153345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static Address[] parse(String addressList) { 154345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addressList == null || addressList.length() == 0) { 155345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return EMPTY_ADDRESS_ARRAY; 156345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 157345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList); 158345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein ArrayList<Address> addresses = new ArrayList<Address>(); 159345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (int i = 0, length = tokens.length; i < length; ++i) { 160345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Rfc822Token token = tokens[i]; 161345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein String address = token.getAddress(); 162345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (!TextUtils.isEmpty(address)) { 163345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (isValidAddress(address)) { 164345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein String name = token.getName(); 165345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (TextUtils.isEmpty(name)) { 166345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein name = null; 167345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 168345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein addresses.add(new Address(address, name)); 169345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 170345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 171345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 172345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return addresses.toArray(new Address[] {}); 173345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 174345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 175345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 176345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Checks whether a string email address is valid. 177345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * E.g. name@domain.com is valid. 178345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 179345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein @VisibleForTesting 180f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu static boolean isValidAddress(final String address) { 181f24a125e5fedaae7bdc4eeceeb542cc5a7f3bdd9Yu Ping Hu return EMAIL_ADDRESS.matcher(address).find(); 182345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 183345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 184345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein @Override 185345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public boolean equals(Object o) { 186345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (o instanceof Address) { 187345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // It seems that the spec says that the "user" part is case-sensitive, 188345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // while the domain part in case-insesitive. 189345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // So foo@yahoo.com and Foo@yahoo.com are different. 190345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // This may seem non-intuitive from the user POV, so we 191345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // may re-consider it if it creates UI trouble. 192345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // A problem case is "replyAll" sending to both 193345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // a@b.c and to A@b.c, which turn out to be the same on the server. 194345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // Leave unchanged for now (i.e. case-sensitive). 195345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return getAddress().equals(((Address) o).getAddress()); 196345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 197345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return super.equals(o); 198345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 199345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 200aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy @Override 201345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public int hashCode() { 202345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return getAddress().hashCode(); 203345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 204345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 205345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 206345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get human readable address string. 207345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Do not use this for email header. 208345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 209345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return Human readable address string. Not quoted and not encoded. 210345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 211345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein @Override 212345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String toString() { 213345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mPersonal != null && !mPersonal.equals(mAddress)) { 214345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) { 215345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return quoteString(mPersonal) + " <" + mAddress + ">"; 216345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 217345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mPersonal + " <" + mAddress + ">"; 218345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 219345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 220345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mAddress; 221345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 222345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 223345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 224345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 225345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Ensures that the given string starts and ends with the double quote character. The string is 226345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * not modified in any way except to add the double quote character to start and end if it's not 227345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * already there. 228345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 229345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * TODO: Rename this, because "quoteString()" can mean so many different things. 230345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 231345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * sample -> "sample" 232345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * "sample" -> "sample" 233345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * ""sample"" -> "sample" 234345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * "sample"" -> "sample" 235345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * sa"mp"le -> "sa"mp"le" 236345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * "sa"mp"le" -> "sa"mp"le" 237345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * (empty string) -> "" 238345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * " -> "" 239345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 240345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String quoteString(String s) { 241345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (s == null) { 242345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return null; 243345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 244345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (!s.matches("^\".*\"$")) { 245345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return "\"" + s + "\""; 246345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 247345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein else { 248345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return s; 249345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 250345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 251345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 252345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 253345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get human readable comma-delimited address string. 254345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 255345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addresses Address array 256345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return Human readable comma-delimited address string. 257345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 258345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String toString(Address[] addresses) { 259345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return toString(addresses, ","); 260345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 261345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 262345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 263345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get human readable address strings joined with the specified separator. 264345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 265345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addresses Address array 266345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param separator Separator 267345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return Human readable comma-delimited address string. 268345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 269345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String toString(Address[] addresses, String separator) { 270345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses == null || addresses.length == 0) { 271345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return null; 272345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 273345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses.length == 1) { 274345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return addresses[0].toString(); 275345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 276345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein StringBuffer sb = new StringBuffer(addresses[0].toString()); 277345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (int i = 1; i < addresses.length; i++) { 278345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(separator); 279345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // TODO: investigate why this .trim() is needed. 280345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(addresses[i].toString().trim()); 281345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 282345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return sb.toString(); 283345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 284345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 285345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 286345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get RFC822/MIME compatible address string. 287345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 288345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return RFC822/MIME compatible address string. 289345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary. 290345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 291345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String toHeader() { 292345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mPersonal != null) { 293345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return EncoderUtil.encodeAddressDisplayName(mPersonal) + " <" + mAddress + ">"; 294345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 295345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mAddress; 296345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 297345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 298345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 299345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 300345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get RFC822/MIME compatible comma-delimited address string. 301345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 302345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addresses Address array 303345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return RFC822/MIME compatible comma-delimited address string. 304345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary. 305345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 306345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String toHeader(Address[] addresses) { 307345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses == null || addresses.length == 0) { 308345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return null; 309345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 310345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses.length == 1) { 311345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return addresses[0].toHeader(); 312345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 313345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein StringBuffer sb = new StringBuffer(addresses[0].toHeader()); 314345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (int i = 1; i < addresses.length; i++) { 315345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // We need space character to be able to fold line. 316345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(", "); 317345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(addresses[i].toHeader()); 318345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 319345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return sb.toString(); 320345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 321345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 322345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 323345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get Human friendly address string. 324345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 325345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return the personal part of this Address, or the address part if the 326345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * personal part is not available 327345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 328345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String toFriendly() { 329345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mPersonal != null && mPersonal.length() > 0) { 330345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mPersonal; 331345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 332345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mAddress; 333345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 334345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 335345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 336345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 337345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for 338345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * details on the per-address conversion). 339345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 340345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addresses Array of Address[] values 341345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return A comma-delimited string listing all of the addresses supplied. Null if source 342345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * was null or empty. 343345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 344345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String toFriendly(Address[] addresses) { 345345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses == null || addresses.length == 0) { 346345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return null; 347345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 348345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addresses.length == 1) { 349345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return addresses[0].toFriendly(); 350345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 351345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein StringBuffer sb = new StringBuffer(addresses[0].toFriendly()); 352345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (int i = 1; i < addresses.length; i++) { 353345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(", "); 354345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(addresses[i].toFriendly()); 355345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 356345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return sb.toString(); 357345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 358345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 359345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 360345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Returns exactly the same result as Address.toString(Address.unpack(packedList)). 361345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 362345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String unpackToString(String packedList) { 363345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return toString(unpack(packedList)); 364345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 365345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 366345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 367345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Returns exactly the same result as Address.pack(Address.parse(textList)). 368345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 369345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String parseAndPack(String textList) { 370345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return Address.pack(Address.parse(textList)); 371345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 372345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 373345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 374345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Returns null if the packedList has 0 addresses, otherwise returns the first address. 375345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * The same as Address.unpack(packedList)[0] for non-empty list. 376345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * This is an utility method that offers some performance optimization opportunities. 377345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 378345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static Address unpackFirst(String packedList) { 379345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Address[] array = unpack(packedList); 380345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return array.length > 0 ? array[0] : null; 381345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 382345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 383345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 384345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Convert a packed list of addresses to a form suitable for use in an RFC822 header. 385345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * This implementation is brute-force, and could be replaced with a more efficient version 386345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * if desired. 387345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 388345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String packedToHeader(String packedList) { 389345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return toHeader(unpack(packedList)); 390345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 391345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 392345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 393345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Unpacks an address list that is either CSV of RFC822 addresses OR (for backward 394345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * compatibility) previously packed with pack() 395345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param addressList string packed with pack() or CSV of RFC822 addresses 396345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return array of addresses resulting from unpack 397345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 398345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static Address[] unpack(String addressList) { 399345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addressList == null || addressList.length() == 0) { 400345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return EMPTY_ADDRESS_ARRAY; 401345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 402345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // IF we're CSV, just parse 403345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if ((addressList.indexOf(LIST_DELIMITER_PERSONAL) == -1) && 404345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein (addressList.indexOf(LIST_DELIMITER_EMAIL) == -1)) { 405345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return Address.parse(addressList); 406345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 407345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // Otherwise, do backward-compatibile unpack 408345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein ArrayList<Address> addresses = new ArrayList<Address>(); 409345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int length = addressList.length(); 410345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int pairStartIndex = 0; 411345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int pairEndIndex = 0; 412345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 413345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL 414345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein is used, not for every email address; i.e. not for every iteration of the while(). 415345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein This reduces the theoretical complexity from quadratic to linear, 416345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein and provides some speed-up in practice by removing redundant scans of the string. 417345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 418345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL); 419345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 420345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein while (pairStartIndex < length) { 421345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein pairEndIndex = addressList.indexOf(LIST_DELIMITER_EMAIL, pairStartIndex); 422345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (pairEndIndex == -1) { 423345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein pairEndIndex = length; 424345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 425345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein Address address; 426345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (addressEndIndex == -1 || pairEndIndex <= addressEndIndex) { 427345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // in this case the DELIMITER_PERSONAL is in a future pair, 428345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // so don't use personal, and don't update addressEndIndex 429345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein address = new Address(addressList.substring(pairStartIndex, pairEndIndex), null); 430345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 431345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein address = new Address(addressList.substring(pairStartIndex, addressEndIndex), 432345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein addressList.substring(addressEndIndex + 1, pairEndIndex)); 433345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL 434345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1); 435345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 436345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein addresses.add(address); 437345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein pairStartIndex = pairEndIndex + 1; 438345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 439345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return addresses.toArray(EMPTY_ADDRESS_ARRAY); 440345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 441345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 442345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 443345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Generate a String containing RFC822 addresses separated by commas 444345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * NOTE: We used to "pack" these addresses in an app-specific format, but no longer do so 445345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 446345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public static String pack(Address[] addresses) { 447345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return Address.toHeader(addresses); 448345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 449345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 450345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 451345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Produces the same result as pack(array), but only packs one (this) address. 452345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 453345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String pack() { 454345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein final String address = getAddress(); 455345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein final String personal = getPersonal(); 456345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (personal == null) { 457345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return address; 458345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 459345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return address + LIST_DELIMITER_PERSONAL + personal; 460345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 461345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 462345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein} 463