Add hashCode().

Bug: 4066223
Change-Id: I917761014753605ee06f53cbc6499ca9fe9a7956
diff --git a/java/com/android/vcard/VCardEntry.java b/java/com/android/vcard/VCardEntry.java
index fb26cb0..413050c 100644
--- a/java/com/android/vcard/VCardEntry.java
+++ b/java/com/android/vcard/VCardEntry.java
@@ -131,7 +131,7 @@
         private String mPhoneticMiddle;
 
         // For "SORT-STRING" in vCard 3.0.
-        public String sortString;
+        private String mSortString;
 
         /**
          * Not in vCard but for {@link StructuredName#DISPLAY_NAME}. This field
@@ -197,7 +197,7 @@
             // SORT-STRING is used only when phonetic names aren't specified in
             // the original vCard.
             if (!phoneticNameSpecified) {
-                builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, sortString);
+                builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mSortString);
             }
 
             builder.withValue(StructuredName.DISPLAY_NAME, displayName);
@@ -210,7 +210,7 @@
                     && TextUtils.isEmpty(mGiven) && TextUtils.isEmpty(mPrefix)
                     && TextUtils.isEmpty(mSuffix) && TextUtils.isEmpty(mFormatted)
                     && TextUtils.isEmpty(mPhoneticFamily) && TextUtils.isEmpty(mPhoneticMiddle)
-                    && TextUtils.isEmpty(mPhoneticGiven) && TextUtils.isEmpty(sortString));
+                    && TextUtils.isEmpty(mPhoneticGiven) && TextUtils.isEmpty(mSortString));
         }
 
         @Override
@@ -231,8 +231,20 @@
                     && TextUtils.equals(mFormatted, nameData.mFormatted)
                     && TextUtils.equals(mPhoneticFamily, nameData.mPhoneticFamily)
                     && TextUtils.equals(mPhoneticMiddle, nameData.mPhoneticMiddle)
-                    && TextUtils.equals(mPhoneticGiven, nameData.mPhoneticGiven) && TextUtils
-                    .equals(sortString, nameData.sortString));
+                    && TextUtils.equals(mPhoneticGiven, nameData.mPhoneticGiven)
+                    && TextUtils.equals(mSortString, nameData.mSortString));
+        }
+
+        @Override
+        public int hashCode() {
+            final String[] hashTargets = new String[] {mFamily, mMiddle, mGiven, mPrefix, mSuffix,
+                    mFormatted, mPhoneticFamily, mPhoneticMiddle,
+                    mPhoneticGiven, mSortString};
+            int hash = 0;
+            for (String hashTarget : hashTargets) {
+                hash = hash * 31 + (hashTarget != null ? hashTarget.hashCode() : 0);
+            }
+            return hash;
         }
 
         @Override
@@ -270,6 +282,10 @@
             return mFormatted;
         }
 
+        public String getSortString() {
+            return mSortString;
+        }
+
         /** @hide Just for testing. */
         public void setFamily(String family) { mFamily = family; }
         /** @hide Just for testing. */
@@ -339,6 +355,15 @@
         }
 
         @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + (mNumber != null ? mNumber.hashCode() : 0);
+            hash = hash * 31 + (mLabel != null ? mLabel.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format("type: %d, data: %s, label: %s, isPrimary: %s", mType, mNumber,
                     mLabel, mIsPrimary);
@@ -420,6 +445,15 @@
         }
 
         @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + (mAddress != null ? mAddress.hashCode() : 0);
+            hash = hash * 31 + (mLabel != null ? mLabel.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format("type: %d, data: %s, label: %s, isPrimary: %s", mType, mAddress,
                     mLabel, mIsPrimary);
@@ -593,10 +627,13 @@
 
         @Override
         public boolean isEmpty() {
-            return (TextUtils.isEmpty(mPobox) && TextUtils.isEmpty(mExtendedAddress)
-                    && TextUtils.isEmpty(mStreet) && TextUtils.isEmpty(mLocalty)
-                    && TextUtils.isEmpty(mRegion) && TextUtils.isEmpty(mPostalCode) && TextUtils
-                    .isEmpty(mCountry));
+            return (TextUtils.isEmpty(mPobox)
+                    && TextUtils.isEmpty(mExtendedAddress)
+                    && TextUtils.isEmpty(mStreet)
+                    && TextUtils.isEmpty(mLocalty)
+                    && TextUtils.isEmpty(mRegion)
+                    && TextUtils.isEmpty(mPostalCode)
+                    && TextUtils.isEmpty(mCountry));
         }
 
         @Override
@@ -610,7 +647,8 @@
             final PostalData postalData = (PostalData) obj;
             return (mType == postalData.mType)
                     && (mType == StructuredPostal.TYPE_CUSTOM ? TextUtils.equals(mLabel,
-                            postalData.mLabel) : true) && (mIsPrimary == postalData.mIsPrimary)
+                            postalData.mLabel) : true)
+                    && (mIsPrimary == postalData.mIsPrimary)
                     && TextUtils.equals(mPobox, postalData.mPobox)
                     && TextUtils.equals(mExtendedAddress, postalData.mExtendedAddress)
                     && TextUtils.equals(mStreet, postalData.mStreet)
@@ -621,6 +659,20 @@
         }
 
         @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + (mLabel != null ? mLabel.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+
+            final String[] hashTargets = new String[] {mPobox, mExtendedAddress, mStreet,
+                    mLocalty, mRegion, mPostalCode, mCountry};
+            for (String hashTarget : hashTargets) {
+                hash = hash * 31 + (hashTarget != null ? hashTarget.hashCode() : 0);
+            }
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format("type: %d, label: %s, isPrimary: %s, pobox: %s, "
                     + "extendedAddress: %s, street: %s, localty: %s, region: %s, postalCode %s, "
@@ -768,6 +820,16 @@
         }
 
         @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + (mOrganizationName != null ? mOrganizationName.hashCode() : 0);
+            hash = hash * 31 + (mDepartmentName != null ? mDepartmentName.hashCode() : 0);
+            hash = hash * 31 + (mTitle != null ? mTitle.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format(
                     "type: %d, organization: %s, department: %s, title: %s, isPrimary: %s", mType,
@@ -853,13 +915,24 @@
                 return false;
             }
             ImData imData = (ImData) obj;
-            return (mType == imData.mType && mProtocol == imData.mProtocol
+            return (mType == imData.mType
+                    && mProtocol == imData.mProtocol
                     && TextUtils.equals(mCustomProtocol, imData.mCustomProtocol)
                     && TextUtils.equals(mAddress, imData.mAddress)
                     && (mIsPrimary == imData.mIsPrimary));
         }
 
         @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + mProtocol;
+            hash = hash * 31 + (mCustomProtocol != null ? mCustomProtocol.hashCode() : 0);
+            hash = hash * 31 + (mAddress != null ? mAddress.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format(
                     "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s", mType,
@@ -906,6 +979,8 @@
         // TODO: make this private. Currently the app outside this class refers to this.
         public final byte[] photoBytes;
 
+        private Integer mHashCode = null;
+
         public PhotoData(String format, byte[] photoBytes, boolean isPrimary) {
             mFormat = format;
             this.photoBytes = photoBytes;
@@ -946,6 +1021,25 @@
         }
 
         @Override
+        public int hashCode() {
+            if (mHashCode != null) {
+                return mHashCode;
+            }
+
+            int hash = mFormat != null ? mFormat.hashCode() : 0;
+            hash = hash * 31;
+            if (photoBytes != null) {
+                for (byte b : photoBytes) {
+                    hash += b;
+                }
+            }
+
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            mHashCode = hash;
+            return hash;
+        }
+
+        @Override
         public String toString() {
             return String.format("format: %s: size: %d, isPrimary: %s", mFormat, photoBytes.length,
                     mIsPrimary);
@@ -1003,6 +1097,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return mNickname != null ? mNickname.hashCode() : 0;
+        }
+
+        @Override
         public String toString() {
             return "nickname: " + mNickname;
         }
@@ -1053,6 +1152,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return mNote != null ? mNote.hashCode() : 0;
+        }
+
+        @Override
         public String toString() {
             return "note: " + mNote;
         }
@@ -1106,6 +1210,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return mWebsite != null ? mWebsite.hashCode() : 0;
+        }
+
+        @Override
         public String toString() {
             return "website: " + mWebsite;
         }
@@ -1157,6 +1266,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return mBirthday != null ? mBirthday.hashCode() : 0;
+        }
+
+        @Override
         public String toString() {
             return "birthday: " + mBirthday;
         }
@@ -1208,6 +1322,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return mAnniversary != null ? mAnniversary.hashCode() : 0;
+        }
+
+        @Override
         public String toString() {
             return "anniversary: " + mAnniversary;
         }
@@ -1274,7 +1393,19 @@
                 return false;
             }
             SipData sipData = (SipData) obj;
-            return TextUtils.equals(mAddress, sipData.mAddress);
+            return (mType == sipData.mType
+                    && TextUtils.equals(mLabel, sipData.mLabel)
+                    && TextUtils.equals(mAddress, sipData.mAddress)
+                    && (mIsPrimary == sipData.mIsPrimary));
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = mType;
+            hash = hash * 31 + (mLabel != null ? mLabel.hashCode() : 0);
+            hash = hash * 31 + (mAddress != null ? mAddress.hashCode() : 0);
+            hash = hash * 31 + (mIsPrimary ? 1231 : 1237);
+            return hash;
         }
 
         @Override
@@ -1380,6 +1511,17 @@
         }
 
         @Override
+        public int hashCode() {
+            int hash = mMimeType != null ? mMimeType.hashCode() : 0;
+            if (mDataList != null) {
+                for (String data : mDataList) {
+                    hash = hash * 31 + (data != null ? data.hashCode() : 0);
+                }
+            }
+            return hash;
+        }
+
+        @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
             builder.append("android-custom: " + mMimeType + ", data: ");
@@ -1990,7 +2132,7 @@
         } else if (propertyName.equals(VCardConstants.PROPERTY_N)) {
             handleNProperty(propertyValueList, paramMap);
         } else if (propertyName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
-            mNameData.sortString = propValue;
+            mNameData.mSortString = propValue;
         } else if (propertyName.equals(VCardConstants.PROPERTY_NICKNAME)
                 || propertyName.equals(VCardConstants.ImportOnly.PROPERTY_X_NICKNAME)) {
             addNickName(propValue);