Deduplicate Address class
Change-Id: I4f6df51f5641475ffaf96b0189ccc00748880cc0
diff --git a/proguard.flags b/proguard.flags
index 1ac96a5..3e3182d 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -77,3 +77,7 @@
# not yet used in UnifiedEmail, but needed by UnifiedEmailTests
-keep class com.android.mail.ui.MailAsyncTaskLoader
+
+-keepclasseswithmembers class com.android.emailcommon.mail.Address {
+ public <methods>;
+}
diff --git a/src/com/android/emailcommon/mail/Address.java b/src/com/android/emailcommon/mail/Address.java
index b648b81..ccd18a3 100644
--- a/src/com/android/emailcommon/mail/Address.java
+++ b/src/com/android/emailcommon/mail/Address.java
@@ -13,13 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.emailcommon.mail;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.Html;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
import org.apache.james.mime4j.codec.EncoderUtil;
@@ -39,7 +43,8 @@
* Name and comment part should be MIME/base64 encoded in header if necessary.
*
*/
-public class Address {
+public class Address implements Parcelable {
+ public static final String ADDRESS_DELIMETER = ",";
/**
* Address part, in the form local_part@domain_part. No surrounding angle brackets.
*/
@@ -51,6 +56,12 @@
*/
private String mPersonal;
+ /**
+ * When personal is set, it will return the first token of the personal
+ * string. Otherwise, it will return the e-mail address up to the '@' sign.
+ */
+ private String mSimplifiedName;
+
// Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$'
private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$");
// Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$'
@@ -58,7 +69,6 @@
// Regex that matches escaped character '\\([\\"])'
private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])");
-
// TODO: LOCAL_PART and DOMAIN_PART_PART are too permissive and can be improved.
// TODO: Fix this to better constrain comments.
/** Regex for the local part of an email address. */
@@ -79,15 +89,64 @@
private static final char LIST_DELIMITER_EMAIL = '\1';
private static final char LIST_DELIMITER_PERSONAL = '\2';
- public Address(String address, String personal) {
- setAddress(address);
- setPersonal(personal);
- }
+ private static final String LOG_TAG = LogTag.getLogTag();
public Address(String address) {
setAddress(address);
}
+ public Address(String address, String personal) {
+ setPersonal(personal);
+ setAddress(address);
+ }
+
+ /**
+ * Returns a simplified string for this e-mail address.
+ * When a name is known, it will return the first token of that name. Otherwise, it will
+ * return the e-mail address up to the '@' sign.
+ */
+ public String getSimplifiedName() {
+ if (mSimplifiedName == null) {
+ if (TextUtils.isEmpty(mPersonal) && !TextUtils.isEmpty(mAddress)) {
+ int atSign = mAddress.indexOf('@');
+ mSimplifiedName = (atSign != -1) ? mAddress.substring(0, atSign) : "";
+ } else if (!TextUtils.isEmpty(mPersonal)) {
+
+ // TODO: use Contacts' NameSplitter for more reliable first-name extraction
+
+ int end = mPersonal.indexOf(' ');
+ while (end > 0 && mPersonal.charAt(end - 1) == ',') {
+ end--;
+ }
+ mSimplifiedName = (end < 1) ? mPersonal : mPersonal.substring(0, end);
+
+ } else {
+ LogUtils.w(LOG_TAG, "Unable to get a simplified name");
+ mSimplifiedName = "";
+ }
+ }
+ return mSimplifiedName;
+ }
+
+ public static synchronized Address getEmailAddress(String rawAddress) {
+ if (TextUtils.isEmpty(rawAddress)) {
+ return null;
+ }
+ String name, address;
+ final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(rawAddress);
+ if (tokens.length > 0) {
+ final String tokenizedName = tokens[0].getName();
+ name = tokenizedName != null ? Html.fromHtml(tokenizedName.trim()).toString()
+ : "";
+ address = Html.fromHtml(tokens[0].getAddress()).toString();
+ } else {
+ name = "";
+ address = rawAddress == null ?
+ "" : Html.fromHtml(rawAddress).toString();
+ }
+ return new Address(address, name);
+ }
+
public String getAddress() {
return mAddress;
}
@@ -106,12 +165,22 @@
}
/**
- * Set name part from UTF-16 string. Optional surrounding double quote will be removed.
+ * Set personal part from UTF-16 string. Optional surrounding double quote will be removed.
* It will be also unquoted and MIME/base64 decoded.
*
* @param personal name part of email address as UTF-16 string. Null is acceptable.
*/
public void setPersonal(String personal) {
+ mPersonal = decodeAddressPersonal(personal);
+ }
+
+ /**
+ * Decodes name from UTF-16 string. Optional surrounding double quote will be removed.
+ * It will be also unquoted and MIME/base64 decoded.
+ *
+ * @param personal name part of email address as UTF-16 string. Null is acceptable.
+ */
+ public static String decodeAddressPersonal(String personal) {
if (personal != null) {
personal = REMOVE_OPTIONAL_DQUOTE.matcher(personal).replaceAll("$1");
personal = UNQUOTE.matcher(personal).replaceAll("$1");
@@ -120,7 +189,7 @@
personal = null;
}
}
- mPersonal = personal;
+ return personal;
}
/**
@@ -169,7 +238,7 @@
}
}
}
- return addresses.toArray(new Address[] {});
+ return addresses.toArray(new Address[addresses.size()]);
}
/**
@@ -212,7 +281,7 @@
public String toString() {
if (mPersonal != null && !mPersonal.equals(mAddress)) {
if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
- return quoteString(mPersonal) + " <" + mAddress + ">";
+ return ensureQuotedString(mPersonal) + " <" + mAddress + ">";
} else {
return mPersonal + " <" + mAddress + ">";
}
@@ -226,8 +295,6 @@
* not modified in any way except to add the double quote character to start and end if it's not
* already there.
*
- * TODO: Rename this, because "quoteString()" can mean so many different things.
- *
* sample -> "sample"
* "sample" -> "sample"
* ""sample"" -> "sample"
@@ -237,14 +304,13 @@
* (empty string) -> ""
* " -> ""
*/
- public static String quoteString(String s) {
+ private static String ensureQuotedString(String s) {
if (s == null) {
return null;
}
if (!s.matches("^\".*\"$")) {
return "\"" + s + "\"";
- }
- else {
+ } else {
return s;
}
}
@@ -256,7 +322,7 @@
* @return Human readable comma-delimited address string.
*/
public static String toString(Address[] addresses) {
- return toString(addresses, ",");
+ return toString(addresses, ADDRESS_DELIMETER);
}
/**
@@ -273,7 +339,7 @@
if (addresses.length == 1) {
return addresses[0].toString();
}
- StringBuffer sb = new StringBuffer(addresses[0].toString());
+ StringBuilder sb = new StringBuilder(addresses[0].toString());
for (int i = 1; i < addresses.length; i++) {
sb.append(separator);
// TODO: investigate why this .trim() is needed.
@@ -310,7 +376,7 @@
if (addresses.length == 1) {
return addresses[0].toHeader();
}
- StringBuffer sb = new StringBuffer(addresses[0].toHeader());
+ StringBuilder sb = new StringBuilder(addresses[0].toHeader());
for (int i = 1; i < addresses.length; i++) {
// We need space character to be able to fold line.
sb.append(", ");
@@ -348,7 +414,7 @@
if (addresses.length == 1) {
return addresses[0].toFriendly();
}
- StringBuffer sb = new StringBuffer(addresses[0].toFriendly());
+ StringBuilder sb = new StringBuilder(addresses[0].toFriendly());
for (int i = 1; i < addresses.length; i++) {
sb.append(", ");
sb.append(addresses[i].toFriendly());
@@ -404,11 +470,11 @@
(addressList.indexOf(LIST_DELIMITER_EMAIL) == -1)) {
return Address.parse(addressList);
}
- // Otherwise, do backward-compatibile unpack
+ // Otherwise, do backward-compatible unpack
ArrayList<Address> addresses = new ArrayList<Address>();
int length = addressList.length();
int pairStartIndex = 0;
- int pairEndIndex = 0;
+ int pairEndIndex;
/* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL
is used, not for every email address; i.e. not for every iteration of the while().
@@ -429,14 +495,14 @@
address = new Address(addressList.substring(pairStartIndex, pairEndIndex), null);
} else {
address = new Address(addressList.substring(pairStartIndex, addressEndIndex),
- addressList.substring(addressEndIndex + 1, pairEndIndex));
+ addressList.substring(addressEndIndex + 1, pairEndIndex));
// only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL
addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1);
}
addresses.add(address);
pairStartIndex = pairEndIndex + 1;
}
- return addresses.toArray(EMPTY_ADDRESS_ARRAY);
+ return addresses.toArray(new Address[addresses.size()]);
}
/**
@@ -451,12 +517,34 @@
* Produces the same result as pack(array), but only packs one (this) address.
*/
public String pack() {
- final String address = getAddress();
- final String personal = getPersonal();
- if (personal == null) {
- return address;
- } else {
- return address + LIST_DELIMITER_PERSONAL + personal;
+ return toHeader();
+ }
+
+ public static final Creator<Address> CREATOR = new Creator<Address>() {
+ @Override
+ public Address createFromParcel(Parcel parcel) {
+ return new Address(parcel);
}
+
+ @Override
+ public Address[] newArray(int size) {
+ return new Address[size];
+ }
+ };
+
+ public Address(Parcel in) {
+ setPersonal(in.readString());
+ setAddress(in.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mPersonal);
+ out.writeString(mAddress);
}
}
diff --git a/src/com/android/mail/browse/ConversationItemView.java b/src/com/android/mail/browse/ConversationItemView.java
index 464ce5a..6d42e79 100644
--- a/src/com/android/mail/browse/ConversationItemView.java
+++ b/src/com/android/mail/browse/ConversationItemView.java
@@ -71,7 +71,6 @@
import com.android.mail.bitmap.ContactDrawable;
import com.android.mail.browse.ConversationItemViewModel.SenderFragment;
import com.android.mail.perf.Timer;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Attachment;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java
index 8406531..5dc55eb 100644
--- a/src/com/android/mail/browse/ConversationViewAdapter.java
+++ b/src/com/android/mail/browse/ConversationViewAdapter.java
@@ -26,13 +26,13 @@
import android.view.ViewGroup;
import android.widget.BaseAdapter;
+import com.android.emailcommon.mail.Address;
import com.android.mail.ContactInfoSource;
import com.android.mail.FormattedDateBuilder;
import com.android.mail.R;
import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
import com.android.mail.browse.SuperCollapsedBlock.OnClickListener;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.UIProvider;
import com.android.mail.ui.ControllableActivity;
diff --git a/src/com/android/mail/browse/EmlMessageViewFragment.java b/src/com/android/mail/browse/EmlMessageViewFragment.java
index 04eabd6..8549303 100644
--- a/src/com/android/mail/browse/EmlMessageViewFragment.java
+++ b/src/com/android/mail/browse/EmlMessageViewFragment.java
@@ -32,9 +32,9 @@
import android.view.ViewGroup;
import android.webkit.WebView;
+import com.android.emailcommon.mail.Address;
import com.android.mail.R;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.ui.AbstractConversationWebViewClient;
import com.android.mail.ui.ContactLoaderCallbacks;
import com.android.mail.ui.SecureConversationViewController;
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index 48d78c5..5659527 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -44,6 +44,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.emailcommon.mail.Address;
import com.android.mail.ContactInfo;
import com.android.mail.ContactInfoSource;
import com.android.mail.R;
@@ -55,7 +56,6 @@
import com.android.mail.photomanager.LetterTileProvider;
import com.android.mail.print.PrintUtils;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.Message;
@@ -562,7 +562,7 @@
if (sender == null) {
return "";
}
- final String displayName = sender.getName();
+ final String displayName = sender.getPersonal();
return TextUtils.isEmpty(displayName) ? sender.getAddress() : displayName;
}
@@ -753,7 +753,7 @@
final String emailAddress = email.getAddress();
final String name;
if (mMatcher != null && mMatcher.isVeiledAddress(emailAddress)) {
- if (TextUtils.isEmpty(email.getName())) {
+ if (TextUtils.isEmpty(email.getPersonal())) {
// Let's write something more readable.
name = mContext.getString(VeiledAddressMatcher.VEILED_SUMMARY_UNKNOWN);
} else {
@@ -809,7 +809,9 @@
// and ensure either the contact URI or email is set so the click
// handling works
String contentDesc = getResources().getString(R.string.contact_info_string,
- !TextUtils.isEmpty(mSender.getName()) ? mSender.getName() : mSender.getAddress());
+ !TextUtils.isEmpty(mSender.getPersonal())
+ ? mSender.getPersonal()
+ : mSender.getAddress());
mPhotoView.setContentDescription(contentDesc);
boolean photoSet = false;
final String email = mSender.getAddress();
@@ -825,7 +827,7 @@
}
if (!photoSet) {
- mPhotoView.setImageBitmap(makeLetterTile(mSender.getName(), email));
+ mPhotoView.setImageBitmap(makeLetterTile(mSender.getPersonal(), email));
}
}
@@ -1341,7 +1343,7 @@
final String[] formattedEmails = new String[emails.length];
for (int i = 0; i < emails.length; i++) {
final Address email = Utils.getAddress(addressCache, emails[i]);
- String name = email.getName();
+ String name = email.getPersonal();
final String address = email.getAddress();
// Check if the address here is a veiled address. If it is, we need to display an
// alternate layout
diff --git a/src/com/android/mail/browse/SendersView.java b/src/com/android/mail/browse/SendersView.java
index bafa532..8d8240f 100644
--- a/src/com/android/mail/browse/SendersView.java
+++ b/src/com/android/mail/browse/SendersView.java
@@ -33,8 +33,8 @@
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
+import com.android.emailcommon.mail.Address;
import com.android.mail.R;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.ConversationInfo;
import com.android.mail.providers.MessageInfo;
@@ -399,7 +399,7 @@
final String[] namesOnly = new String[senders.length];
String display;
for (int i = 0; i < senders.length; i++) {
- display = Address.decodeAddressName(senders[i].getName());
+ display = Address.decodeAddressPersonal(senders[i].getName());
if (TextUtils.isEmpty(display)) {
display = senders[i].getAddress();
}
diff --git a/src/com/android/mail/browse/SnapHeader.java b/src/com/android/mail/browse/SnapHeader.java
index 9428a9f..bd8a026 100644
--- a/src/com/android/mail/browse/SnapHeader.java
+++ b/src/com/android/mail/browse/SnapHeader.java
@@ -20,9 +20,9 @@
import android.util.AttributeSet;
import android.widget.LinearLayout;
+import com.android.emailcommon.mail.Address;
import com.android.mail.ContactInfoSource;
import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
-import com.android.mail.providers.Address;
import com.android.mail.utils.VeiledAddressMatcher;
import java.util.Map;
diff --git a/src/com/android/mail/browse/SpamWarningView.java b/src/com/android/mail/browse/SpamWarningView.java
index 1dbc2b3..598d109 100644
--- a/src/com/android/mail/browse/SpamWarningView.java
+++ b/src/com/android/mail/browse/SpamWarningView.java
@@ -8,8 +8,8 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.emailcommon.mail.Address;
import com.android.mail.R;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Message;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.Utils;
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 0b714a5..c507161 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -72,6 +72,7 @@
import com.android.common.Rfc822Validator;
import com.android.common.contacts.DataUsageStatUpdater;
+import com.android.emailcommon.mail.Address;
import com.android.ex.chips.RecipientEditTextView;
import com.android.mail.MailIntentService;
import com.android.mail.R;
@@ -82,7 +83,6 @@
import com.android.mail.compose.FromAddressSpinner.OnAccountChangedListener;
import com.android.mail.compose.QuotedTextView.RespondInlineListener;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Attachment;
import com.android.mail.providers.Folder;
import com.android.mail.providers.MailAppProvider;
@@ -950,7 +950,7 @@
: mAccount != null ? mAccount.getEmailAddress() : null;
final String senderName = selectedReplyFromAccount != null ? selectedReplyFromAccount.name
: mAccount != null ? mAccount.getSenderName() : null;
- final Address address = new Address(senderName, email);
+ final Address address = new Address(email, senderName);
message.setFrom(address.toHeader());
message.draftType = getDraftType(mode);
return message;
diff --git a/src/com/android/mail/print/PrintUtils.java b/src/com/android/mail/print/PrintUtils.java
index 2d387c3..86210db 100644
--- a/src/com/android/mail/print/PrintUtils.java
+++ b/src/com/android/mail/print/PrintUtils.java
@@ -26,11 +26,11 @@
import android.webkit.WebSettings;
import android.webkit.WebView;
+import com.android.emailcommon.mail.Address;
import com.android.mail.FormattedDateBuilder;
import com.android.mail.R;
import com.android.mail.browse.MessageCursor;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Attachment;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Message;
@@ -160,7 +160,7 @@
final long when = message.dateReceivedMs;
final String date = dateBuilder.formatDateTimeForPrinting(when);
- templates.appendMessage(fromAddress.getName(), fromAddress.getAddress(), date,
+ templates.appendMessage(fromAddress.getPersonal(), fromAddress.getAddress(), date,
renderRecipients(res, addressCache, message), message.getBodyAsHtml(),
renderAttachments(context, res, message));
}
@@ -229,7 +229,7 @@
final String[] formattedEmails = new String[emails.length];
for (int i = 0; i < emails.length; i++) {
final Address email = Utils.getAddress(addressCache, emails[i]);
- final String name = email.getName();
+ final String name = email.getPersonal();
final String address = email.getAddress();
if (TextUtils.isEmpty(name)) {
diff --git a/src/com/android/mail/providers/Address.java b/src/com/android/mail/providers/Address.java
deleted file mode 100644
index edd2db1..0000000
--- a/src/com/android/mail/providers/Address.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.mail.providers;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-
-import com.android.common.Rfc822Validator;
-import com.android.mail.utils.LogTag;
-import com.android.mail.utils.LogUtils;
-import com.android.mail.utils.Utils;
-import com.google.common.annotations.VisibleForTesting;
-
-import org.apache.james.mime4j.codec.EncoderUtil;
-import org.apache.james.mime4j.decoder.DecoderUtil;
-
-import java.util.ArrayList;
-import java.util.regex.Pattern;
-
-/**
- * This class represent email address.
- *
- * RFC822 email address may have following format.
- * "name" <address> (comment)
- * "name" <address>
- * name <address>
- * address
- * Name and comment part should be MIME/base64 encoded in header if necessary.
- *
- */
-public class Address implements Parcelable {
- public static final String ADDRESS_DELIMETER = ",";
- /**
- * Address part, in the form local_part@domain_part. No surrounding angle brackets.
- */
- private String mAddress;
-
- /**
- * Name part. No surrounding double quote, and no MIME/base64 encoding.
- * This must be null if Address has no name part.
- */
- private String mName;
-
- /**
- * When personal is set, it will return the first token of the personal
- * string. Otherwise, it will return the e-mail address up to the '@' sign.
- */
- private String mSimplifiedName;
-
- // Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$'
- private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$");
- // Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$'
- private static final Pattern REMOVE_OPTIONAL_DQUOTE = Pattern.compile("^\"?([^\"]*)\"?$");
- // Regex that matches escaped character '\\([\\"])'
- private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])");
-
- private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0];
-
- // delimiters are chars that do not appear in an email address, used by pack/unpack
- private static final char LIST_DELIMITER_EMAIL = '\1';
- private static final char LIST_DELIMITER_PERSONAL = '\2';
-
- private static final String LOG_TAG = LogTag.getLogTag();
-
- public Address(String name, String address) {
- setName(name);
- setAddress(address);
- }
-
-
-
- /**
- * Returns a simplified string for this e-mail address.
- * When a name is known, it will return the first token of that name. Otherwise, it will
- * return the e-mail address up to the '@' sign.
- */
- public String getSimplifiedName() {
- if (mSimplifiedName == null) {
- if (TextUtils.isEmpty(mName) && !TextUtils.isEmpty(mAddress)) {
- int atSign = mAddress.indexOf('@');
- mSimplifiedName = (atSign != -1) ? mAddress.substring(0, atSign) : "";
- } else if (!TextUtils.isEmpty(mName)) {
-
- // TODO: use Contacts' NameSplitter for more reliable first-name extraction
-
- int end = mName.indexOf(' ');
- while (end > 0 && mName.charAt(end - 1) == ',') {
- end--;
- }
- mSimplifiedName = (end < 1) ? mName : mName.substring(0, end);
-
- } else {
- LogUtils.w(LOG_TAG, "Unable to get a simplified name");
- mSimplifiedName = "";
- }
- }
- return mSimplifiedName;
- }
-
- public static synchronized Address getEmailAddress(String rawAddress) {
- if (TextUtils.isEmpty(rawAddress)) {
- return null;
- }
- String name, address;
- final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(rawAddress);
- if (tokens.length > 0) {
- final String tokenizedName = tokens[0].getName();
- name = tokenizedName != null ? Utils.convertHtmlToPlainText(tokenizedName.trim())
- .toString() : "";
- address = Utils.convertHtmlToPlainText(tokens[0].getAddress()).toString();
- } else {
- name = "";
- address = rawAddress == null ?
- "" : Utils.convertHtmlToPlainText(rawAddress).toString();
- }
- return new Address(name, address);
- }
-
- public Address(String address) {
- setAddress(address);
- }
-
- public String getAddress() {
- return mAddress;
- }
-
- public void setAddress(String address) {
- mAddress = REMOVE_OPTIONAL_BRACKET.matcher(address).replaceAll("$1");
- }
-
- /**
- * Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding.
- *
- * @return Name part of email address. Returns null if it is omitted.
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Set name part from UTF-16 string. Optional surrounding double quote will be removed.
- * It will be also unquoted and MIME/base64 decoded.
- *
- * @param name name part of email address as UTF-16 string. Null is acceptable.
- */
- public void setName(String name) {
- mName = decodeAddressName(name);
- }
-
- /**
- * Decodes name from UTF-16 string. Optional surrounding double quote will be removed.
- * It will be also unquoted and MIME/base64 decoded.
- *
- * @param name name part of email address as UTF-16 string. Null is acceptable.
- */
- public static String decodeAddressName(String name) {
- if (name != null) {
- name = REMOVE_OPTIONAL_DQUOTE.matcher(name).replaceAll("$1");
- name = UNQUOTE.matcher(name).replaceAll("$1");
- name = DecoderUtil.decodeEncodedWords(name);
- if (name.length() == 0) {
- name = null;
- }
- }
- return name;
- }
-
- /**
- * This method is used to check that all the addresses that the user
- * entered in a list (e.g. To:) are valid, so that none is dropped.
- */
- public static boolean isAllValid(String addressList) {
- // This code mimics the parse() method below.
- // I don't know how to better avoid the code-duplication.
- if (addressList != null && addressList.length() > 0) {
- Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
- for (int i = 0, length = tokens.length; i < length; ++i) {
- Rfc822Token token = tokens[i];
- String address = token.getAddress();
- if (!TextUtils.isEmpty(address) && !isValidAddress(address)) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Parse a comma-delimited list of addresses in RFC822 format and return an
- * array of Address objects.
- *
- * @param addressList Address list in comma-delimited string.
- * @return An array of 0 or more Addresses.
- */
- public static Address[] parse(String addressList) {
- if (addressList == null || addressList.length() == 0) {
- return EMPTY_ADDRESS_ARRAY;
- }
- Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
- ArrayList<Address> addresses = new ArrayList<Address>();
- for (int i = 0, length = tokens.length; i < length; ++i) {
- Rfc822Token token = tokens[i];
- String address = token.getAddress();
- if (!TextUtils.isEmpty(address)) {
- if (isValidAddress(address)) {
- String name = token.getName();
- if (TextUtils.isEmpty(name)) {
- name = null;
- }
- addresses.add(new Address(name, address));
- }
- }
- }
- return addresses.toArray(new Address[] {});
- }
-
- /**
- * Checks whether a string email address is valid.
- * E.g. name@domain.com is valid.
- */
- @VisibleForTesting
- static boolean isValidAddress(String address) {
- if (TextUtils.isEmpty(address)) {
- return false;
- }
- int index = address.indexOf("@");
- return index == -1 ? false : new Rfc822Validator(address.substring(0, index))
- .isValid(address);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Address) {
- // It seems that the spec says that the "user" part is case-sensitive,
- // while the domain part in case-insesitive.
- // So foo@yahoo.com and Foo@yahoo.com are different.
- // This may seem non-intuitive from the user POV, so we
- // may re-consider it if it creates UI trouble.
- // A problem case is "replyAll" sending to both
- // a@b.c and to A@b.c, which turn out to be the same on the server.
- // Leave unchanged for now (i.e. case-sensitive).
- return getAddress().equals(((Address) o).getAddress());
- }
- return super.equals(o);
- }
-
- /**
- * Get human readable address string.
- * Do not use this for email header.
- *
- * @return Human readable address string. Not quoted and not encoded.
- */
- @Override
- public String toString() {
- if (mName != null && !mName.equals(mAddress)) {
- if (mName.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
- return Utils.ensureQuotedString(mName) + " <" + mAddress + ">";
- } else {
- return mName + " <" + mAddress + ">";
- }
- } else {
- return mAddress;
- }
- }
-
- /**
- * Get human readable comma-delimited address string.
- *
- * @param addresses Address array
- * @return Human readable comma-delimited address string.
- */
- public static String toString(Address[] addresses) {
- return toString(addresses, ADDRESS_DELIMETER);
- }
-
- /**
- * Get human readable address strings joined with the specified separator.
- *
- * @param addresses Address array
- * @param separator Separator
- * @return Human readable comma-delimited address string.
- */
- public static String toString(Address[] addresses, String separator) {
- if (addresses == null || addresses.length == 0) {
- return null;
- }
- if (addresses.length == 1) {
- return addresses[0].toString();
- }
- StringBuffer sb = new StringBuffer(addresses[0].toString());
- for (int i = 1; i < addresses.length; i++) {
- sb.append(separator);
- // TODO: investigate why this .trim() is needed.
- sb.append(addresses[i].toString().trim());
- }
- return sb.toString();
- }
-
- /**
- * Get RFC822/MIME compatible address string.
- *
- * @return RFC822/MIME compatible address string.
- * It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary.
- */
- public String toHeader() {
- if (mName != null) {
- return EncoderUtil.encodeAddressDisplayName(mName) + " <" + mAddress + ">";
- } else {
- return mAddress;
- }
- }
-
- /**
- * Get RFC822/MIME compatible comma-delimited address string.
- *
- * @param addresses Address array
- * @return RFC822/MIME compatible comma-delimited address string.
- * it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary.
- */
- public static String toHeader(Address[] addresses) {
- if (addresses == null || addresses.length == 0) {
- return null;
- }
- if (addresses.length == 1) {
- return addresses[0].toHeader();
- }
- StringBuffer sb = new StringBuffer(addresses[0].toHeader());
- for (int i = 1; i < addresses.length; i++) {
- // We need space character to be able to fold line.
- sb.append(", ");
- sb.append(addresses[i].toHeader());
- }
- return sb.toString();
- }
-
- /**
- * Get Human friendly address string.
- *
- * @return the personal part of this Address, or the address part if the
- * personal part is not available
- */
- public String toFriendly() {
- if (mName != null && mName.length() > 0) {
- return mName;
- } else {
- return mAddress;
- }
- }
-
- /**
- * Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for
- * details on the per-address conversion).
- *
- * @param addresses Array of Address[] values
- * @return A comma-delimited string listing all of the addresses supplied. Null if source
- * was null or empty.
- */
- public static String toFriendly(Address[] addresses) {
- if (addresses == null || addresses.length == 0) {
- return null;
- }
- if (addresses.length == 1) {
- return addresses[0].toFriendly();
- }
- StringBuffer sb = new StringBuffer(addresses[0].toFriendly());
- for (int i = 1; i < addresses.length; i++) {
- sb.append(", ");
- sb.append(addresses[i].toFriendly());
- }
- return sb.toString();
- }
-
- /**
- * Returns exactly the same result as Address.toString(Address.unpack(packedList)).
- */
- public static String unpackToString(String packedList) {
- return toString(unpack(packedList));
- }
-
- /**
- * Returns exactly the same result as Address.pack(Address.parse(textList)).
- */
- public static String parseAndPack(String textList) {
- return Address.pack(Address.parse(textList));
- }
-
- /**
- * Returns null if the packedList has 0 addresses, otherwise returns the first address.
- * The same as Address.unpack(packedList)[0] for non-empty list.
- * This is an utility method that offers some performance optimization opportunities.
- */
- public static Address unpackFirst(String packedList) {
- Address[] array = unpack(packedList);
- return array.length > 0 ? array[0] : null;
- }
-
- /**
- * Convert a packed list of addresses to a form suitable for use in an RFC822 header.
- * This implementation is brute-force, and could be replaced with a more efficient version
- * if desired.
- */
- public static String packedToHeader(String packedList) {
- return toHeader(unpack(packedList));
- }
-
- /**
- * Unpacks an address list previously packed with pack()
- * @param addressList String with packed addresses as returned by pack()
- * @return array of addresses resulting from unpack
- */
- public static Address[] unpack(String addressList) {
- if (addressList == null || addressList.length() == 0) {
- return EMPTY_ADDRESS_ARRAY;
- }
- ArrayList<Address> addresses = new ArrayList<Address>();
- int length = addressList.length();
- int pairStartIndex = 0;
- int pairEndIndex = 0;
-
- /* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL
- is used, not for every email address; i.e. not for every iteration of the while().
- This reduces the theoretical complexity from quadratic to linear,
- and provides some speed-up in practice by removing redundant scans of the string.
- */
- int addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL);
-
- while (pairStartIndex < length) {
- pairEndIndex = addressList.indexOf(LIST_DELIMITER_EMAIL, pairStartIndex);
- if (pairEndIndex == -1) {
- pairEndIndex = length;
- }
- Address address;
- if (addressEndIndex == -1 || pairEndIndex <= addressEndIndex) {
- // in this case the DELIMITER_PERSONAL is in a future pair,
- // so don't use personal, and don't update addressEndIndex
- address = new Address(null, addressList.substring(pairStartIndex, pairEndIndex));
- } else {
- address = new Address(addressList.substring(addressEndIndex + 1, pairEndIndex),
- addressList.substring(pairStartIndex, addressEndIndex));
- // only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL
- addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1);
- }
- addresses.add(address);
- pairStartIndex = pairEndIndex + 1;
- }
- return addresses.toArray(EMPTY_ADDRESS_ARRAY);
- }
-
- /**
- * Packs an address list into a String that is very quick to read
- * and parse. Packed lists can be unpacked with unpack().
- * The format is a series of packed addresses separated by LIST_DELIMITER_EMAIL.
- * Each address is packed as
- * a pair of address and personal separated by LIST_DELIMITER_PERSONAL,
- * where the personal and delimiter are optional.
- * E.g. "foo@x.com\1joe@x.com\2Joe Doe"
- * @param addresses Array of addresses
- * @return a string containing the packed addresses.
- */
- public static String pack(Address[] addresses) {
- // TODO: return same value for both null & empty list
- if (addresses == null) {
- return null;
- }
- final int nAddr = addresses.length;
- if (nAddr == 0) {
- return "";
- }
-
- // shortcut: one email with no displayName
- if (nAddr == 1 && addresses[0].getName() == null) {
- return addresses[0].getAddress();
- }
-
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < nAddr; i++) {
- if (i != 0) {
- sb.append(LIST_DELIMITER_EMAIL);
- }
- final Address address = addresses[i];
- sb.append(address.getAddress());
- final String displayName = address.getName();
- if (displayName != null) {
- sb.append(LIST_DELIMITER_PERSONAL);
- sb.append(displayName);
- }
- }
- return sb.toString();
- }
-
- /**
- * Produces the same result as pack(array), but only packs one (this) address.
- */
- public String pack() {
- final String address = getAddress();
- final String personal = getName();
- if (personal == null) {
- return address;
- } else {
- return address + LIST_DELIMITER_PERSONAL + personal;
- }
- }
-
- public static final Creator<Address> CREATOR = new Creator<Address>() {
- @Override
- public Address createFromParcel(Parcel parcel) {
- return new Address(parcel);
- }
-
- @Override
- public Address[] newArray(int size) {
- return new Address[size];
- }
- };
-
- public Address(Parcel in) {
- setName(in.readString());
- setAddress(in.readString());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(mName);
- out.writeString(mAddress);
- }
-}
diff --git a/src/com/android/mail/providers/Message.java b/src/com/android/mail/providers/Message.java
index 8bd8469..0611e91 100644
--- a/src/com/android/mail/providers/Message.java
+++ b/src/com/android/mail/providers/Message.java
@@ -31,6 +31,7 @@
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
+import com.android.emailcommon.mail.Address;
import com.android.emailcommon.internet.MimeMessage;
import com.android.emailcommon.internet.MimeUtility;
import com.android.emailcommon.mail.MessagingException;
@@ -368,14 +369,14 @@
public Message(Context context, MimeMessage mimeMessage, Uri emlFileUri)
throws MessagingException {
// Set message header values.
- setFrom(com.android.emailcommon.mail.Address.pack(mimeMessage.getFrom()));
- setTo(com.android.emailcommon.mail.Address.pack(mimeMessage.getRecipients(
+ setFrom(Address.pack(mimeMessage.getFrom()));
+ setTo(Address.pack(mimeMessage.getRecipients(
com.android.emailcommon.mail.Message.RecipientType.TO)));
- setCc(com.android.emailcommon.mail.Address.pack(mimeMessage.getRecipients(
+ setCc(Address.pack(mimeMessage.getRecipients(
com.android.emailcommon.mail.Message.RecipientType.CC)));
- setBcc(com.android.emailcommon.mail.Address.pack(mimeMessage.getRecipients(
+ setBcc(Address.pack(mimeMessage.getRecipients(
com.android.emailcommon.mail.Message.RecipientType.BCC)));
- setReplyTo(com.android.emailcommon.mail.Address.pack(mimeMessage.getReplyTo()));
+ setReplyTo(Address.pack(mimeMessage.getReplyTo()));
subject = mimeMessage.getSubject();
final Date sentDate = mimeMessage.getSentDate();
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index 5425f7d..d70305b 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -30,7 +30,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
-import com.android.mail.ContactInfoSource;
+import com.android.emailcommon.mail.Address;
import com.android.mail.R;
import com.android.mail.analytics.Analytics;
import com.android.mail.browse.ConversationAccountController;
@@ -43,7 +43,6 @@
import com.android.mail.preferences.AccountPreferences;
import com.android.mail.providers.Account;
import com.android.mail.providers.AccountObserver;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.ListParams;
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index 76c4b4e..2b12a61 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -43,6 +43,7 @@
import android.webkit.WebView;
import android.widget.Button;
+import com.android.emailcommon.mail.Address;
import com.android.mail.FormattedDateBuilder;
import com.android.mail.R;
import com.android.mail.analytics.Analytics;
@@ -68,7 +69,6 @@
import com.android.mail.content.ObjectCursor;
import com.android.mail.print.PrintUtils;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Message;
import com.android.mail.providers.Settings;
@@ -1345,8 +1345,8 @@
} else {
final Address addr = getAddress(senderAddress);
return res.getString(R.string.new_incoming_messages_one,
- sBidiFormatter.unicodeWrap(TextUtils.isEmpty(addr.getName())
- ? addr.getAddress() : addr.getName()));
+ sBidiFormatter.unicodeWrap(TextUtils.isEmpty(addr.getPersonal())
+ ? addr.getAddress() : addr.getPersonal()));
}
}
}
diff --git a/src/com/android/mail/ui/NestedFolderTeaserView.java b/src/com/android/mail/ui/NestedFolderTeaserView.java
index c4d33e7..ebbdbdc 100644
--- a/src/com/android/mail/ui/NestedFolderTeaserView.java
+++ b/src/com/android/mail/ui/NestedFolderTeaserView.java
@@ -34,12 +34,12 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.emailcommon.mail.Address;
import com.android.mail.R;
import com.android.mail.browse.ConversationCursor;
import com.android.mail.content.ObjectCursor;
import com.android.mail.content.ObjectCursorLoader;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.MessageInfo;
@@ -52,14 +52,12 @@
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* The teaser list item in the conversation list that shows nested folders.
@@ -556,7 +554,7 @@
final Address[] senderAddresses = Address.parse(unreadSenders);
for (final Address senderAddress : senderAddresses) {
- String sender = senderAddress.getName();
+ String sender = senderAddress.getPersonal();
final String senderEmail = senderAddress.getAddress();
if (!TextUtils.isEmpty(sender)) {
diff --git a/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
index f53de7d..e4d32a0 100644
--- a/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
+++ b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
@@ -21,11 +21,11 @@
import android.net.Uri;
import android.os.Handler;
+import com.android.emailcommon.mail.Address;
import com.android.mail.ContactInfoSource;
import com.android.mail.browse.ConversationAccountController;
import com.android.mail.browse.ConversationViewHeader;
import com.android.mail.browse.MessageHeaderView;
-import com.android.mail.providers.Address;
import java.util.Map;
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index 051940c..8762b0c 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.webkit.WebView;
+import com.android.emailcommon.mail.Address;
import com.android.mail.browse.ConversationAccountController;
import com.android.mail.browse.ConversationMessage;
import com.android.mail.browse.ConversationViewHeader;
@@ -33,7 +34,6 @@
import com.android.mail.browse.MessageHeaderView;
import com.android.mail.content.ObjectCursor;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
diff --git a/src/com/android/mail/utils/NotificationUtils.java b/src/com/android/mail/utils/NotificationUtils.java
index 694ac79..ef7fd70 100644
--- a/src/com/android/mail/utils/NotificationUtils.java
+++ b/src/com/android/mail/utils/NotificationUtils.java
@@ -41,11 +41,11 @@
import android.util.Pair;
import android.util.SparseArray;
+import com.android.emailcommon.mail.Address;
import com.android.mail.EmailAddress;
import com.android.mail.MailIntentService;
import com.android.mail.R;
import com.android.mail.analytics.Analytics;
-import com.android.mail.analytics.AnalyticsUtils;
import com.android.mail.browse.MessageCursor;
import com.android.mail.browse.SendersView;
import com.android.mail.photomanager.LetterTileProvider;
@@ -53,7 +53,6 @@
import com.android.mail.preferences.FolderPreferences;
import com.android.mail.preferences.MailPrefs;
import com.android.mail.providers.Account;
-import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.Message;
@@ -1434,7 +1433,7 @@
String displayableSender = address.getName();
if (!TextUtils.isEmpty(displayableSender)) {
- return Address.decodeAddressName(displayableSender);
+ return Address.decodeAddressPersonal(displayableSender);
}
// If that fails, default to the sender address.
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 459e090..9ca5510 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -16,12 +16,11 @@
package com.android.mail.utils;
-import com.android.mail.providers.Address;
+import com.android.emailcommon.mail.Address;
import com.google.android.mail.common.html.parser.HtmlDocument;
import com.google.android.mail.common.html.parser.HtmlParser;
import com.google.android.mail.common.html.parser.HtmlTree;
import com.google.android.mail.common.html.parser.HtmlTreeBuilder;
-import com.google.common.collect.Maps;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -44,7 +43,6 @@
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.text.style.TextAppearanceSpan;
@@ -206,25 +204,6 @@
return text.substring(0, realMax) + extension;
}
- /**
- * Ensures that the given string starts and ends with the double quote
- * character. The string is not modified in any way except to add the double
- * quote character to start and end if it's not already there. sample ->
- * "sample" "sample" -> "sample" ""sample"" -> "sample"
- * "sample"" -> "sample" sa"mp"le -> "sa"mp"le" "sa"mp"le" -> "sa"mp"le"
- * (empty string) -> "" " -> ""
- */
- public static String ensureQuotedString(String s) {
- if (s == null) {
- return null;
- }
- if (!s.matches("^\".*\"$")) {
- return "\"" + s + "\"";
- } else {
- return s;
- }
- }
-
private static int sMaxUnreadCount = -1;
private static final CharacterStyle ACTION_BAR_UNREAD_STYLE = new StyleSpan(Typeface.BOLD);
private static String sUnreadText;
diff --git a/tests/src/com/android/emailcommon/mail/AddressUnitTests.java b/tests/src/com/android/emailcommon/mail/AddressUnitTests.java
new file mode 100644
index 0000000..13bf686
--- /dev/null
+++ b/tests/src/com/android/emailcommon/mail/AddressUnitTests.java
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.emailcommon.mail;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.apache.james.mime4j.decoder.DecoderUtil;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/**
+ * This is a series of unit tests for the Address class. These tests must be locally
+ * complete - no server(s) required.
+ */
+@SmallTest
+public class AddressUnitTests extends AndroidTestCase {
+
+ private static final String MULTI_ADDRESSES_LIST =
+ "noname1@dom1.com, "
+ + "<noname2@dom2.com>, "
+ + "simple name <address3@dom3.org>, "
+ + "\"name,4\" <address4@dom4.org>,"
+ + "\"big \\\"G\\\"\" <bigG@dom5.net>,"
+ + "\u65E5\u672C\u8A9E <address6@co.jp>,"
+ + "\"\u65E5\u672C\u8A9E\" <address7@co.jp>,"
+ + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
+ + "\"\uD834\uDF01\uD834\uDF46\" <address9@ne.jp>,"
+ + "noname@dom.com <noname@dom.com>" // personal == address
+ ;
+ private static final int MULTI_ADDRESSES_COUNT = 10;
+
+ private static final Address PACK_ADDR_1 = new Address("john@gmail.com", "John Doe");
+ private static final Address PACK_ADDR_2 = new Address("foo@bar.com", null);
+ private static final Address PACK_ADDR_3 = new Address(
+ "mar.y+test@gmail.com", "Mar-y, B; B*arr");
+ private static final Address[][] PACK_CASES = {
+ {PACK_ADDR_2}, {PACK_ADDR_1},
+ {PACK_ADDR_1, PACK_ADDR_2}, {PACK_ADDR_2, PACK_ADDR_1},
+ {PACK_ADDR_1, PACK_ADDR_3}, {PACK_ADDR_2, PACK_ADDR_2},
+ {PACK_ADDR_1, PACK_ADDR_2, PACK_ADDR_3}, {PACK_ADDR_3, PACK_ADDR_1, PACK_ADDR_2}
+ };
+
+ Address mAddress1;
+ Address mAddress2;
+ Address mAddress3;
+
+ /**
+ * Setup code. We generate a handful of Address objects
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mAddress1 = new Address("address1", "personal1");
+ mAddress2 = new Address("address2", "");
+ mAddress3 = new Address("address3", null);
+ }
+
+ // see documentation of DecoderUtil.decodeEncodedWords() for details
+ private String padEncoded(String s) {
+ return "=?UTF-8?B?" + s + "?=";
+ }
+
+ /**
+ * Generate strings of incresing lenght by taking prefix substrings.
+ * For each of them, compare with the decoding of the precomputed base-64 encoding.
+ */
+ public void testBase64Decode() {
+ String testString = "xyza\0\"";
+ String base64Encoded[] = {"", "eA==", "eHk=", "eHl6", "eHl6YQ==", "eHl6YQA=", "eHl6YQAi"};
+ int len = testString.length();
+ for (int i = 1; i <= len; ++i) {
+ String encoded = padEncoded(base64Encoded[i]);
+ String decoded = DecoderUtil.decodeEncodedWords(encoded);
+ String prefix = testString.substring(0, i);
+ assertEquals(""+i, prefix, decoded);
+ }
+ }
+
+ /**
+ * Test for setAddress().
+ */
+ public void testSetAddress() {
+ String bareAddress = "user1@dom1.com";
+ String bracketAddress = "<user2@dom2.com>";
+
+ Address address = new Address(bareAddress);
+ assertEquals("bare address", "user1@dom1.com", address.getAddress());
+
+ address.setAddress(bracketAddress);
+ assertEquals("bracket address", "user2@dom2.com", address.getAddress());
+ }
+
+ /**
+ * Test for empty setPersonal().
+ */
+ public void brokentestNullPersonal() {
+ Address address = new Address("user1@dom1.org");
+ assertNull("no name", address.getPersonal());
+
+ address.setPersonal(null);
+ assertNull("null name", address.getPersonal());
+
+ address.setPersonal("");
+ assertNull("empty name", address.getPersonal());
+
+ address.setPersonal("\"\"");
+ assertNull("quoted empty address", address.getPersonal());
+ }
+
+ /**
+ * Test for setPersonal().
+ */
+ public void brokentestSetPersonal() {
+ Address address = new Address("user1@dom1.net", "simple name");
+ assertEquals("simple name", "simple name", address.getPersonal());
+
+ address.setPersonal("big \\\"G\\\"");
+ assertEquals("quoted name", "big \"G\"", address.getPersonal());
+
+ address.setPersonal("=?UTF-8?Q?big \"G\"?=");
+ assertEquals("quoted printable name", "big \"G\"", address.getPersonal());
+
+ address.setPersonal("=?UTF-8?B?YmlnICJHIg==?=");
+ assertEquals("base64 encoded name", "big \"G\"", address.getPersonal());
+ }
+
+ /**
+ * Test for setPersonal() with utf-16 and utf-32.
+ */
+ public void brokentestSetPersonalMultipleEncodings() {
+ Address address = new Address("user1@dom1.co.jp", "=?UTF-8?B?5bK45pys?=");
+ assertEquals("base64 utf-16 name", "\u5CB8\u672C", address.getPersonal());
+
+ address.setPersonal("\"=?UTF-8?Q?=E5=B2=B8=E6=9C=AC?=\"");
+ assertEquals("quoted printable utf-16 name", "\u5CB8\u672C", address.getPersonal());
+
+ address.setPersonal("=?ISO-2022-JP?B?GyRCNF9LXBsoQg==?=");
+ assertEquals("base64 jis encoded name", "\u5CB8\u672C", address.getPersonal());
+
+ address.setPersonal("\"=?UTF-8?B?8J2MgfCdjYY=?=\"");
+ assertEquals("base64 utf-32 name", "\uD834\uDF01\uD834\uDF46", address.getPersonal());
+
+ address.setPersonal("=?UTF-8?Q?=F0=9D=8C=81=F0=9D=8D=86?=");
+ assertEquals("quoted printable utf-32 name",
+ "\uD834\uDF01\uD834\uDF46", address.getPersonal());
+ }
+
+ /**
+ * TODO: more in-depth tests for parse()
+ */
+
+ /**
+ * Simple quick checks of empty-input edge conditions for parse()
+ *
+ * NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
+ * behavior while I am changing some of the code in the function under test.
+ */
+ public void testEmptyParse() {
+ Address[] result;
+
+ // null input => empty array
+ result = Address.parse(null);
+ assertTrue("parsing null address", result != null && result.length == 0);
+
+ // empty string input => empty array
+ result = Address.parse("");
+ assertTrue("parsing zero-length", result != null && result.length == 0);
+
+ // spaces
+ result = Address.parse(" ");
+ assertTrue("parsing spaces", result != null && result.length == 0);
+
+ // spaces with comma
+ result = Address.parse(" , ");
+ assertTrue("parsing spaces with comma", result != null && result.length == 0);
+ }
+
+ /**
+ * Test parsing for single address.
+ */
+ public void testSingleParse() {
+ Address[] address1 = Address.parse("address1@dom1.com");
+ assertEquals("bare address count", 1, address1.length);
+ assertEquals("bare address", "address1@dom1.com", address1[0].getAddress());
+ assertNull("name of bare address", address1[0].getPersonal());
+
+ Address[] address2 = Address.parse("<address2@dom2.com>");
+ assertEquals("bracket address count", 1, address2.length);
+ assertEquals("bracket address", "address2@dom2.com", address2[0].getAddress());
+ assertNull("name of bracket address", address2[0].getPersonal());
+
+ Address[] address3 = Address.parse("first last <address3@dom3.org>");
+ assertEquals("address with name count", 1, address3.length);
+ assertEquals("address with name", "address3@dom3.org", address3[0].getAddress());
+ assertEquals("name of address with name", "first last", address3[0].getPersonal());
+
+ Address[] address4 = Address.parse("\"first,last\" <address4@dom4.org>");
+ assertEquals("address with quoted name count", 1, address4.length);
+ assertEquals("address with quoted name", "address4@dom4.org", address4[0].getAddress());
+ assertEquals("name of address with quoted name", "first,last", address4[0].getPersonal());
+ }
+
+ /**
+ * Test parsing for illegal address.
+ */
+ public void testIllegalParse() {
+ Address[] address1 = Address.parse("address1");
+ assertEquals("no atmark", 0, address1.length);
+
+ Address[] address2 = Address.parse("address2@");
+ assertEquals("no domain", 0, address2.length);
+
+ Address[] address3 = Address.parse("@dom3.com");
+ assertEquals("no local part", 0, address3.length);
+
+ Address[] address4 = Address.parse("address4@sub@dom4.org");
+ assertEquals("more than one atmark", 0, address4.length);
+
+ Address[] address5 = Address.parse("address5@dom5");
+ assertEquals("not dot in domain part", 0, address5.length);
+
+ Address[] address6 = Address.parse("address6@dom6.com.");
+ assertEquals("domain ends with dot", 0, address6.length);
+
+ Address[] address7 = Address.parse("address7@.dom7.org");
+ assertEquals("domain starts with dot", 0, address7.length);
+ }
+
+ /**
+ * Test parsing for address part.
+ */
+ public void testParsingAddress() {
+ Address[] addresses = Address.parse("address1@dom1.net, <address2@dom2.com>");
+ assertEquals("address count", 2, addresses.length);
+
+ assertEquals("bare address", "address1@dom1.net", addresses[0].getAddress());
+ assertNull("bare address name", addresses[0].getPersonal());
+
+ assertEquals("bracket address", "address2@dom2.com", addresses[1].getAddress());
+ assertNull("bracket address name", addresses[1].getPersonal());
+ }
+
+ /**
+ * Test parsing for simple name part.
+ */
+ public void testParsingSimpleName() {
+ Address[] addresses = Address.parse(
+ "name 1 <address1@dom1.net>, " +
+ "\"name,2\" <address2@dom2.org>");
+ assertEquals("address count", 2, addresses.length);
+
+ assertEquals("bare name address", "address1@dom1.net", addresses[0].getAddress());
+ assertEquals("bare name", "name 1", addresses[0].getPersonal());
+
+ assertEquals("double quoted name address", "address2@dom2.org", addresses[1].getAddress());
+ assertEquals("double quoted name", "name,2", addresses[1].getPersonal());
+ }
+
+ /**
+ * Test parsing for utf-16 name part.
+ */
+ public void testParsingUtf16Name() {
+ Address[] addresses = Address.parse(
+ "\u3042\u3044\u3046 \u3048\u304A <address1@dom1.jp>, " +
+ "\"\u3042\u3044\u3046,\u3048\u304A\" <address2@dom2.jp>");
+ assertEquals("address count", 2, addresses.length);
+
+ assertEquals("bare utf-16 name address", "address1@dom1.jp", addresses[0].getAddress());
+ assertEquals("bare utf-16 name",
+ "\u3042\u3044\u3046 \u3048\u304A", addresses[0].getPersonal());
+
+ assertEquals("double quoted utf-16 name address",
+ "address2@dom2.jp", addresses[1].getAddress());
+ assertEquals("double quoted utf-16 name",
+ "\u3042\u3044\u3046,\u3048\u304A", addresses[1].getPersonal());
+ }
+
+ /**
+ * Test parsing for utf-32 name part.
+ */
+ public void testParsingUtf32Name() {
+ Address[] addresses = Address.parse(
+ "\uD834\uDF01\uD834\uDF46 \uD834\uDF22 <address1@dom1.net>, " +
+ "\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <address2@dom2.com>");
+ assertEquals("address count", 2, addresses.length);
+
+ assertEquals("bare utf-32 name address", "address1@dom1.net", addresses[0].getAddress());
+ assertEquals("bare utf-32 name",
+ "\uD834\uDF01\uD834\uDF46 \uD834\uDF22", addresses[0].getPersonal());
+
+ assertEquals("double quoted utf-32 name address",
+ "address2@dom2.com", addresses[1].getAddress());
+ assertEquals("double quoted utf-32 name",
+ "\uD834\uDF01\uD834\uDF46,\uD834\uDF22", addresses[1].getPersonal());
+ }
+
+ /**
+ * Test parsing for multi addresses.
+ */
+ public void testParseMulti() {
+ Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
+
+ assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
+
+ assertEquals("no name 1 address", "noname1@dom1.com", addresses[0].getAddress());
+ assertNull("no name 1 name", addresses[0].getPersonal());
+ assertEquals("no name 2 address", "noname2@dom2.com", addresses[1].getAddress());
+ assertNull("no name 2 name", addresses[1].getPersonal());
+ assertEquals("simple name address", "address3@dom3.org", addresses[2].getAddress());
+ assertEquals("simple name name", "simple name", addresses[2].getPersonal());
+ assertEquals("double quoted name address", "address4@dom4.org", addresses[3].getAddress());
+ assertEquals("double quoted name name", "name,4", addresses[3].getPersonal());
+ assertEquals("quoted name address", "bigG@dom5.net", addresses[4].getAddress());
+ assertEquals("quoted name name", "big \"G\"", addresses[4].getPersonal());
+ assertEquals("utf-16 name address", "address6@co.jp", addresses[5].getAddress());
+ assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", addresses[5].getPersonal());
+ assertEquals("utf-16 quoted name address", "address7@co.jp", addresses[6].getAddress());
+ assertEquals("utf-16 quoted name name", "\u65E5\u672C\u8A9E",
+ addresses[6].getPersonal());
+ assertEquals("utf-32 name address", "address8@ne.jp", addresses[7].getAddress());
+ assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", addresses[7].getPersonal());
+ assertEquals("utf-32 quoted name address", "address9@ne.jp", addresses[8].getAddress());
+ assertEquals("utf-32 quoted name name", "\uD834\uDF01\uD834\uDF46",
+ addresses[8].getPersonal());
+ }
+
+ /**
+ * Test various combinations of the toString (single) method
+ */
+ public void testToStringSingle() {
+ Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
+
+ assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
+
+ // test for toString() results.
+ assertEquals("no name 1", "noname1@dom1.com", addresses[0].toString());
+ assertEquals("no name 2", "noname2@dom2.com", addresses[1].toString());
+ assertEquals("simple name", "simple name <address3@dom3.org>", addresses[2].toString());
+ assertEquals("double quoted name",
+ "\"name,4\" <address4@dom4.org>", addresses[3].toString());
+ assertEquals("quoted name", "\"big \"G\"\" <bigG@dom5.net>", addresses[4].toString());
+ assertEquals("utf-16 name", "\u65E5\u672C\u8A9E <address6@co.jp>",
+ addresses[5].toString());
+ assertEquals("utf-16 quoted name", "\u65E5\u672C\u8A9E <address7@co.jp>",
+ addresses[6].toString());
+ assertEquals("utf-32 name", "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>",
+ addresses[7].toString());
+ assertEquals("utf-32 quoted name", "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>",
+ addresses[8].toString());
+ assertEquals("name==address", "noname@dom.com", addresses[9].toString());
+ }
+
+ /**
+ * Test various combinations of the toString (multi) method
+ */
+ public void testToStringMulti() {
+ final Address[] address = Address.parse("noname1@dom1.com");
+ final Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
+
+ assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
+
+ {
+ String line = Address.toString(address);
+ assertEquals("toString multi-1",
+ "noname1@dom1.com",
+ line);
+ }
+ {
+ String line = Address.toString(addresses);
+ assertEquals("toString multi-n",
+ "noname1@dom1.com,"
+ + "noname2@dom2.com,"
+ + "simple name <address3@dom3.org>,"
+ + "\"name,4\" <address4@dom4.org>,"
+ + "\"big \"G\"\" <bigG@dom5.net>,"
+ + "\u65E5\u672C\u8A9E <address6@co.jp>,"
+ + "\u65E5\u672C\u8A9E <address7@co.jp>,"
+ + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
+ + "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>,"
+ + "noname@dom.com",
+ line);
+ }
+
+ // With custom separator
+ {
+ String line = Address.toString(address, "$");
+ assertEquals("toString multi-1",
+ "noname1@dom1.com",
+ line);
+ }
+
+ {
+ String line = Address.toString(addresses, "$");
+ assertEquals("toString multi-n",
+ "noname1@dom1.com$"
+ + "noname2@dom2.com$"
+ + "simple name <address3@dom3.org>$"
+ + "\"name,4\" <address4@dom4.org>$"
+ + "\"big \"G\"\" <bigG@dom5.net>$"
+ + "\u65E5\u672C\u8A9E <address6@co.jp>$"
+ + "\u65E5\u672C\u8A9E <address7@co.jp>$"
+ + "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>$"
+ + "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>$"
+ + "noname@dom.com",
+ line);
+ }
+ }
+
+ /**
+ * Test parsing for quoted and encoded name part.
+ */
+ public void testParsingQuotedEncodedName() {
+ Address[] addresses = Address.parse(
+ "\"big \\\"G\\\"\" <bigG@dom1.com>, =?UTF-8?B?5pel5pys6Kqe?= <address2@co.jp>");
+
+ assertEquals("address count", 2, addresses.length);
+
+ assertEquals("quoted name address", "bigG@dom1.com", addresses[0].getAddress());
+ assertEquals("quoted name", "big \"G\"", addresses[0].getPersonal());
+
+ assertEquals("encoded name address", "address2@co.jp", addresses[1].getAddress());
+ assertEquals("encoded name", "\u65E5\u672C\u8A9E", addresses[1].getPersonal());
+ }
+
+ /**
+ * Test various combinations of the toHeader (single) method
+ */
+ public void testToHeaderSingle() {
+ Address noName1 = new Address("noname1@dom1.com");
+ Address noName2 = new Address("<noname2@dom2.com>", "");
+ Address simpleName = new Address("address3@dom3.org", "simple name");
+ Address dquoteName = new Address("address4@dom4.org", "name,4");
+ Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
+ Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
+ Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
+ Address sameName = new Address("address@dom.org", "address@dom.org");
+
+ // test for internal states.
+ assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
+ assertNull("no name 1 name", noName1.getPersonal());
+ assertEquals("no name 2 address", "noname2@dom2.com", noName2.getAddress());
+ assertNull("no name 2 name", noName2.getPersonal());
+ assertEquals("simple name address", "address3@dom3.org", simpleName.getAddress());
+ assertEquals("simple name name", "simple name", simpleName.getPersonal());
+ assertEquals("double quoted name address", "address4@dom4.org", dquoteName.getAddress());
+ assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
+ assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
+ assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
+ assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
+ assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
+ assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
+ assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
+ assertEquals("name == address address", "address@dom.org", sameName.getAddress());
+ assertEquals("name == address name", "address@dom.org", sameName.getPersonal());
+
+ // Test for toHeader() results.
+ assertEquals("no name 1", "noname1@dom1.com", noName1.toHeader());
+ assertEquals("no name 2", "noname2@dom2.com", noName2.toHeader());
+ assertEquals("simple name", "simple name <address3@dom3.org>", simpleName.toHeader());
+ assertEquals("double quoted name", "\"name,4\" <address4@dom4.org>", dquoteName.toHeader());
+ assertEquals("quoted name", "\"big \\\"G\\\"\" <bigG@dom5.net>", quotedName.toHeader());
+ assertEquals("utf-16 name", "=?UTF-8?B?5pel5pys6Kqe?= <address6@co.jp>",
+ utf16Name.toHeader());
+ assertEquals("utf-32 name", "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
+ utf32Name.toHeader());
+ assertEquals("name == address", "\"address@dom.org\" <address@dom.org>",
+ sameName.toHeader());
+ }
+
+ /**
+ * Test various combinations of the toHeader (multi) method
+ */
+ public void testToHeaderMulti() {
+ Address noName1 = new Address("noname1@dom1.com");
+ Address noName2 = new Address("<noname2@dom2.com>", "");
+ Address simpleName = new Address("address3@dom3.org", "simple name");
+ Address dquoteName = new Address("address4@dom4.org", "name,4");
+ Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
+ Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
+ Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
+
+ // test for internal states.
+ assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
+ assertNull("no name 1 name", noName1.getPersonal());
+ assertEquals("no name 2 address", "noname2@dom2.com", noName2.getAddress());
+ assertNull("no name 2 name", noName2.getPersonal());
+ assertEquals("simple name address", "address3@dom3.org", simpleName.getAddress());
+ assertEquals("simple name name", "simple name", simpleName.getPersonal());
+ assertEquals("double quoted name address", "address4@dom4.org", dquoteName.getAddress());
+ assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
+ assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
+ assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
+ assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
+ assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
+ assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
+ assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
+
+ Address[] addresses = new Address[] {
+ noName1, noName2, simpleName, dquoteName, quotedName, utf16Name, utf32Name,
+ };
+ String line = Address.toHeader(addresses);
+
+ assertEquals("toHeader() multi",
+ "noname1@dom1.com, "
+ + "noname2@dom2.com, "
+ + "simple name <address3@dom3.org>, "
+ + "\"name,4\" <address4@dom4.org>, "
+ + "\"big \\\"G\\\"\" <bigG@dom5.net>, "
+ + "=?UTF-8?B?5pel5pys6Kqe?= <address6@co.jp>, "
+ + "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
+ line);
+ }
+
+ /**
+ * Test various combinations of the toFriendly (single) method
+ */
+ public void testToFriendlySingle() {
+ assertEquals("personal1", mAddress1.toFriendly());
+ assertEquals("address2", mAddress2.toFriendly());
+ assertEquals("address3", mAddress3.toFriendly());
+ }
+
+ /**
+ * Test various combinations of the toFriendly (array) method
+ */
+ public void testToFriendlyArray() {
+ Address[] list1 = null;
+ Address[] list2 = new Address[0];
+ Address[] list3 = new Address[] { mAddress1 };
+ Address[] list4 = new Address[] { mAddress1, mAddress2, mAddress3 };
+
+ assertEquals(null, Address.toFriendly(list1));
+ assertEquals(null, Address.toFriendly(list2));
+ assertEquals("personal1", Address.toFriendly(list3));
+ assertEquals("personal1, address2, address3", Address.toFriendly(list4));
+ }
+
+ /**
+ * Simple quick checks of empty-input edge conditions for pack()
+ *
+ * NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
+ * behavior while I am changing some of the code in the function under test.
+ */
+ public void testEmptyPack() {
+ String result;
+
+ // null input => null string
+ result = Address.pack(null);
+ assertNull("packing null", result);
+
+ // zero-length input => null string
+ result = Address.pack(new Address[] { });
+ assertNull("packing empty array", result);
+ }
+
+ /**
+ * Simple quick checks of empty-input edge conditions for unpack()
+ *
+ * NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
+ * behavior while I am changing some of the code in the function under test.
+ */
+ public void testEmptyUnpack() {
+ Address[] result;
+
+ /*
+ // null input => empty array
+ result = Address.unpack(null);
+ assertTrue("unpacking null address", result != null && result.length == 0);
+ */
+ // empty string input => empty array
+ result = Address.unpack("");
+ assertTrue("unpacking zero-length", result != null && result.length == 0);
+ }
+
+ private static boolean addressEquals(Address a1, Address a2) {
+ if (!a1.equals(a2)) {
+ return false;
+ }
+ final String displayName1 = a1.getPersonal();
+ final String displayName2 = a2.getPersonal();
+ if (displayName1 == null) {
+ return displayName2 == null;
+ } else {
+ return displayName1.equals(displayName2);
+ }
+ }
+
+ private static boolean addressArrayEquals(Address[] array1, Address[] array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ for (int i = array1.length - 1; i >= 0; --i) {
+ if (!addressEquals(array1[i], array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void testPackUnpack() {
+ for (Address[] list : PACK_CASES) {
+ String packed = Address.pack(list);
+ assertTrue(packed, addressArrayEquals(list, Address.unpack(packed)));
+ }
+ }
+
+ /**
+ * Tests that unpackToString() returns the same result as toString(unpack()).
+ */
+ public void testUnpackToString() {
+ assertNull(Address.unpackToString(null));
+ assertNull(Address.unpackToString(""));
+
+ for (Address[] list : PACK_CASES) {
+ String packed = Address.pack(list);
+ String s1 = Address.unpackToString(packed);
+ String s2 = Address.toString(Address.unpack(packed));
+ assertEquals(s2, s2, s1);
+ }
+ }
+
+ /**
+ * Tests that parseAndPack() returns the same result as pack(parse()).
+ */
+ public void testParseAndPack() {
+ String s1 = Address.parseAndPack(MULTI_ADDRESSES_LIST);
+ String s2 = Address.pack(Address.parse(MULTI_ADDRESSES_LIST));
+ assertEquals(s2, s1);
+ }
+
+ public void testSinglePack() {
+ Address[] addrArray = new Address[1];
+ for (Address address : new Address[]{PACK_ADDR_1, PACK_ADDR_2, PACK_ADDR_3}) {
+ String packed1 = address.pack();
+ addrArray[0] = address;
+ String packed2 = Address.pack(addrArray);
+ assertEquals(packed1, packed2);
+ }
+ }
+
+ /**
+ * Tests that:
+ * 1. unpackFirst() with empty list returns null.
+ * 2. unpackFirst() with non-empty returns the same as unpack()[0]
+ */
+ public void testUnpackFirst() {
+ assertNull(Address.unpackFirst(null));
+ assertNull(Address.unpackFirst(""));
+
+ for (Address[] list : PACK_CASES) {
+ String packed = Address.pack(list);
+ Address[] array = Address.unpack(packed);
+ Address first = Address.unpackFirst(packed);
+ assertTrue(packed, addressEquals(array[0], first));
+ }
+ }
+
+ public void testIsValidAddress() {
+ String notValid[] = {"", "foo", "john@", "x@y", "x@y.", "foo.com"};
+ String valid[] = {"x@y.z", "john@gmail.com", "a@b.c.d"};
+ for (String address : notValid) {
+ assertTrue(address, !Address.isValidAddress(address));
+ }
+ for (String address : valid) {
+ assertTrue(address, Address.isValidAddress(address));
+ }
+
+ // isAllValid() must accept empty address list as valid
+ assertTrue("Empty address list is valid", Address.isAllValid(""));
+ }
+
+ /**
+ * Legacy pack() used for testing legacyUnpack().
+ * The packed list is a comma separated list of:
+ * URLENCODE(address)[;URLENCODE(personal)]
+ * @See pack()
+ */
+ private static String legacyPack(Address[] addresses) {
+ if (addresses == null) {
+ return null;
+ } else if (addresses.length == 0) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, count = addresses.length; i < count; i++) {
+ Address address = addresses[i];
+ try {
+ sb.append(URLEncoder.encode(address.getAddress(), "UTF-8"));
+ if (address.getPersonal() != null) {
+ sb.append(';');
+ sb.append(URLEncoder.encode(address.getPersonal(), "UTF-8"));
+ }
+ if (i < count - 1) {
+ sb.append(',');
+ }
+ }
+ catch (UnsupportedEncodingException uee) {
+ return null;
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/tests/src/com/android/mail/providers/AddressTests.java b/tests/src/com/android/mail/providers/AddressTests.java
deleted file mode 100644
index 01bd173..0000000
--- a/tests/src/com/android/mail/providers/AddressTests.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2011, Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mail.providers;
-
-import android.test.AndroidTestCase;
-
-public class AddressTests extends AndroidTestCase {
-
- public void testIsValid() {
- assertTrue(Address.isValidAddress("\"Daisuké Miyakawa (宮川 大輔)\" <dmiyakawa@google.com>"));
- assertTrue(Address.isValidAddress("\"宮川 大輔\" <dmiyakawa@google.com>"));
- }
-}
\ No newline at end of file