aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/plugin/addrbook
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java/sip/communicator/plugin/addrbook')
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java7
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java2757
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java457
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java92
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java2005
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java487
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java143
7 files changed, 3276 insertions, 2672 deletions
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
index 354a361..8b211c2 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
@@ -185,6 +185,13 @@ extends EditableContactDetail
}
super.setDetail(value);
+
+ EditableSourceContact sourceContact = getSourceContact();
+ if(sourceContact != null
+ && sourceContact instanceof MacOSXAddrBookSourceContact)
+ {
+ ((MacOSXAddrBookSourceContact) sourceContact).updated();
+ }
}
/**
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
index 3f6126a..ebf1f03 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
@@ -1,1352 +1,1405 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.plugin.addrbook.macosx;
-
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.contactsource.ContactDetail.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implements <tt>ContactQuery</tt> for the Address Book of Mac OS X.
- *
- * @author Lyubomir Marinov
- */
-public class MacOSXAddrBookContactQuery
- extends AbstractAddrBookContactQuery<MacOSXAddrBookContactSourceService>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookContactQuery</tt> class
- * and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(MacOSXAddrBookContactQuery.class);
-
- /**
- * The properties of <tt>ABPerson</tt> which are to be queried by the
- * <tt>MacOSXAddrBookContactQuery</tt> instances.
- */
- public static final long[] ABPERSON_PROPERTIES
- = new long[]
- {
- kABAIMInstantProperty(),
- kABEmailProperty(),
- kABFirstNameProperty(),
- kABFirstNamePhoneticProperty(),
- kABICQInstantProperty(),
- kABJabberInstantProperty(),
- kABLastNameProperty(),
- kABLastNamePhoneticProperty(),
- kABMiddleNameProperty(),
- kABMiddleNamePhoneticProperty(),
- kABMSNInstantProperty(),
- kABNicknameProperty(),
- kABPhoneProperty(),
- kABYahooInstantProperty(),
- kABPersonFlags(),
- kABOrganizationProperty(),
- kABMaidenNameProperty(),
- kABBirthdayProperty(),
- kABJobTitleProperty(),
- kABHomePageProperty(),
- kABURLsProperty(),
- kABCalendarURIsProperty(),
- kABAddressProperty(),
- kABOtherDatesProperty(),
- kABRelatedNamesProperty(),
- kABDepartmentProperty(),
- kABNoteProperty(),
- kABTitleProperty(),
- kABSuffixProperty()
- };
-
- /**
- * The index of the <tt>kABAIMInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABAIMInstantProperty = 0;
-
- /**
- * The index of the <tt>kABEmailProperty</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABEmailProperty = 1;
-
- /**
- * The index of the <tt>kABFirstNameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABFirstNameProperty = 2;
-
- /**
- * The index of the <tt>kABFirstNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABFirstNamePhoneticProperty = 3;
-
- /**
- * The index of the <tt>kABICQInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABICQInstantProperty = 4;
-
- /**
- * The index of the <tt>kABJabberInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABJabberInstantProperty = 5;
-
- /**
- * The index of the <tt>kABLastNameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABLastNameProperty = 6;
-
- /**
- * The index of the <tt>kABLastNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABLastNamePhoneticProperty = 7;
-
- /**
- * The index of the <tt>kABMiddleNameProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMiddleNameProperty = 8;
-
- /**
- * The index of the <tt>kABMiddleNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMiddleNamePhoneticProperty = 9;
-
- /**
- * The index of the <tt>kABMSNInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMSNInstantProperty = 10;
-
- /**
- * The index of the <tt>kABNicknameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABNicknameProperty = 11;
-
- /**
- * The index of the <tt>kABOrganizationProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABOrganizationProperty = 15;
-
- /**
- * The index of the <tt>kABPersonFlags</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABPersonFlags = 14;
-
- /**
- * The index of the <tt>kABPhoneProperty</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABPhoneProperty = 12;
-
- /**
- * The flag which indicates that an <tt>ABRecord</tt> is to be displayed as
- * a company.
- */
- public static final long kABShowAsCompany = 1;
-
- /**
- * The mask which extracts the <tt>kABShowAsXXX</tt> flag from the
- * <tt>personFlags</tt> of an <tt>ABPerson</tt>.
- */
- public static final long kABShowAsMask = 7;
-
- /**
- * The index of the <tt>kABYahooInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABYahooInstantProperty = 13;
-
- /**
- * The index of the <tt>kABMaidenNameProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMaidenNameProperty = 16;
-
- /**
- * The index of the <tt>kABBirthdayProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABBirthdayProperty = 17;
-
- /**
- * The index of the <tt>kABJobTitleProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABJobTitleProperty = 18;
-
- /**
- * The index of the <tt>kABHomePageProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABHomePageProperty = 19;
-
- /**
- * The index of the <tt>kABURLsProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABURLsProperty = 20;
-
- /**
- * The index of the <tt>kABCalendarURIsProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABCalendarURIsProperty = 21;
-
- /**
- * The index of the <tt>kABAddressProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABAddressProperty = 22;
-
- /**
- * The index of the <tt>kABOtherDatesProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABOtherDatesProperty = 23;
-
- /**
- * The index of the <tt>kABRelatedNamesProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABRelatedNamesProperty = 24;
-
- /**
- * The index of the <tt>kABDepartmentProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABDepartmentProperty = 25;
-
- /**
- * The index of the <tt>kABNoteProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABNoteProperty = 26;
-
- /**
- * The index of the <tt>kABTitleProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABTitleProperty = 27;
-
- /**
- * The index of the <tt>kABSuffixProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABSuffixProperty = 28;
-
- /**
- * The regex which matches the superfluous parts of an <tt>ABMultiValue</tt>
- * label.
- */
- private static final Pattern LABEL_PATTERN
- = Pattern.compile(
- "kAB|Email|Phone|Label|(\\p{Punct}*)",
- Pattern.CASE_INSENSITIVE);
-
- static
- {
- System.loadLibrary("jmacosxaddrbook");
- }
-
- /**
- * Initializes a new <tt>MacOSXAddrBookContactQuery</tt> which is to perform
- * a specific <tt>query</tt> in the Address Book of Mac OS X on behalf of a
- * specific <tt>MacOSXAddrBookContactSourceService</tt>.
- *
- * @param contactSource the <tt>MacOSXAddrBookContactSourceService</tt>
- * which is to perform the new <tt>ContactQuery</tt> instance
- * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> i.e.
- * the Address Book of Mac OS X is being queried
- */
- public MacOSXAddrBookContactQuery(
- MacOSXAddrBookContactSourceService contactSource,
- Pattern query)
- {
- super(contactSource, query);
- }
-
- /**
- * Gets the <tt>imageData</tt> of a specific <tt>ABPerson</tt> instance.
- *
- * @param person the pointer to the <tt>ABPerson</tt> instance to get the
- * <tt>imageData</tt> of
- * @return the <tt>imageData</tt> of the specified <tt>ABPerson</tt>
- * instance
- */
- public static native byte[] ABPerson_imageData(long person);
-
- /**
- * Gets the values of a specific set of <tt>ABRecord</tt> properties for a
- * specific <tt>ABRecord</tt> instance.
- *
- * @param record the pointer to the <tt>ABRecord</tt> to get the property
- * values of
- * @param properties the set of <tt>ABRecord</tt> properties to get the
- * values of
- * @return the values of the specified set of <tt>ABRecord</tt> properties
- * for the specified <tt>ABRecord</tt> instance
- */
- public static native Object[] ABRecord_valuesForProperties(
- long record,
- long[] properties);
-
- /**
- * Returns the unique id of a record.
- * @param record the record which id is retrieved.
- * @return the record id.
- */
- public static native String ABRecord_uniqueId(long record);
-
- /**
- * Sets property for the supplied person id.
- * @param id the person id
- * @param property the property to use.
- * @param subPropety any sub property if available.
- * @param value the value to set.
- * @return whether the result was successfully added.
- */
- public static native boolean setProperty(
- String id, long property, String subPropety, Object value);
-
- /**
- * Remove a property.
- * @param id the person id.
- * @param property the property.
- * @return whether the result was successfully removed.
- */
- public static native boolean removeProperty(String id, long property);
-
- /**
- * Initializes a new <tt>ContactDetail</tt> instance which is to reperesent
- * a specific contact address that is the value of a specific
- * <tt>ABPerson</tt> property and, optionally, has a specific label.
- *
- * @param property the index in {@link #ABPERSON_PROPERTIES} of the
- * <tt>ABPerson</tt> property to be represented by <tt>ContactDetail</tt>
- * @param contactAddress the contact address to be represented by the new
- * <tt>ContactDetail</tt> instance
- * @param label an optional label to be added to the set of labels, if any,
- * determined by <tt>property</tt>
- * @param id The id of the detail.
- *
- * @return a new <tt>ContactDetail</tt> instance which represents the
- * specified <tt>contactAddress</tt>
- */
- private ContactDetail createContactDetail(
- int property,
- String contactAddress,
- Object label,
- String additionalProperty,
- String id)
- {
- Category c;
- SubCategory sc = null;
-
- switch (property)
- {
- case kABEmailProperty:
- c = Category.Email;
- break;
- case kABPhoneProperty:
- c = Category.Phone;
- break;
- case kABAIMInstantProperty:
- sc = SubCategory.AIM;
- c = Category.InstantMessaging;
- break;
- case kABICQInstantProperty:
- sc = SubCategory.ICQ;
- c = Category.InstantMessaging;
- break;
- case kABJabberInstantProperty:
- sc = SubCategory.Jabber;
- c = Category.InstantMessaging;
- break;
- case kABMSNInstantProperty:
- sc = SubCategory.MSN;
- c = Category.InstantMessaging;
- break;
- case kABYahooInstantProperty:
- sc = SubCategory.Yahoo;
- c = Category.InstantMessaging;
- break;
- case kABMaidenNameProperty:
- case kABFirstNameProperty:
- sc = SubCategory.Name;
- c = Category.Personal;
- break;
- case kABFirstNamePhoneticProperty:
- sc = SubCategory.Name;
- c = Category.Personal;
- break;
- case kABLastNameProperty:
- sc = SubCategory.LastName;
- c = Category.Personal;
- break;
- case kABLastNamePhoneticProperty:
- sc = SubCategory.LastName;
- c = Category.Personal;
- break;
- case kABMiddleNameProperty:
- case kABMiddleNamePhoneticProperty:
- case kABNicknameProperty:
- sc = SubCategory.Nickname;
- c = Category.Personal;
- break;
- case kABBirthdayProperty:
- case kABURLsProperty:
- case kABHomePageProperty:
- sc = SubCategory.HomePage;
- c = Category.Personal;
- break;
- case kABOtherDatesProperty:
- case kABRelatedNamesProperty:
- case kABNoteProperty:
- case kABTitleProperty:
- case kABSuffixProperty:
- c = Category.Personal;
- break;
- case kABOrganizationProperty:
- case kABJobTitleProperty:
- sc = SubCategory.JobTitle;
- c = Category.Organization;
- break;
- case kABDepartmentProperty:
- c = Category.Organization;
- sc = SubCategory.Name;
- break;
- case kABAddressProperty:
- c = Category.Address;
- break;
- default:
- c = null;
- break;
- }
-
- if (sc == null)
- {
- if (label == null)
- sc = null;
- else
- {
- sc = getSubCategoryFromLabel(label);
- }
- }
-
- SubCategory[] subCategories;
- SubCategory additionalSubCategory = null;
-
- if(additionalProperty != null)
- additionalSubCategory = getSubCategoryFromLabel(additionalProperty);
-
- if(additionalSubCategory != null)
- subCategories = new SubCategory[]
- { sc, additionalSubCategory };
- else
- subCategories = new SubCategory[]{ sc };
-
- return new MacOSXAddrBookContactDetail(
- property,
- contactAddress,
- c,
- subCategories,
- additionalProperty,
- id);
- }
-
- /**
- * Returns the SubCategory corresponding to the given label.
- *
- * @param label the label to match to a <tt>SubDirectory</tt>
- * @return the <tt>SubDirectory</tt> corresponding to the
- * given label
- */
- private SubCategory getSubCategoryFromLabel(Object label)
- {
- String labelString
- = LABEL_PATTERN.matcher((String) label).replaceAll("").trim();
-
- if (labelString.length() < 1)
- return null;
-
- SubCategory subCategory = null;
-
- if (labelString.equalsIgnoreCase("home"))
- subCategory = SubCategory.Home;
- else if (labelString.equalsIgnoreCase("work"))
- subCategory = SubCategory.Work;
- else if (labelString.equalsIgnoreCase("other"))
- subCategory = SubCategory.Other;
- else if (labelString.equalsIgnoreCase("mobile"))
- subCategory = SubCategory.Mobile;
- else if (labelString.equalsIgnoreCase("homepage"))
- subCategory = SubCategory.HomePage;
- else if (labelString.equalsIgnoreCase("street"))
- subCategory = SubCategory.Street;
- else if (labelString.equalsIgnoreCase("state"))
- subCategory = SubCategory.State;
- else if (labelString.equalsIgnoreCase("ZIP"))
- subCategory = SubCategory.PostalCode;
- else if (labelString.equalsIgnoreCase("country"))
- subCategory = SubCategory.Country;
- else if (labelString.equalsIgnoreCase("city"))
- subCategory = SubCategory.City;
- else if (labelString.equalsIgnoreCase("InstantMessageUsername"))
- subCategory = SubCategory.Nickname;
- else if (labelString.equalsIgnoreCase("workfax"))
- subCategory = SubCategory.Fax;
- else if (labelString.equalsIgnoreCase("fax"))
- subCategory = SubCategory.Fax;
-
- return subCategory;
- }
-
- /**
- * Calls back to a specific <tt>PtrCallback</tt> for each <tt>ABPerson</tt>
- * found in the Address Book of Mac OS X which matches a specific
- * <tt>String</tt> query.
- *
- * @param query the <tt>String</tt> for which the Address Book of Mac OS X
- * is to be queried. <b>Warning</b>: Ignored at the time of this writing.
- * @param callback the <tt>PtrCallback</tt> to be notified about the
- * matching <tt>ABPerson</tt>s
- */
- private static native void foreachPerson(
- String query,
- PtrCallback callback);
-
- /**
- * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent an <tt>ABPerson</tt> specified by the values of its
- * {@link #ABPERSON_PROPERTIES}.
- *
- * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
- * represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of
- * @param id The id of the detail.
- *
- * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
- */
- private List<ContactDetail> getContactDetails(Object[] values, String id)
- {
- List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
-
- for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
- {
- int property = i;
- Object value = values[property];
-
- if (value instanceof String)
- {
- String stringValue = (String) value;
-
- if (stringValue.length() != 0)
- {
- if (kABPhoneProperty == property)
- stringValue
- = PhoneNumberI18nService.normalize(stringValue);
-
- contactDetails.add(
- setCapabilities(
- createContactDetail(
- property,
- stringValue,
- null,
- null,
- id),
- property));
- }
- }
- else if (value instanceof Object[])
- {
- parseMultiDetails(contactDetails,
- (Object[]) value,
- property,
- null,
- id);
- }
- }
- return contactDetails;
- }
-
- /**
- * Parses the multi value data resulting it in contact details.
- * @param contactDetails the result list
- * @param multiValue the values to parse.
- * @param property the current property being parsed.
- * @param id The id of the detail.
- */
- private void parseMultiDetails(
- List<ContactDetail> contactDetails,
- Object[] multiValue,
- int property,
- String label,
- String id)
- {
- if(multiValue == null)
- return;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
-
- if (subValue instanceof String)
- {
- String stringSubValue = (String) subValue;
-
- if (stringSubValue.length() != 0)
- {
- if (kABPhoneProperty == property)
- {
- stringSubValue = PhoneNumberI18nService
- .normalize(stringSubValue);
- }
-
- Object l = multiValue[multiValueIndex + 1];
-
- contactDetails.add(
- setCapabilities(
- createContactDetail(
- property,
- stringSubValue,
- l,
- label,
- id),
- property));
- }
- }
- else if (subValue instanceof Object[])
- {
- String l = null;
-
- Object lObject = multiValue[multiValueIndex + 1];
- if(lObject instanceof String)
- l = (String)lObject;
-
- parseMultiDetails(contactDetails,
- (Object[]) subValue,
- property,
- l,
- id);
- }
- }
- }
-
- /**
- * Gets the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent an <tt>ABPerson</tt> specified by the values of its
- * {@link #ABPERSON_PROPERTIES}.
- *
- * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
- * represent the <tt>ABPerson</tt> to get the <tt>displayName</tt> of
- * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
- */
- static String getDisplayName(Object[] values)
- {
- long personFlags
- = (values[kABPersonFlags] instanceof Long)
- ? ((Long) values[kABPersonFlags]).longValue()
- : 0;
- String displayName;
-
- if ((personFlags & kABShowAsMask) == kABShowAsCompany)
- {
- displayName
- = (values[kABOrganizationProperty] instanceof String)
- ? (String) values[kABOrganizationProperty]
- : "";
- if (displayName.length() != 0)
- return displayName;
- }
-
- displayName
- = (values[kABNicknameProperty] instanceof String)
- ? (String) values[kABNicknameProperty]
- : "";
- if (displayName.length() != 0)
- return displayName;
-
- String firstName
- = (values[kABFirstNameProperty] instanceof String)
- ? (String) values[kABFirstNameProperty]
- : "";
-
- if ((firstName.length() == 0)
- && (values[kABFirstNamePhoneticProperty] instanceof String))
- {
- firstName = (String) values[kABFirstNamePhoneticProperty];
- }
-
- String lastName
- = (values[kABLastNameProperty] instanceof String)
- ? (String) values[kABLastNameProperty]
- : "";
-
- if ((lastName.length() == 0)
- && (values[kABLastNamePhoneticProperty] instanceof String))
- lastName = (String) values[kABLastNamePhoneticProperty];
- if ((lastName.length() == 0)
- && (values[kABMiddleNameProperty] instanceof String))
- lastName = (String) values[kABMiddleNameProperty];
- if ((lastName.length() == 0)
- && (values[kABMiddleNamePhoneticProperty] instanceof String))
- lastName = (String) values[kABMiddleNamePhoneticProperty];
-
- if (firstName.length() == 0)
- displayName = lastName;
- else
- {
- displayName
- = (lastName.length() == 0)
- ? firstName
- : (firstName + " " + lastName);
- }
- if (displayName.length() != 0)
- return displayName;
-
- for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
- {
- Object value = values[i];
-
- if (value instanceof String)
- {
- String stringValue = (String) value;
-
- if (stringValue.length() != 0)
- {
- displayName = stringValue;
- break;
- }
- }
- else if (value instanceof Object[])
- {
- Object[] multiValue = (Object[]) value;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
-
- if (subValue instanceof String)
- {
- String stringSubValue = (String) subValue;
-
- if (stringSubValue.length() != 0)
- {
- displayName = stringSubValue;
- break;
- }
- }
- }
- }
- }
- return displayName;
- }
-
- /**
- * Gets the value of the <tt>kABAIMInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABAIMInstantProperty</tt> constant
- */
- public static native long kABAIMInstantProperty();
-
- /**
- * Gets the value of the <tt>kABEmailProperty</tt> constant.
- *
- * @return the value of the <tt>kABEmailProperty</tt> constant
- */
- public static native long kABEmailProperty();
-
- /**
- * Gets the value of the <tt>kABFirstNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABFirstNameProperty</tt> constant
- */
- public static native long kABFirstNameProperty();
-
- /**
- * Gets the value of the <tt>kABFirstNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABFirstNamePhoneticProperty</tt> constant
- */
- public static native long kABFirstNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABICQInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABICQInstantProperty</tt> constant
- */
- public static native long kABICQInstantProperty();
-
- /**
- * Gets the value of the <tt>kABJabberInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABJabberInstantProperty</tt> constant
- */
- public static native long kABJabberInstantProperty();
-
- /**
- * Gets the value of the <tt>kABLastNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABLastNameProperty</tt> constant
- */
- public static native long kABLastNameProperty();
-
- /**
- * Gets the value of the <tt>kABLastNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABLastNamePhoneticProperty</tt> constant
- */
- public static native long kABLastNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABMiddleNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABMiddleNameProperty</tt> constant
- */
- public static native long kABMiddleNameProperty();
-
- /**
- * Gets the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant
- */
- public static native long kABMiddleNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABMSNInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABMSNInstantProperty</tt> constant
- */
- public static native long kABMSNInstantProperty();
-
- /**
- * Gets the value of the <tt>kABNicknameProperty</tt> constant.
- *
- * @return the value of the <tt>kABNicknameProperty</tt> constant
- */
- public static native long kABNicknameProperty();
-
- /**
- * Gets the value of the <tt>kABOrganizationProperty</tt> constant.
- *
- * @return the value of the <tt>kABOrganizationProperty</tt> constant
- */
- public static native long kABOrganizationProperty();
-
- /**
- * Gets the value of the <tt>kABPersonFlags</tt> constant.
- *
- * @return the value of the <tt>kABPersonFlags</tt> constant
- */
- public static native long kABPersonFlags();
-
- /**
- * Gets the value of the <tt>kABPhoneProperty</tt> constant.
- *
- * @return the value of the <tt>kABPhoneProperty</tt> constant
- */
- public static native long kABPhoneProperty();
-
- /**
- * Gets the value of the <tt>kABYahooInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABYahooInstantProperty</tt> constant
- */
- public static native long kABYahooInstantProperty();
-
- /**
- * Gets the value of the <tt>kABMaidenNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABMaidenNameProperty</tt> constant
- */
- public static native long kABMaidenNameProperty();
-
- /**
- * Gets the value of the <tt>kABBirthdayProperty</tt> constant.
- *
- * @return the value of the <tt>kABBirthdayProperty</tt> constant
- */
- public static native long kABBirthdayProperty();
-
- /**
- * Gets the value of the <tt>kABJobTitleProperty</tt> constant.
- *
- * @return the value of the <tt>kABJobTitleProperty</tt> constant
- */
- public static native long kABJobTitleProperty();
-
- /**
- * Gets the value of the <tt>kABHomePageProperty</tt> constant.
- *
- * @return the value of the <tt>kABHomePageProperty</tt> constant
- */
- public static native long kABHomePageProperty();
-
- /**
- * Gets the value of the <tt>kABURLsProperty</tt> constant.
- *
- * @return the value of the <tt>kABURLsProperty</tt> constant
- */
- public static native long kABURLsProperty();
-
- /**
- * Gets the value of the <tt>kABCalendarURIsProperty</tt> constant.
- *
- * @return the value of the <tt>kABCalendarURIsProperty</tt> constant
- */
- public static native long kABCalendarURIsProperty();
-
- /**
- * Gets the value of the <tt>kABAddressProperty</tt> constant.
- *
- * @return the value of the <tt>kABAddressProperty</tt> constant
- */
- public static native long kABAddressProperty();
-
- /**
- * Gets the value of the <tt>kABOtherDatesProperty</tt> constant.
- *
- * @return the value of the <tt>kABOtherDatesProperty</tt> constant
- */
- public static native long kABOtherDatesProperty();
-
- /**
- * Gets the value of the <tt>kABRelatedNamesProperty</tt> constant.
- *
- * @return the value of the <tt>kABRelatedNamesProperty</tt> constant
- */
- public static native long kABRelatedNamesProperty();
-
- /**
- * Gets the value of the <tt>kABDepartmentProperty</tt> constant.
- *
- * @return the value of the <tt>kABDepartmentProperty</tt> constant
- */
- public static native long kABDepartmentProperty();
-
- /**
- * Gets the value of the <tt>kABInstantMessageProperty</tt> constant.
- *
- * @return the value of the <tt>kABInstantMessageProperty</tt> constant
- */
- public static native long kABInstantMessageProperty();
-
- /**
- * Gets the value of the <tt>kABNoteProperty</tt> constant.
- *
- * @return the value of the <tt>kABNoteProperty</tt> constant
- */
- public static native long kABNoteProperty();
-
- /**
- * Gets the value of the <tt>kABTitleProperty</tt> constant.
- *
- * @return the value of the <tt>kABTitleProperty</tt> constant
- */
- public static native long kABTitleProperty();
-
- /**
- * Gets the value of the <tt>kABSuffixProperty</tt> constant.
- *
- * @return the value of the <tt>kABSuffixProperty</tt> constant
- */
- public static native long kABSuffixProperty();
-
- public static native String kABEmailWorkLabel();
- public static native String kABEmailHomeLabel();
- public static native String kABAddressHomeLabel();
- public static native String kABAddressWorkLabel();
- public static native String kABPhoneWorkLabel();
- public static native String kABPhoneHomeLabel();
- public static native String kABPhoneMobileLabel();
- public static native String kABPhoneMainLabel();
- public static native String kABPhoneWorkFAXLabel();
- public static native String kABHomeLabel();
- public static native String kABWorkLabel();
- public static native String kABOtherLabel();
- public static native String kABAddressStreetKey();
- public static native String kABAddressCityKey();
- public static native String kABAddressStateKey();
- public static native String kABAddressZIPKey();
- public static native String kABAddressCountryKey();
-
-
- /**
- * Determines whether a specific <tt>ABPerson</tt> property with a specific
- * <tt>value</tt> matches the {@link #query} of this
- * <tt>AsyncContactQuery</tt>.
- *
- * @param property the <tt>ABPerson</tt> property to check
- * @param value the value of the <tt>property</tt> to check
- * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
- * <tt>property</tt> matches the <tt>query</tt> of this
- * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
- */
- private boolean matches(int property, String value)
- {
- return
- query.matcher(value).find()
- || ((kABPhoneProperty == property) && phoneNumberMatches(value));
- }
-
- /**
- * Determines whether an <tt>ABPerson</tt> represented by the values of its
- * {@link #ABPERSON_PROPERTIES} matches {@link #query}.
- *
- * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
- * represent the <tt>ABPerson</tt> to be determined whether it matches
- * <tt>query</tt>
- * @return <tt>true</tt> if the <tt>ABPerson</tt> represented by the
- * specified <tt>values</tt> matches <tt>query</tt>; otherwise,
- * <tt>false</tt>
- */
- private boolean matches(Object[] values)
- {
- int property = 0;
-
- for (Object value : values)
- {
- if (value instanceof String)
- {
- if (matches(property, (String) value))
- return true;
- }
- else if (value instanceof Object[])
- {
- Object[] multiValue = (Object[]) value;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
- if ((subValue instanceof String)
- && matches(property, (String) subValue))
- return true;
- }
- }
- property++;
- }
- return false;
- }
-
- /**
- * Notifies this <tt>MacOSXAddrBookContactQuery</tt> about a specific
- * <tt>ABPerson</tt>.
- *
- * @param person a pointer to the <tt>ABPerson</tt> instance to notify about
- * @return <tt>true</tt> if this <tt>MacOSXAddrBookContactQuery</tt> is to
- * continue being called; otherwise, <tt>false</tt>
- */
- private boolean onPerson(long person)
- {
- Object[] values
- = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
- final String id = ABRecord_uniqueId(person);
-
- String displayName = getDisplayName(values);
- if ((displayName.length() != 0)
- && (query.matcher(displayName).find() || matches(values)))
- {
- List<ContactDetail> contactDetails = getContactDetails(values, id);
-
- if (!contactDetails.isEmpty())
- {
- final MacOSXAddrBookSourceContact sourceContact
- = new MacOSXAddrBookSourceContact(
- getContactSource(),
- displayName,
- contactDetails);
- sourceContact.setData(SourceContact.DATA_ID, id);
-
- try
- {
- byte[] image = ABPerson_imageData(person);
-
- if (image != null)
- sourceContact.setImage(image);
- }
- catch (OutOfMemoryError oome)
- {
- // Ignore it, the image is not vital.
- }
-
- addQueryResult(sourceContact);
- }
- }
- return (getStatus() == QUERY_IN_PROGRESS);
- }
-
- /**
- * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
- *
- * @see AsyncContactQuery#run()
- */
- protected void run()
- {
- foreachPerson(
- query.toString(),
- new PtrCallback()
- {
- public boolean callback(long person)
- {
- return onPerson(person);
- }
- });
- }
-
- /**
- * Sets the capabilities of a specific <tt>ContactDetail</tt> (e.g.
- * <tt>supportedOpSets</tt>) depending on the <tt>ABPerson</tt> property
- * that it stands for.
- *
- * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities
- * of
- * @param property the index in {@link #ABPERSON_PROPERTIES} of the
- * <tt>ABPerson</tt> property represented by <tt>ContactDetail</tt>
- * @return <tt>contactDetail</tt>
- */
- private ContactDetail setCapabilities(
- ContactDetail contactDetail,
- int property)
- {
- List<Class<? extends OperationSet>> supportedOpSets
- = new LinkedList<Class<? extends OperationSet>>();
- Map<Class<? extends OperationSet>, String> preferredProtocols
- = new HashMap<Class<? extends OperationSet>, String>();
-
- // can be added as contacts
- supportedOpSets.add(OperationSetPersistentPresence.class);
-
- switch (property)
- {
- case kABAIMInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.AIM);
- break;
- case kABEmailProperty:
- break;
- case kABICQInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.ICQ);
- break;
- case kABJabberInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.JABBER);
- supportedOpSets.add(OperationSetBasicTelephony.class);
- preferredProtocols.put(
- OperationSetBasicTelephony.class,
- ProtocolNames.JABBER);
- break;
- case kABPhoneProperty:
- supportedOpSets.add(OperationSetBasicTelephony.class);
- break;
- case kABMSNInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.MSN);
- break;
- case kABYahooInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.YAHOO);
- break;
- default:
- break;
- }
- contactDetail.setSupportedOpSets(supportedOpSets);
- if (!preferredProtocols.isEmpty())
- contactDetail.setPreferredProtocols(preferredProtocols);
-
- return contactDetail;
- }
-
- /**
- * Callback method when receiving notifications for inserted items.
- */
- public void inserted(long person)
- {
- onPerson(person);
- }
-
- /**
- * Callback method when receiving notifications for updated items.
- */
- public void updated(long person)
- {
- SourceContact sourceContact =
- findSourceContactByID(ABRecord_uniqueId(person));
- if(sourceContact != null
- && sourceContact instanceof MacOSXAddrBookSourceContact)
- {
- // let's update the the details
- Object[] values
- = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
- String displayName = getDisplayName(values);
- final String id = ABRecord_uniqueId(person);
-
-
- MacOSXAddrBookSourceContact editableSourceContact
- = (MacOSXAddrBookSourceContact)sourceContact;
-
- editableSourceContact.setDisplayName(displayName);
-
- List<ContactDetail> contactDetails = getContactDetails(values, id);
- editableSourceContact.setDetails(contactDetails);
-
- fireContactChanged(sourceContact);
- }
- }
-
- /**
- * Callback method when receiving notifications for deleted items.
- */
- public void deleted(String id)
- {
- SourceContact sourceContact = findSourceContactByID(id);
-
- if(sourceContact != null)
- fireContactRemoved(sourceContact);
- }
-
- /**
- * Find the property from category and subcategories.
- *
- * @param category
- * @param subCategories
- * @return
- */
- public static int getProperty(
- Category category,
- Collection<SubCategory> subCategories)
- {
- switch(category)
- {
- case Personal:
- if(subCategories.contains(SubCategory.Name))
- return kABFirstNameProperty;
- else if(subCategories.contains(SubCategory.LastName))
- return kABLastNameProperty;
- else if(subCategories.contains(SubCategory.Nickname))
- return kABNicknameProperty;
- else if(subCategories.contains(SubCategory.HomePage))
- return kABHomePageProperty;
- break;
- case Organization:
- if(subCategories.contains(SubCategory.JobTitle))
- return kABJobTitleProperty;
- else
- return kABDepartmentProperty;
- case Email:
- return kABEmailProperty;
- case InstantMessaging:
- if(subCategories.contains(SubCategory.AIM))
- return kABAIMInstantProperty;
- else if(subCategories.contains(SubCategory.ICQ))
- return kABICQInstantProperty;
- else if(subCategories.contains(SubCategory.MSN))
- return kABMSNInstantProperty;
- else if(subCategories.contains(
- SubCategory.Jabber))
- return kABJabberInstantProperty;
- else if(subCategories.contains(
- SubCategory.Yahoo))
- return kABYahooInstantProperty;
- break;
- case Phone:
- return kABPhoneProperty;
- case Address:
- return kABAddressProperty;
- default: return -1;
- }
-
- return -1;
- }
-
- /**
- * Finds the label from category and sub categories.
- * @param subCategory
- * @return
- */
- public static String getLabel(
- int property,
- SubCategory subCategory,
- String subProperty)
- {
- switch(property)
- {
- case kABEmailProperty:
- if(subCategory == SubCategory.Home)
- return kABEmailHomeLabel();
- if(subCategory == SubCategory.Work)
- return kABEmailWorkLabel();
- break;
- case kABICQInstantProperty:
- case kABAIMInstantProperty:
- case kABYahooInstantProperty:
- case kABMSNInstantProperty:
- case kABJabberInstantProperty:
- return subProperty;
- case kABPhoneProperty:
- if(subCategory == SubCategory.Home)
- return kABPhoneHomeLabel();
- if(subCategory == SubCategory.Work)
- return kABPhoneWorkLabel();
- if(subCategory == SubCategory.Fax)
- return kABPhoneWorkFAXLabel();
- if(subCategory == SubCategory.Mobile)
- return kABPhoneMobileLabel();
- if(subCategory == SubCategory.Other)
- return "other";
- break;
- case kABAddressProperty:
- if(subCategory == SubCategory.Street)
- return kABAddressStreetKey();
- if(subCategory == SubCategory.City)
- return kABAddressCityKey();
- if(subCategory == SubCategory.State)
- return kABAddressStateKey();
- if(subCategory == SubCategory.Country)
- return kABAddressCountryKey();
- if(subCategory == SubCategory.PostalCode)
- return kABAddressZIPKey();
- break;
- default: return null;
- }
-
- return null;
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.addrbook.macosx;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.contactsource.ContactDetail.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactQuery</tt> for the Address Book of Mac OS X.
+ *
+ * @author Lyubomir Marinov
+ */
+public class MacOSXAddrBookContactQuery
+ extends AbstractAddrBookContactQuery<MacOSXAddrBookContactSourceService>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookContactQuery</tt> class
+ * and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MacOSXAddrBookContactQuery.class);
+
+ /**
+ * The properties of <tt>ABPerson</tt> which are to be queried by the
+ * <tt>MacOSXAddrBookContactQuery</tt> instances.
+ */
+ public static final long[] ABPERSON_PROPERTIES
+ = new long[]
+ {
+ kABAIMInstantProperty(),
+ kABEmailProperty(),
+ kABFirstNameProperty(),
+ kABFirstNamePhoneticProperty(),
+ kABICQInstantProperty(),
+ kABJabberInstantProperty(),
+ kABLastNameProperty(),
+ kABLastNamePhoneticProperty(),
+ kABMiddleNameProperty(),
+ kABMiddleNamePhoneticProperty(),
+ kABMSNInstantProperty(),
+ kABNicknameProperty(),
+ kABPhoneProperty(),
+ kABYahooInstantProperty(),
+ kABPersonFlags(),
+ kABOrganizationProperty(),
+ kABMaidenNameProperty(),
+ kABBirthdayProperty(),
+ kABJobTitleProperty(),
+ kABHomePageProperty(),
+ kABURLsProperty(),
+ kABCalendarURIsProperty(),
+ kABAddressProperty(),
+ kABOtherDatesProperty(),
+ kABRelatedNamesProperty(),
+ kABDepartmentProperty(),
+ kABNoteProperty(),
+ kABTitleProperty(),
+ kABSuffixProperty()
+ };
+
+ /**
+ * The index of the <tt>kABAIMInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABAIMInstantProperty = 0;
+
+ /**
+ * The index of the <tt>kABEmailProperty</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABEmailProperty = 1;
+
+ /**
+ * The index of the <tt>kABFirstNameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABFirstNameProperty = 2;
+
+ /**
+ * The index of the <tt>kABFirstNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABFirstNamePhoneticProperty = 3;
+
+ /**
+ * The index of the <tt>kABICQInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABICQInstantProperty = 4;
+
+ /**
+ * The index of the <tt>kABJabberInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABJabberInstantProperty = 5;
+
+ /**
+ * The index of the <tt>kABLastNameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABLastNameProperty = 6;
+
+ /**
+ * The index of the <tt>kABLastNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABLastNamePhoneticProperty = 7;
+
+ /**
+ * The index of the <tt>kABMiddleNameProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMiddleNameProperty = 8;
+
+ /**
+ * The index of the <tt>kABMiddleNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMiddleNamePhoneticProperty = 9;
+
+ /**
+ * The index of the <tt>kABMSNInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMSNInstantProperty = 10;
+
+ /**
+ * The index of the <tt>kABNicknameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABNicknameProperty = 11;
+
+ /**
+ * The index of the <tt>kABOrganizationProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABOrganizationProperty = 15;
+
+ /**
+ * The index of the <tt>kABPersonFlags</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABPersonFlags = 14;
+
+ /**
+ * The index of the <tt>kABPhoneProperty</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABPhoneProperty = 12;
+
+ /**
+ * The flag which indicates that an <tt>ABRecord</tt> is to be displayed as
+ * a company.
+ */
+ public static final long kABShowAsCompany = 1;
+
+ /**
+ * The mask which extracts the <tt>kABShowAsXXX</tt> flag from the
+ * <tt>personFlags</tt> of an <tt>ABPerson</tt>.
+ */
+ public static final long kABShowAsMask = 7;
+
+ /**
+ * The index of the <tt>kABYahooInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABYahooInstantProperty = 13;
+
+ /**
+ * The index of the <tt>kABMaidenNameProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMaidenNameProperty = 16;
+
+ /**
+ * The index of the <tt>kABBirthdayProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABBirthdayProperty = 17;
+
+ /**
+ * The index of the <tt>kABJobTitleProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABJobTitleProperty = 18;
+
+ /**
+ * The index of the <tt>kABHomePageProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABHomePageProperty = 19;
+
+ /**
+ * The index of the <tt>kABURLsProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABURLsProperty = 20;
+
+ /**
+ * The index of the <tt>kABCalendarURIsProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABCalendarURIsProperty = 21;
+
+ /**
+ * The index of the <tt>kABAddressProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABAddressProperty = 22;
+
+ /**
+ * The index of the <tt>kABOtherDatesProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABOtherDatesProperty = 23;
+
+ /**
+ * The index of the <tt>kABRelatedNamesProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABRelatedNamesProperty = 24;
+
+ /**
+ * The index of the <tt>kABDepartmentProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABDepartmentProperty = 25;
+
+ /**
+ * The index of the <tt>kABNoteProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABNoteProperty = 26;
+
+ /**
+ * The index of the <tt>kABTitleProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABTitleProperty = 27;
+
+ /**
+ * The index of the <tt>kABSuffixProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABSuffixProperty = 28;
+
+ /**
+ * The regex which matches the superfluous parts of an <tt>ABMultiValue</tt>
+ * label.
+ */
+ private static final Pattern LABEL_PATTERN
+ = Pattern.compile(
+ "kAB|Email|Phone|Label|(\\p{Punct}*)",
+ Pattern.CASE_INSENSITIVE);
+
+ static
+ {
+ System.loadLibrary("jmacosxaddrbook");
+ }
+
+ /**
+ * Initializes a new <tt>MacOSXAddrBookContactQuery</tt> which is to perform
+ * a specific <tt>query</tt> in the Address Book of Mac OS X on behalf of a
+ * specific <tt>MacOSXAddrBookContactSourceService</tt>.
+ *
+ * @param contactSource the <tt>MacOSXAddrBookContactSourceService</tt>
+ * which is to perform the new <tt>ContactQuery</tt> instance
+ * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> i.e.
+ * the Address Book of Mac OS X is being queried
+ */
+ public MacOSXAddrBookContactQuery(
+ MacOSXAddrBookContactSourceService contactSource,
+ Pattern query)
+ {
+ super(contactSource, query);
+ }
+
+ /**
+ * Gets the <tt>imageData</tt> of a specific <tt>ABPerson</tt> instance.
+ *
+ * @param person the pointer to the <tt>ABPerson</tt> instance to get the
+ * <tt>imageData</tt> of
+ * @return the <tt>imageData</tt> of the specified <tt>ABPerson</tt>
+ * instance
+ */
+ public static native byte[] ABPerson_imageData(long person);
+
+ /**
+ * Gets the values of a specific set of <tt>ABRecord</tt> properties for a
+ * specific <tt>ABRecord</tt> instance.
+ *
+ * @param record the pointer to the <tt>ABRecord</tt> to get the property
+ * values of
+ * @param properties the set of <tt>ABRecord</tt> properties to get the
+ * values of
+ * @return the values of the specified set of <tt>ABRecord</tt> properties
+ * for the specified <tt>ABRecord</tt> instance
+ */
+ public static native Object[] ABRecord_valuesForProperties(
+ long record,
+ long[] properties);
+
+ /**
+ * Returns the unique id of a record.
+ * @param record the record which id is retrieved.
+ * @return the record id.
+ */
+ public static native String ABRecord_uniqueId(long record);
+
+ /**
+ * Sets property for the supplied person id.
+ * @param id the person id
+ * @param property the property to use.
+ * @param subPropety any sub property if available.
+ * @param value the value to set.
+ * @return whether the result was successfully added.
+ */
+ public static native boolean setProperty(
+ String id, long property, String subPropety, Object value);
+
+ /**
+ * Remove a property.
+ * @param id the person id.
+ * @param property the property.
+ * @return whether the result was successfully removed.
+ */
+ public static native boolean removeProperty(String id, long property);
+
+ /**
+ * Removes a contact from the address book.
+ *
+ * @param id the person id.
+ *
+ * @return whether the contact was successfully removed.
+ */
+ public static native boolean deleteContact(String id);
+
+ /**
+ * Creates a new address book contact.
+ *
+ * @return The identifier of the created contact. null if failed.
+ */
+ public static native String createContact();
+
+ /**
+ * Gets the pointer of the given contact.
+ *
+ * @param id the person id.
+ *
+ * @return The pointer of the given contact. Null if failed.
+ */
+ public static native long getContactPointer(String id);
+
+ /**
+ * Initializes a new <tt>ContactDetail</tt> instance which is to reperesent
+ * a specific contact address that is the value of a specific
+ * <tt>ABPerson</tt> property and, optionally, has a specific label.
+ *
+ * @param property the index in {@link #ABPERSON_PROPERTIES} of the
+ * <tt>ABPerson</tt> property to be represented by <tt>ContactDetail</tt>
+ * @param contactAddress the contact address to be represented by the new
+ * <tt>ContactDetail</tt> instance
+ * @param label an optional label to be added to the set of labels, if any,
+ * determined by <tt>property</tt>
+ * @param id The id of the detail.
+ *
+ * @return a new <tt>ContactDetail</tt> instance which represents the
+ * specified <tt>contactAddress</tt>
+ */
+ private ContactDetail createContactDetail(
+ int property,
+ String contactAddress,
+ Object label,
+ String additionalProperty,
+ String id)
+ {
+ Category c;
+ SubCategory sc = null;
+
+ switch (property)
+ {
+ case kABEmailProperty:
+ c = Category.Email;
+ break;
+ case kABPhoneProperty:
+ c = Category.Phone;
+ break;
+ case kABAIMInstantProperty:
+ sc = SubCategory.AIM;
+ c = Category.InstantMessaging;
+ break;
+ case kABICQInstantProperty:
+ sc = SubCategory.ICQ;
+ c = Category.InstantMessaging;
+ break;
+ case kABJabberInstantProperty:
+ sc = SubCategory.Jabber;
+ c = Category.InstantMessaging;
+ break;
+ case kABMSNInstantProperty:
+ sc = SubCategory.MSN;
+ c = Category.InstantMessaging;
+ break;
+ case kABYahooInstantProperty:
+ sc = SubCategory.Yahoo;
+ c = Category.InstantMessaging;
+ break;
+ case kABMaidenNameProperty:
+ case kABFirstNameProperty:
+ sc = SubCategory.Name;
+ c = Category.Personal;
+ break;
+ case kABFirstNamePhoneticProperty:
+ sc = SubCategory.Name;
+ c = Category.Personal;
+ break;
+ case kABLastNameProperty:
+ sc = SubCategory.LastName;
+ c = Category.Personal;
+ break;
+ case kABLastNamePhoneticProperty:
+ sc = SubCategory.LastName;
+ c = Category.Personal;
+ break;
+ case kABMiddleNameProperty:
+ case kABMiddleNamePhoneticProperty:
+ case kABNicknameProperty:
+ sc = SubCategory.Nickname;
+ c = Category.Personal;
+ break;
+ case kABBirthdayProperty:
+ case kABURLsProperty:
+ case kABHomePageProperty:
+ sc = SubCategory.HomePage;
+ c = Category.Personal;
+ break;
+ case kABOtherDatesProperty:
+ case kABRelatedNamesProperty:
+ case kABNoteProperty:
+ case kABTitleProperty:
+ case kABSuffixProperty:
+ c = Category.Personal;
+ break;
+ case kABOrganizationProperty:
+ case kABJobTitleProperty:
+ sc = SubCategory.JobTitle;
+ c = Category.Organization;
+ break;
+ case kABDepartmentProperty:
+ c = Category.Organization;
+ sc = SubCategory.Name;
+ break;
+ case kABAddressProperty:
+ c = Category.Address;
+ break;
+ default:
+ c = null;
+ break;
+ }
+
+ if (sc == null)
+ {
+ if (label == null)
+ sc = null;
+ else
+ {
+ sc = getSubCategoryFromLabel(label);
+ }
+ }
+
+ SubCategory[] subCategories;
+ SubCategory additionalSubCategory = null;
+
+ if(additionalProperty != null)
+ additionalSubCategory = getSubCategoryFromLabel(additionalProperty);
+
+ if(additionalSubCategory != null)
+ subCategories = new SubCategory[]
+ { sc, additionalSubCategory };
+ else
+ subCategories = new SubCategory[]{ sc };
+
+ return new MacOSXAddrBookContactDetail(
+ property,
+ contactAddress,
+ c,
+ subCategories,
+ additionalProperty,
+ id);
+ }
+
+ /**
+ * Returns the SubCategory corresponding to the given label.
+ *
+ * @param label the label to match to a <tt>SubDirectory</tt>
+ * @return the <tt>SubDirectory</tt> corresponding to the
+ * given label
+ */
+ private SubCategory getSubCategoryFromLabel(Object label)
+ {
+ String labelString
+ = LABEL_PATTERN.matcher((String) label).replaceAll("").trim();
+
+ if (labelString.length() < 1)
+ return null;
+
+ SubCategory subCategory = null;
+
+ if (labelString.equalsIgnoreCase("home"))
+ subCategory = SubCategory.Home;
+ else if (labelString.equalsIgnoreCase("work"))
+ subCategory = SubCategory.Work;
+ else if (labelString.equalsIgnoreCase("other"))
+ subCategory = SubCategory.Other;
+ else if (labelString.equalsIgnoreCase("mobile"))
+ subCategory = SubCategory.Mobile;
+ else if (labelString.equalsIgnoreCase("homepage"))
+ subCategory = SubCategory.HomePage;
+ else if (labelString.equalsIgnoreCase("street"))
+ subCategory = SubCategory.Street;
+ else if (labelString.equalsIgnoreCase("state"))
+ subCategory = SubCategory.State;
+ else if (labelString.equalsIgnoreCase("ZIP"))
+ subCategory = SubCategory.PostalCode;
+ else if (labelString.equalsIgnoreCase("country"))
+ subCategory = SubCategory.Country;
+ else if (labelString.equalsIgnoreCase("city"))
+ subCategory = SubCategory.City;
+ else if (labelString.equalsIgnoreCase("InstantMessageUsername"))
+ subCategory = SubCategory.Nickname;
+ else if (labelString.equalsIgnoreCase("workfax"))
+ subCategory = SubCategory.Fax;
+ else if (labelString.equalsIgnoreCase("fax"))
+ subCategory = SubCategory.Fax;
+
+ return subCategory;
+ }
+
+ /**
+ * Calls back to a specific <tt>PtrCallback</tt> for each <tt>ABPerson</tt>
+ * found in the Address Book of Mac OS X which matches a specific
+ * <tt>String</tt> query.
+ *
+ * @param query the <tt>String</tt> for which the Address Book of Mac OS X
+ * is to be queried. <b>Warning</b>: Ignored at the time of this writing.
+ * @param callback the <tt>PtrCallback</tt> to be notified about the
+ * matching <tt>ABPerson</tt>s
+ */
+ private static native void foreachPerson(
+ String query,
+ PtrCallback callback);
+
+ /**
+ * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent an <tt>ABPerson</tt> specified by the values of its
+ * {@link #ABPERSON_PROPERTIES}.
+ *
+ * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
+ * represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of
+ * @param id The id of the detail.
+ *
+ * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
+ */
+ private List<ContactDetail> getContactDetails(Object[] values, String id)
+ {
+ List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
+
+ for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
+ {
+ int property = i;
+ Object value = values[property];
+
+ if (value instanceof String)
+ {
+ String stringValue = (String) value;
+
+ if (stringValue.length() != 0)
+ {
+ if (kABPhoneProperty == property)
+ stringValue
+ = PhoneNumberI18nService.normalize(stringValue);
+
+ contactDetails.add(
+ setCapabilities(
+ createContactDetail(
+ property,
+ stringValue,
+ null,
+ null,
+ id),
+ property));
+ }
+ }
+ else if (value instanceof Object[])
+ {
+ parseMultiDetails(contactDetails,
+ (Object[]) value,
+ property,
+ null,
+ id);
+ }
+ }
+ return contactDetails;
+ }
+
+ /**
+ * Parses the multi value data resulting it in contact details.
+ * @param contactDetails the result list
+ * @param multiValue the values to parse.
+ * @param property the current property being parsed.
+ * @param id The id of the detail.
+ */
+ private void parseMultiDetails(
+ List<ContactDetail> contactDetails,
+ Object[] multiValue,
+ int property,
+ String label,
+ String id)
+ {
+ if(multiValue == null)
+ return;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+
+ if (subValue instanceof String)
+ {
+ String stringSubValue = (String) subValue;
+
+ if (stringSubValue.length() != 0)
+ {
+ if (kABPhoneProperty == property)
+ {
+ stringSubValue = PhoneNumberI18nService
+ .normalize(stringSubValue);
+ }
+
+ Object l = multiValue[multiValueIndex + 1];
+
+ contactDetails.add(
+ setCapabilities(
+ createContactDetail(
+ property,
+ stringSubValue,
+ l,
+ label,
+ id),
+ property));
+ }
+ }
+ else if (subValue instanceof Object[])
+ {
+ String l = null;
+
+ Object lObject = multiValue[multiValueIndex + 1];
+ if(lObject instanceof String)
+ l = (String)lObject;
+
+ parseMultiDetails(contactDetails,
+ (Object[]) subValue,
+ property,
+ l,
+ id);
+ }
+ }
+ }
+
+ /**
+ * Gets the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent an <tt>ABPerson</tt> specified by the values of its
+ * {@link #ABPERSON_PROPERTIES}.
+ *
+ * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
+ * represent the <tt>ABPerson</tt> to get the <tt>displayName</tt> of
+ * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
+ */
+ static String getDisplayName(Object[] values)
+ {
+ long personFlags
+ = (values[kABPersonFlags] instanceof Long)
+ ? ((Long) values[kABPersonFlags]).longValue()
+ : 0;
+ String displayName;
+
+ if ((personFlags & kABShowAsMask) == kABShowAsCompany)
+ {
+ displayName
+ = (values[kABOrganizationProperty] instanceof String)
+ ? (String) values[kABOrganizationProperty]
+ : "";
+ if (displayName.length() != 0)
+ return displayName;
+ }
+
+ displayName
+ = (values[kABNicknameProperty] instanceof String)
+ ? (String) values[kABNicknameProperty]
+ : "";
+ if (displayName.length() != 0)
+ return displayName;
+
+ String firstName
+ = (values[kABFirstNameProperty] instanceof String)
+ ? (String) values[kABFirstNameProperty]
+ : "";
+
+ if ((firstName.length() == 0)
+ && (values[kABFirstNamePhoneticProperty] instanceof String))
+ {
+ firstName = (String) values[kABFirstNamePhoneticProperty];
+ }
+
+ String lastName
+ = (values[kABLastNameProperty] instanceof String)
+ ? (String) values[kABLastNameProperty]
+ : "";
+
+ if ((lastName.length() == 0)
+ && (values[kABLastNamePhoneticProperty] instanceof String))
+ lastName = (String) values[kABLastNamePhoneticProperty];
+ if ((lastName.length() == 0)
+ && (values[kABMiddleNameProperty] instanceof String))
+ lastName = (String) values[kABMiddleNameProperty];
+ if ((lastName.length() == 0)
+ && (values[kABMiddleNamePhoneticProperty] instanceof String))
+ lastName = (String) values[kABMiddleNamePhoneticProperty];
+
+ if (firstName.length() == 0)
+ displayName = lastName;
+ else
+ {
+ displayName
+ = (lastName.length() == 0)
+ ? firstName
+ : (firstName + " " + lastName);
+ }
+ if (displayName.length() != 0)
+ return displayName;
+
+ for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
+ {
+ Object value = values[i];
+
+ if (value instanceof String)
+ {
+ String stringValue = (String) value;
+
+ if (stringValue.length() != 0)
+ {
+ displayName = stringValue;
+ break;
+ }
+ }
+ else if (value instanceof Object[])
+ {
+ Object[] multiValue = (Object[]) value;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+
+ if (subValue instanceof String)
+ {
+ String stringSubValue = (String) subValue;
+
+ if (stringSubValue.length() != 0)
+ {
+ displayName = stringSubValue;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return displayName;
+ }
+
+ /**
+ * Gets the value of the <tt>kABAIMInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABAIMInstantProperty</tt> constant
+ */
+ public static native long kABAIMInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABEmailProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABEmailProperty</tt> constant
+ */
+ public static native long kABEmailProperty();
+
+ /**
+ * Gets the value of the <tt>kABFirstNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABFirstNameProperty</tt> constant
+ */
+ public static native long kABFirstNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABFirstNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABFirstNamePhoneticProperty</tt> constant
+ */
+ public static native long kABFirstNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABICQInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABICQInstantProperty</tt> constant
+ */
+ public static native long kABICQInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABJabberInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABJabberInstantProperty</tt> constant
+ */
+ public static native long kABJabberInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABLastNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABLastNameProperty</tt> constant
+ */
+ public static native long kABLastNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABLastNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABLastNamePhoneticProperty</tt> constant
+ */
+ public static native long kABLastNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABMiddleNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMiddleNameProperty</tt> constant
+ */
+ public static native long kABMiddleNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant
+ */
+ public static native long kABMiddleNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABMSNInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMSNInstantProperty</tt> constant
+ */
+ public static native long kABMSNInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABNicknameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABNicknameProperty</tt> constant
+ */
+ public static native long kABNicknameProperty();
+
+ /**
+ * Gets the value of the <tt>kABOrganizationProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABOrganizationProperty</tt> constant
+ */
+ public static native long kABOrganizationProperty();
+
+ /**
+ * Gets the value of the <tt>kABPersonFlags</tt> constant.
+ *
+ * @return the value of the <tt>kABPersonFlags</tt> constant
+ */
+ public static native long kABPersonFlags();
+
+ /**
+ * Gets the value of the <tt>kABPhoneProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABPhoneProperty</tt> constant
+ */
+ public static native long kABPhoneProperty();
+
+ /**
+ * Gets the value of the <tt>kABYahooInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABYahooInstantProperty</tt> constant
+ */
+ public static native long kABYahooInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABMaidenNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMaidenNameProperty</tt> constant
+ */
+ public static native long kABMaidenNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABBirthdayProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABBirthdayProperty</tt> constant
+ */
+ public static native long kABBirthdayProperty();
+
+ /**
+ * Gets the value of the <tt>kABJobTitleProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABJobTitleProperty</tt> constant
+ */
+ public static native long kABJobTitleProperty();
+
+ /**
+ * Gets the value of the <tt>kABHomePageProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABHomePageProperty</tt> constant
+ */
+ public static native long kABHomePageProperty();
+
+ /**
+ * Gets the value of the <tt>kABURLsProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABURLsProperty</tt> constant
+ */
+ public static native long kABURLsProperty();
+
+ /**
+ * Gets the value of the <tt>kABCalendarURIsProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABCalendarURIsProperty</tt> constant
+ */
+ public static native long kABCalendarURIsProperty();
+
+ /**
+ * Gets the value of the <tt>kABAddressProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABAddressProperty</tt> constant
+ */
+ public static native long kABAddressProperty();
+
+ /**
+ * Gets the value of the <tt>kABOtherDatesProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABOtherDatesProperty</tt> constant
+ */
+ public static native long kABOtherDatesProperty();
+
+ /**
+ * Gets the value of the <tt>kABRelatedNamesProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABRelatedNamesProperty</tt> constant
+ */
+ public static native long kABRelatedNamesProperty();
+
+ /**
+ * Gets the value of the <tt>kABDepartmentProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABDepartmentProperty</tt> constant
+ */
+ public static native long kABDepartmentProperty();
+
+ /**
+ * Gets the value of the <tt>kABInstantMessageProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABInstantMessageProperty</tt> constant
+ */
+ public static native long kABInstantMessageProperty();
+
+ /**
+ * Gets the value of the <tt>kABNoteProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABNoteProperty</tt> constant
+ */
+ public static native long kABNoteProperty();
+
+ /**
+ * Gets the value of the <tt>kABTitleProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABTitleProperty</tt> constant
+ */
+ public static native long kABTitleProperty();
+
+ /**
+ * Gets the value of the <tt>kABSuffixProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABSuffixProperty</tt> constant
+ */
+ public static native long kABSuffixProperty();
+
+ public static native String kABEmailWorkLabel();
+ public static native String kABEmailHomeLabel();
+ public static native String kABAddressHomeLabel();
+ public static native String kABAddressWorkLabel();
+ public static native String kABPhoneWorkLabel();
+ public static native String kABPhoneHomeLabel();
+ public static native String kABPhoneMobileLabel();
+ public static native String kABPhoneMainLabel();
+ public static native String kABPhoneWorkFAXLabel();
+ public static native String kABHomeLabel();
+ public static native String kABWorkLabel();
+ public static native String kABOtherLabel();
+ public static native String kABAddressStreetKey();
+ public static native String kABAddressCityKey();
+ public static native String kABAddressStateKey();
+ public static native String kABAddressZIPKey();
+ public static native String kABAddressCountryKey();
+
+
+ /**
+ * Determines whether a specific <tt>ABPerson</tt> property with a specific
+ * <tt>value</tt> matches the {@link #query} of this
+ * <tt>AsyncContactQuery</tt>.
+ *
+ * @param property the <tt>ABPerson</tt> property to check
+ * @param value the value of the <tt>property</tt> to check
+ * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
+ * <tt>property</tt> matches the <tt>query</tt> of this
+ * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
+ */
+ private boolean matches(int property, String value)
+ {
+ return
+ query.matcher(value).find()
+ || ((kABPhoneProperty == property) && phoneNumberMatches(value));
+ }
+
+ /**
+ * Determines whether an <tt>ABPerson</tt> represented by the values of its
+ * {@link #ABPERSON_PROPERTIES} matches {@link #query}.
+ *
+ * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
+ * represent the <tt>ABPerson</tt> to be determined whether it matches
+ * <tt>query</tt>
+ * @return <tt>true</tt> if the <tt>ABPerson</tt> represented by the
+ * specified <tt>values</tt> matches <tt>query</tt>; otherwise,
+ * <tt>false</tt>
+ */
+ private boolean matches(Object[] values)
+ {
+ int property = 0;
+
+ for (Object value : values)
+ {
+ if (value instanceof String)
+ {
+ if (matches(property, (String) value))
+ return true;
+ }
+ else if (value instanceof Object[])
+ {
+ Object[] multiValue = (Object[]) value;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+ if ((subValue instanceof String)
+ && matches(property, (String) subValue))
+ return true;
+ }
+ }
+ property++;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies this <tt>MacOSXAddrBookContactQuery</tt> about a specific
+ * <tt>ABPerson</tt>.
+ *
+ * @param person a pointer to the <tt>ABPerson</tt> instance to notify about
+ * @return <tt>true</tt> if this <tt>MacOSXAddrBookContactQuery</tt> is to
+ * continue being called; otherwise, <tt>false</tt>
+ */
+ private boolean onPerson(long person)
+ {
+ Object[] values
+ = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
+ final String id = ABRecord_uniqueId(person);
+
+ String displayName = getDisplayName(values);
+ if ((displayName.length() != 0)
+ && (query.matcher(displayName).find() || matches(values)))
+ {
+ List<ContactDetail> contactDetails = getContactDetails(values, id);
+
+ if (!contactDetails.isEmpty())
+ {
+ final MacOSXAddrBookSourceContact sourceContact
+ = new MacOSXAddrBookSourceContact(
+ getContactSource(),
+ displayName,
+ contactDetails);
+ sourceContact.setData(SourceContact.DATA_ID, id);
+
+ try
+ {
+ byte[] image = ABPerson_imageData(person);
+
+ if (image != null)
+ sourceContact.setImage(image);
+ }
+ catch (OutOfMemoryError oome)
+ {
+ // Ignore it, the image is not vital.
+ }
+
+ addQueryResult(sourceContact);
+ }
+ }
+ return (getStatus() == QUERY_IN_PROGRESS);
+ }
+
+ /**
+ * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
+ *
+ * @see AsyncContactQuery#run()
+ */
+ protected void run()
+ {
+ foreachPerson(
+ query.toString(),
+ new PtrCallback()
+ {
+ public boolean callback(long person)
+ {
+ return onPerson(person);
+ }
+ });
+ }
+
+ /**
+ * Sets the capabilities of a specific <tt>ContactDetail</tt> (e.g.
+ * <tt>supportedOpSets</tt>) depending on the <tt>ABPerson</tt> property
+ * that it stands for.
+ *
+ * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities
+ * of
+ * @param property the index in {@link #ABPERSON_PROPERTIES} of the
+ * <tt>ABPerson</tt> property represented by <tt>ContactDetail</tt>
+ * @return <tt>contactDetail</tt>
+ */
+ private ContactDetail setCapabilities(
+ ContactDetail contactDetail,
+ int property)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new LinkedList<Class<? extends OperationSet>>();
+ Map<Class<? extends OperationSet>, String> preferredProtocols
+ = new HashMap<Class<? extends OperationSet>, String>();
+
+ // can be added as contacts
+ supportedOpSets.add(OperationSetPersistentPresence.class);
+
+ switch (property)
+ {
+ case kABAIMInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.AIM);
+ break;
+ case kABEmailProperty:
+ break;
+ case kABICQInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.ICQ);
+ break;
+ case kABJabberInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.JABBER);
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ preferredProtocols.put(
+ OperationSetBasicTelephony.class,
+ ProtocolNames.JABBER);
+ break;
+ case kABPhoneProperty:
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ break;
+ case kABMSNInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.MSN);
+ break;
+ case kABYahooInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.YAHOO);
+ break;
+ default:
+ break;
+ }
+ contactDetail.setSupportedOpSets(supportedOpSets);
+ if (!preferredProtocols.isEmpty())
+ contactDetail.setPreferredProtocols(preferredProtocols);
+
+ return contactDetail;
+ }
+
+ /**
+ * Callback method when receiving notifications for inserted items.
+ */
+ public void inserted(long person)
+ {
+ onPerson(person);
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ */
+ public void updated(long person)
+ {
+ SourceContact sourceContact =
+ findSourceContactByID(ABRecord_uniqueId(person));
+ if(sourceContact != null
+ && sourceContact instanceof MacOSXAddrBookSourceContact)
+ {
+ // let's update the the details
+ Object[] values
+ = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
+ String displayName = getDisplayName(values);
+ final String id = ABRecord_uniqueId(person);
+
+ MacOSXAddrBookSourceContact editableSourceContact
+ = (MacOSXAddrBookSourceContact)sourceContact;
+
+ editableSourceContact.setDisplayName(displayName);
+
+ List<ContactDetail> contactDetails = getContactDetails(values, id);
+ editableSourceContact.setDetails(contactDetails);
+
+ fireContactChanged(sourceContact);
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ */
+ public void deleted(String id)
+ {
+ SourceContact sourceContact = findSourceContactByID(id);
+
+ if(sourceContact != null)
+ fireContactRemoved(sourceContact);
+ }
+
+ /**
+ * Find the property from category and subcategories.
+ *
+ * @param category
+ * @param subCategories
+ * @return
+ */
+ public static int getProperty(
+ Category category,
+ Collection<SubCategory> subCategories)
+ {
+ switch(category)
+ {
+ case Personal:
+ if(subCategories.contains(SubCategory.Name))
+ return kABFirstNameProperty;
+ else if(subCategories.contains(SubCategory.LastName))
+ return kABLastNameProperty;
+ else if(subCategories.contains(SubCategory.Nickname))
+ return kABNicknameProperty;
+ else if(subCategories.contains(SubCategory.HomePage))
+ return kABHomePageProperty;
+ break;
+ case Organization:
+ if(subCategories.contains(SubCategory.JobTitle))
+ return kABJobTitleProperty;
+ else
+ return kABDepartmentProperty;
+ case Email:
+ return kABEmailProperty;
+ case InstantMessaging:
+ if(subCategories.contains(SubCategory.AIM))
+ return kABAIMInstantProperty;
+ else if(subCategories.contains(SubCategory.ICQ))
+ return kABICQInstantProperty;
+ else if(subCategories.contains(SubCategory.MSN))
+ return kABMSNInstantProperty;
+ else if(subCategories.contains(
+ SubCategory.Jabber))
+ return kABJabberInstantProperty;
+ else if(subCategories.contains(
+ SubCategory.Yahoo))
+ return kABYahooInstantProperty;
+ break;
+ case Phone:
+ return kABPhoneProperty;
+ case Address:
+ return kABAddressProperty;
+ default: return -1;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Finds the label from category and sub categories.
+ * @param subCategory
+ * @return
+ */
+ public static String getLabel(
+ int property,
+ SubCategory subCategory,
+ String subProperty)
+ {
+ switch(property)
+ {
+ case kABEmailProperty:
+ if(subCategory == SubCategory.Home)
+ return kABEmailHomeLabel();
+ if(subCategory == SubCategory.Work)
+ return kABEmailWorkLabel();
+ break;
+ case kABICQInstantProperty:
+ case kABAIMInstantProperty:
+ case kABYahooInstantProperty:
+ case kABMSNInstantProperty:
+ case kABJabberInstantProperty:
+ return subProperty;
+ case kABPhoneProperty:
+ if(subCategory == SubCategory.Home)
+ return kABPhoneHomeLabel();
+ if(subCategory == SubCategory.Work)
+ return kABPhoneWorkLabel();
+ if(subCategory == SubCategory.Fax)
+ return kABPhoneWorkFAXLabel();
+ if(subCategory == SubCategory.Mobile)
+ return kABPhoneMobileLabel();
+ if(subCategory == SubCategory.Other)
+ return "other";
+ break;
+ case kABAddressProperty:
+ if(subCategory == SubCategory.Street)
+ return kABAddressStreetKey();
+ if(subCategory == SubCategory.City)
+ return kABAddressCityKey();
+ if(subCategory == SubCategory.State)
+ return kABAddressStateKey();
+ if(subCategory == SubCategory.Country)
+ return kABAddressCountryKey();
+ if(subCategory == SubCategory.PostalCode)
+ return kABAddressZIPKey();
+ break;
+ default: return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null)
+ {
+ final MacOSXAddrBookSourceContact sourceContact
+ = new MacOSXAddrBookSourceContact(
+ getContactSource(),
+ null,
+ new LinkedList<ContactDetail>());
+ sourceContact.setData(SourceContact.DATA_ID, id);
+ addQueryResult(sourceContact);
+ }
+ }
+
+ /**
+ * Fires a contact changed event for the given contact.
+ *
+ * @param sourceContact The contact which has changed.
+ */
+ public void contactChanged(SourceContact sourceContact)
+ {
+ fireContactChanged(sourceContact);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
index 7129ad2..0150913 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
@@ -1,202 +1,255 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.plugin.addrbook.macosx;
-
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-
-/**
- * Implements <tt>ContactSourceService</tt> for the Address Book of Mac OS X.
- *
- * @author Lyubomir Marinov
- */
-public class MacOSXAddrBookContactSourceService
- extends AsyncContactSourceService
-{
- /**
- * the Mac OS X address book prefix.
- */
- public static final String MACOSX_ADDR_BOOK_PREFIX
- = "net.java.sip.communicator.plugin.addrbook.MACOSX_ADDR_BOOK_PREFIX";
-
- static
- {
- System.loadLibrary("jmacosxaddrbook");
- }
-
- /**
- * The pointer to the native counterpart of this
- * <tt>MacOSXAddrBookContactSourceService</tt>.
- */
- private long ptr;
-
- /**
- * The latest query created.
- */
- private MacOSXAddrBookContactQuery latestQuery;
-
- /**
- * Initializes a new <tt>MacOSXAddrBookContactSourceService</tt> instance.
- */
- public MacOSXAddrBookContactSourceService()
- {
- ptr = start();
- if (0 == ptr)
- throw new IllegalStateException("ptr");
- setDelegate(ptr, new NotificationsDelegate());
- }
-
- /**
- * Gets a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation.
- *
- * @return a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation
- * @see ContactSourceService#getDisplayName()
- */
- public String getDisplayName()
- {
- return "Address Book";
- }
-
- /**
- * Gets a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MacOSXAddrBookContactSourceService</tt> implementation.
- *
- * @return a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MacOSXAddrBookContactSourceService</tt> implementation
- * @see ContactSourceService#getType()
- */
- public int getType()
- {
- return SEARCH_TYPE;
- }
-
- /**
- * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
- * which match a specific <tt>query</tt> <tt>Pattern</tt>.
- *
- * @param query the <tt>Pattern</tt> which this
- * <tt>ContactSourceService</tt> is being queried for
- * @return a <tt>ContactQuery</tt> which represents the query of this
- * <tt>ContactSourceService</tt> implementation for the specified
- * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
- * any) will be returned
- * @see ExtendedContactSourceService#queryContactSource(Pattern)
- */
- public ContactQuery queryContactSource(Pattern query)
- {
- if(latestQuery != null)
- latestQuery.clear();
-
- latestQuery = new MacOSXAddrBookContactQuery(this, query);
-
- latestQuery.start();
- return latestQuery;
- }
-
- /**
- * Starts a new native <tt>MacOSXAddrBookContactSourceService</tt> instance.
- *
- * @return a pointer to the newly-started native
- * <tt>MacOSXAddrBookContactSourceService</tt> instance
- */
- private static native long start();
-
- /**
- * Stops this <tt>ContactSourceService</tt> implementation and prepares it
- * for garbage collection.
- *
- * @see AsyncContactSourceService#stop()
- */
- public synchronized void stop()
- {
- if (0 != ptr)
- {
- if(latestQuery != null)
- {
- latestQuery.clear();
- latestQuery = null;
- }
-
- stop(ptr);
- ptr = 0;
- }
- }
-
- /**
- * Returns the global phone number prefix to be used when calling contacts
- * from this contact source.
- *
- * @return the global phone number prefix
- */
- public String getPhoneNumberPrefix()
- {
- return AddrBookActivator.getConfigService()
- .getString(MACOSX_ADDR_BOOK_PREFIX);
- }
-
- /**
- * Returns the index of the contact source in the result list.
- *
- * @return the index of the contact source in the result list
- */
- public int getIndex()
- {
- return -1;
- }
-
- /**
- * Stops a native <tt>MacOSXAddrBookContactSourceService</tt>.
- *
- * @param ptr the pointer to the native
- * <tt>MacOSXAddrBookContactSourceService</tt> to stop
- */
- private static native void stop(long ptr);
-
- /**
- * Sets notifier delegate.
- * @param ptr
- * @param delegate
- */
- public static native void setDelegate(long ptr, NotificationsDelegate delegate);
-
- /**
- * Delegate class to be notified for addressbook changes.
- */
- public class NotificationsDelegate
- {
- /**
- * Callback method when receiving notifications for inserted items.
- */
- public void inserted(long person)
- {
- if(latestQuery != null)
- latestQuery.inserted(person);
- }
-
- /**
- * Callback method when receiving notifications for updated items.
- */
- public void updated(long person)
- {
- if(latestQuery != null)
- latestQuery.updated(person);
- }
-
- /**
- * Callback method when receiving notifications for deleted items.
- */
- public void deleted(String id)
- {
- if(latestQuery != null)
- latestQuery.deleted(id);
- }
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.addrbook.macosx;
+
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+
+/**
+ * Implements <tt>ContactSourceService</tt> for the Address Book of Mac OS X.
+ *
+ * @author Lyubomir Marinov
+ */
+public class MacOSXAddrBookContactSourceService
+ extends AsyncContactSourceService
+ implements EditableContactSourceService
+{
+ /**
+ * the Mac OS X address book prefix.
+ */
+ public static final String MACOSX_ADDR_BOOK_PREFIX
+ = "net.java.sip.communicator.plugin.addrbook.MACOSX_ADDR_BOOK_PREFIX";
+
+ static
+ {
+ System.loadLibrary("jmacosxaddrbook");
+ }
+
+ /**
+ * The pointer to the native counterpart of this
+ * <tt>MacOSXAddrBookContactSourceService</tt>.
+ */
+ private long ptr;
+
+ /**
+ * The latest query created.
+ */
+ private MacOSXAddrBookContactQuery latestQuery;
+
+ /**
+ * Initializes a new <tt>MacOSXAddrBookContactSourceService</tt> instance.
+ */
+ public MacOSXAddrBookContactSourceService()
+ {
+ ptr = start();
+ if (0 == ptr)
+ throw new IllegalStateException("ptr");
+ setDelegate(ptr, new NotificationsDelegate());
+ }
+
+ /**
+ * Gets a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation.
+ *
+ * @return a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation
+ * @see ContactSourceService#getDisplayName()
+ */
+ public String getDisplayName()
+ {
+ return "Address Book";
+ }
+
+ /**
+ * Gets a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MacOSXAddrBookContactSourceService</tt> implementation.
+ *
+ * @return a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MacOSXAddrBookContactSourceService</tt> implementation
+ * @see ContactSourceService#getType()
+ */
+ public int getType()
+ {
+ return SEARCH_TYPE;
+ }
+
+ /**
+ * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
+ * which match a specific <tt>query</tt> <tt>Pattern</tt>.
+ *
+ * @param query the <tt>Pattern</tt> which this
+ * <tt>ContactSourceService</tt> is being queried for
+ * @return a <tt>ContactQuery</tt> which represents the query of this
+ * <tt>ContactSourceService</tt> implementation for the specified
+ * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
+ * any) will be returned
+ * @see ExtendedContactSourceService#queryContactSource(Pattern)
+ */
+ public ContactQuery queryContactSource(Pattern query)
+ {
+ if(latestQuery != null)
+ latestQuery.clear();
+
+ latestQuery = new MacOSXAddrBookContactQuery(this, query);
+
+ latestQuery.start();
+ return latestQuery;
+ }
+
+ /**
+ * Starts a new native <tt>MacOSXAddrBookContactSourceService</tt> instance.
+ *
+ * @return a pointer to the newly-started native
+ * <tt>MacOSXAddrBookContactSourceService</tt> instance
+ */
+ private static native long start();
+
+ /**
+ * Stops this <tt>ContactSourceService</tt> implementation and prepares it
+ * for garbage collection.
+ *
+ * @see AsyncContactSourceService#stop()
+ */
+ public synchronized void stop()
+ {
+ if (0 != ptr)
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.clear();
+ latestQuery = null;
+ }
+
+ stop(ptr);
+ ptr = 0;
+ }
+ }
+
+ /**
+ * Returns the global phone number prefix to be used when calling contacts
+ * from this contact source.
+ *
+ * @return the global phone number prefix
+ */
+ public String getPhoneNumberPrefix()
+ {
+ return AddrBookActivator.getConfigService()
+ .getString(MACOSX_ADDR_BOOK_PREFIX);
+ }
+
+ /**
+ * Returns the index of the contact source in the result list.
+ *
+ * @return the index of the contact source in the result list
+ */
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Stops a native <tt>MacOSXAddrBookContactSourceService</tt>.
+ *
+ * @param ptr the pointer to the native
+ * <tt>MacOSXAddrBookContactSourceService</tt> to stop
+ */
+ private static native void stop(long ptr);
+
+ /**
+ * Sets notifier delegate.
+ * @param ptr
+ * @param delegate
+ */
+ public static native void setDelegate(long ptr, NotificationsDelegate delegate);
+
+ /**
+ * Delegate class to be notified for addressbook changes.
+ */
+ public class NotificationsDelegate
+ {
+ /**
+ * Callback method when receiving notifications for inserted items.
+ */
+ public void inserted(long person)
+ {
+ if(latestQuery != null)
+ latestQuery.inserted(person);
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ */
+ public void updated(long person)
+ {
+ if(latestQuery != null)
+ latestQuery.updated(person);
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ */
+ public void deleted(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.deleted(id);
+ }
+ }
+
+ /**
+ * Returns the latest query created.
+ *
+ * @return the latest query created.
+ */
+ public MacOSXAddrBookContactQuery getLatestQuery()
+ {
+ return this.latestQuery;
+ }
+
+ /**
+ * Creates a new contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @return The ID of the contact to created. NULL if failed to create a new
+ * contact.
+ */
+ public String createContact()
+ {
+ return MacOSXAddrBookContactQuery.createContact();
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null && latestQuery != null)
+ {
+ latestQuery.addEmptyContact(id);
+ }
+ }
+
+ /**
+ * Removes the given contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @param id The ID of the contact to remove.
+ */
+ public void deleteContact(String id)
+ {
+ if(id != null && MacOSXAddrBookContactQuery.deleteContact(id))
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.deleted(id);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
index 274b749..3342731 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
@@ -32,6 +32,14 @@ public class MacOSXAddrBookSourceContact
= Logger.getLogger(MacOSXAddrBookSourceContact.class);
/**
+ * Boolean used to temporarily lock the access to a single modification
+ * source (jitsi or contact). i.e. it can be useful if Jitsi modifies a
+ * batch of details and do not want to receive contact update notification
+ * which can produce concurrent changes of the details.
+ */
+ private Boolean locked = Boolean.FALSE;
+
+ /**
* Initializes a new <tt>AddrBookSourceContact</tt> instance.
*
* @param contactSource the <tt>ContactSourceService</tt> which is creating
@@ -64,7 +72,7 @@ public class MacOSXAddrBookSourceContact
*/
public void addContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
String id = (String)getData(SourceContact.DATA_ID);
@@ -160,6 +168,7 @@ public class MacOSXAddrBookSourceContact
}
contactDetails.add(index, contactDetail);
+ this.updated();
}
}
@@ -316,7 +325,7 @@ public class MacOSXAddrBookSourceContact
*/
public void removeContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
//remove the detail from the addressbook
String id = (String)getData(SourceContact.DATA_ID);
@@ -360,6 +369,7 @@ public class MacOSXAddrBookSourceContact
logger.warn("No id or wrong ContactDetail " + detail);
contactDetails.remove(detail);
+ this.updated();
}
}
@@ -369,7 +379,7 @@ public class MacOSXAddrBookSourceContact
*/
public void setDetails(List<ContactDetail> details)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
contactDetails.clear();
contactDetails.addAll(details);
@@ -377,6 +387,82 @@ public class MacOSXAddrBookSourceContact
}
/**
+ * Function called by the native part (contact) when this contact has been
+ * updated.
+ */
+ public void updated()
+ {
+ synchronized(this)
+ {
+ waitUnlock();
+
+ String id = (String)getData(SourceContact.DATA_ID);
+ ContactSourceService sourceService = getContactSource();
+ if(id != null
+ && sourceService instanceof
+ MacOSXAddrBookContactSourceService)
+ {
+ MacOSXAddrBookContactSourceService macOSXSourceService
+ = (MacOSXAddrBookContactSourceService) sourceService;
+ MacOSXAddrBookContactQuery macOSXContactQuery
+ = macOSXSourceService.getLatestQuery();
+ if(macOSXContactQuery != null)
+ {
+ long contactPointer
+ = MacOSXAddrBookContactQuery.getContactPointer(id);
+ macOSXContactQuery.updated(contactPointer);
+ //macOSXContactQuery.contactChanged(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * Locks this object before adding or removing several contact details.
+ */
+ public void lock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Unlocks this object before after or removing several contact details.
+ */
+ public void unlock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.FALSE;
+ notify();
+ }
+ }
+
+ /**
+ * Waits to be unlocked. This object must be synchronized before calling
+ * this function.
+ */
+ private void waitUnlock()
+ {
+ boolean continueToWait = this.locked;
+
+ while(continueToWait)
+ {
+ try
+ {
+ wait();
+ continueToWait = false;
+ }
+ catch(InterruptedException ie)
+ {
+ // Nothing to do, we will wait until the notify.
+ }
+ }
+ }
+
+ /**
* Returns the index of this source contact in its parent.
*
* @return the index of this source contact in its parent
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
index 2ae7c51..b886ddb 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
@@ -1,889 +1,1116 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.plugin.addrbook.msoutlook;
-
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implements <tt>ContactQuery</tt> for the Address Book of Microsoft Outlook.
- *
- * @author Lyubomir Marinov
- * @author Vincent Lucas
- */
-public class MsOutlookAddrBookContactQuery
- extends AbstractAddrBookContactQuery<MsOutlookAddrBookContactSourceService>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookContactQuery</tt>
- * class and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(MsOutlookAddrBookContactQuery.class);
-
- private static final int dispidEmail1EmailAddress = 11;
-
- private static final int dispidEmail2EmailAddress = 12;
-
- private static final int dispidEmail3EmailAddress = 13;
-
- /**
- * The object type of a <tt>SourceContact</tt> in the Address Book of
- * Microsoft Outlook.
- */
- private static final long MAPI_MAILUSER = 0x00000006;
-
- /**
- * The IDs of the properties of <tt>MAPI_MAILUSER</tt> which are to be
- * queried by the <tt>MsOutlookAddrBookContactQuery</tt> instances.
- */
- private static final long[] MAPI_MAILUSER_PROP_IDS
- = new long[]
- {
- 0x3001 /* PR_DISPLAY_NAME */,
- 0x3003 /* PR_EMAIL_ADDRESS */,
- 0x3A06 /* PR_GIVEN_NAME */,
- 0x3A44 /* PR_MIDDLE_NAME */,
- 0x3A11 /* PR_SURNAME */,
- 0x3A08 /* PR_BUSINESS_TELEPHONE_NUMBER */,
- 0x3A1B /* PR_BUSINESS2_TELEPHONE_NUMBER */,
- 0x3A09 /* PR_HOME_TELEPHONE_NUMBER */,
- 0x3A2F /* PR_HOME2_TELEPHONE_NUMBER */,
- 0x3A1C /* PR_MOBILE_TELEPHONE_NUMBER */,
- 0x0FFE /* PR_OBJECT_TYPE */,
- 0x00008083 /* dispidEmail1EmailAddress */,
- 0x00008093 /* dispidEmail2EmailAddress */,
- 0x000080A3 /* dispidEmail3EmailAddress */,
- 0x3A16 /* PR_COMPANY_NAME */,
- 0x0FFF /* PR_ORIGINAL_ENTRYID */,
- 0x3A24 /* dispidFax1EmailAddress */,
- 0x3A25 /* dispidFax2EmailAddress */,
- 0x3A23 /* dispidFax3EmailAddress */,
- 0x3A4F /* PR_NICKNAME */,
- 0x3A45 /* PR_DISPLAY_NAME_PREFIX */,
- 0x3A50 /* PR_PERSONAL_HOME_PAGE */,
- 0x3A51 /* PR_BUSINESS_HOME_PAGE */
- //0x00008062 /* dispidInstMsg */, // EDITION NOT WORKING
- //0x0000801A /* dispidHomeAddress */, // EDITION NOT WORKING
- //0x0000801B /* dispidWorkAddress */, // EDITION NOT WORKING
- //0x0000801C /* dispidOtherAddress */ // EDITION ONT WORKING
- };
-
- /**
- * The object type of a <tt>SourceContact</tt> in a Contacts folder of
- * Microsoft Outlook.
- */
- private static final long MAPI_MESSAGE = 0x00000005;
-
- /**
- * The flag which signals that MAPI strings should be returned in the
- * Unicode character set.
- */
- private static final long MAPI_UNICODE = 0x80000000;
-
- /**
- * The id of the <tt>PR_ATTACHMENT_CONTACTPHOTO</tt> MAPI property.
- */
- private static final long PR_ATTACHMENT_CONTACTPHOTO = 0x7FFF;
-
- /**
- * The index of the id of the <tt>PR_BUSINESS_TELEPHONE_NUMBER</tt> property
- * in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_BUSINESS_TELEPHONE_NUMBER = 5;
-
- /**
- * The index of the id of the <tt>PR_BUSINESS2_TELEPHONE_NUMBER</tt>
- * property in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_BUSINESS2_TELEPHONE_NUMBER = 6;
-
- private static final int PR_COMPANY_NAME = 14;
-
- /**
- * The index of the id of the <tt>PR_DISPLAY_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_DISPLAY_NAME = 0;
-
- /**
- * The index of the id of the <tt>PR_EMAIL_ADDRESS</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_EMAIL_ADDRESS = 1;
-
- /**
- * The index of the id of the <tt>PR_GIVEN_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_GIVEN_NAME = 2;
-
- /**
- * The index of the id of the <tt>PR_HOME_TELEPHONE_NUMBER</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_HOME_TELEPHONE_NUMBER = 7;
-
- /**
- * The index of the id of the <tt>PR_HOME2_TELEPHONE_NUMBER</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_HOME2_TELEPHONE_NUMBER = 8;
-
- /**
- * The index of the id of the <tt>PR_MIDDLE_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_MIDDLE_NAME = 3;
-
- /**
- * The index of the id of the <tt>PR_MOBILE_TELEPHONE_NUMBER</tt> property
- * in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_MOBILE_TELEPHONE_NUMBER = 9;
-
- /**
- * The index of the id of the <tt>PR_OBJECT_TYPE</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_OBJECT_TYPE = 10;
-
- /**
- * The index of the id of the <tt>PR_SURNAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_SURNAME = 4;
-
- /**
- * The index of the id of the <tt>PR_ORIGINAL_ENTRYID</tt> property
- * in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_ORIGINAL_ENTRYID = 15;
-
- /**
- * The index of the 1st fax telephone number (business fax).
- */
- private static final int dispidFax1EmailAddress = 16;
-
- /**
- * The index of the 2nd fax telephone number (home fax).
- */
- private static final int dispidFax2EmailAddress = 17;
-
- /**
- * The index of the 3rd fax telephone number (other fax).
- */
- private static final int dispidFax3EmailAddress = 18;
-
- /**
- * The index of the nickname.
- */
- private static final int PR_NICKNAME = 19;
-
- /**
- * The index of the name prefix.
- */
- private static final int PR_DISPLAY_NAME_PREFIX = 20;
-
- /**
- * The index of the personnal home page
- */
- private static final int PR_PERSONAL_HOME_PAGE = 21;
-
- /**
- * The index of the business home page
- */
- private static final int PR_BUSINESS_HOME_PAGE = 22;
-
- /**
- * The index of the instant messaging address.
- */
- //private static final int dispidInstMsg = 23;
-
- /**
- * The index of the home address
- */
- //private static final int dispidHomeAddress = 24;
-
- /**
- * The index of the work address
- */
- //private static final int dispidWorkAddress = 25;
-
- /**
- * The index of the other address
- */
- //private static final int dispidOtherAddress = 26;
-
- /**
- * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
- * are to be represented in <tt>SourceContact</tt> as
- * <tt>ContactDetail</tt>s.
- */
- private static final int[] CONTACT_DETAIL_PROP_INDEXES
- = new int[]
- {
- PR_EMAIL_ADDRESS,
- PR_GIVEN_NAME,
- PR_MIDDLE_NAME,
- PR_SURNAME,
- PR_BUSINESS_TELEPHONE_NUMBER,
- PR_BUSINESS2_TELEPHONE_NUMBER,
- PR_HOME_TELEPHONE_NUMBER,
- PR_HOME2_TELEPHONE_NUMBER,
- PR_MOBILE_TELEPHONE_NUMBER,
- dispidEmail1EmailAddress,
- dispidEmail2EmailAddress,
- dispidEmail3EmailAddress,
- PR_COMPANY_NAME,
- dispidFax1EmailAddress,
- dispidFax2EmailAddress,
- dispidFax3EmailAddress,
- PR_NICKNAME,
- PR_DISPLAY_NAME_PREFIX,
- PR_PERSONAL_HOME_PAGE,
- PR_BUSINESS_HOME_PAGE
- //dispidInstMsg,
- //dispidHomeAddress,
- //dispidWorkAddress,
- //dispidOtherAddress
- };
-
- /**
- * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
- * represent an identifier which can be used for telephony or persistent
- * presence.
- */
- private static final int[] CONTACT_OPERATION_SET_ABLE_PROP_INDEXES
- = new int[]
- {
- PR_EMAIL_ADDRESS,
- PR_BUSINESS_TELEPHONE_NUMBER,
- PR_BUSINESS2_TELEPHONE_NUMBER,
- PR_HOME_TELEPHONE_NUMBER,
- PR_HOME2_TELEPHONE_NUMBER,
- PR_MOBILE_TELEPHONE_NUMBER,
- dispidEmail1EmailAddress,
- dispidEmail2EmailAddress,
- dispidEmail3EmailAddress,
- dispidFax1EmailAddress,
- dispidFax2EmailAddress,
- dispidFax3EmailAddress
- //dispidInstMsg
- };
-
- static
- {
- System.loadLibrary("jmsoutlookaddrbook");
- }
-
- /**
- * The number of <tt>SourceContact</tt>s matching this <tt>ContactQuery</tt>
- * which have been retrieved from Contacts folders. Since each one of them
- * may appear multiple times in the Address Book as well, no matching in the
- * Address Book will be performed if there is at least one matching
- * <tt>SourceContact</tt> in a Contacts folder.
- */
- private int mapiMessageCount;
-
- /**
- * Initializes a new <tt>MsOutlookAddrBookContactQuery</tt> instance to
- * be performed by a specific
- * <tt>MsOutlookAddrBookContactSourceService</tt>.
- *
- * @param msoabcss the <tt>MsOutlookAddrBookContactSourceService</tt>
- * which is to perform the new <tt>ContactQuery</tt>
- * @param query the <tt>Pattern</tt> for which <tt>msoabcss</tt> is being
- * queried
- */
- public MsOutlookAddrBookContactQuery(
- MsOutlookAddrBookContactSourceService msoabcss,
- Pattern query)
- {
- super(msoabcss, query);
- }
-
- /**
- * Calls back to a specific <tt>PtrCallback</tt> for each
- * <tt>MAPI_MAILUSER</tt> found in the Address Book of Microsoft Outlook
- * which matches a specific <tt>String</tt> query.
- *
- * @param query the <tt>String</tt> for which the Address Book of Microsoft
- * Outlook is to be queried. <b>Warning</b>: Ignored at the time of this
- * writing.
- * @param callback the <tt>PtrCallback</tt> to be notified about the
- * matching <tt>MAPI_MAILUSER</tt>s
- */
- public static native void foreachMailUser(
- String query,
- PtrCallback callback);
-
- private ContactDetail.Category getCategory(int propIndex)
- {
- switch (propIndex)
- {
- case PR_GIVEN_NAME:
- case PR_MIDDLE_NAME:
- case PR_SURNAME:
- case PR_NICKNAME:
- case PR_DISPLAY_NAME_PREFIX:
- case PR_PERSONAL_HOME_PAGE:
- return ContactDetail.Category.Personal;
- case PR_COMPANY_NAME:
- case PR_BUSINESS_HOME_PAGE:
- return ContactDetail.Category.Organization;
- case dispidEmail1EmailAddress:
- case dispidEmail2EmailAddress:
- case dispidEmail3EmailAddress:
- case PR_EMAIL_ADDRESS:
- return ContactDetail.Category.Email;
- case PR_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- case PR_HOME2_TELEPHONE_NUMBER:
- case PR_HOME_TELEPHONE_NUMBER:
- case PR_MOBILE_TELEPHONE_NUMBER:
- case dispidFax1EmailAddress:
- case dispidFax2EmailAddress:
- case dispidFax3EmailAddress:
- return ContactDetail.Category.Phone;
- //case dispidInstMsg:
- // return ContactDetail.Category.InstantMessaging;
- //case dispidHomeAddress:
- //case dispidWorkAddress:
- //case dispidOtherAddress:
- // return ContactDetail.Category.Address;
- default:
- return null;
- }
- }
-
- /**
- * Gets the set of <tt>ContactDetail</tt> labels to be assigned to a
- * property specified by its index in {@link #MAPI_MAILUSER_PROP_IDS}.
- *
- * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
- * property to get the <tt>ContactDetail</tt> labels of
- * @return the set of <tt>ContactDetail</tt> labels to be assigned to the
- * property specified by its index in <tt>MAPI_MAILUSER_PROP_IDS</tt>
- */
- private ContactDetail.SubCategory[] getSubCategories(int propIndex)
- {
- switch (propIndex)
- {
- case PR_GIVEN_NAME:
- case PR_MIDDLE_NAME:
- case PR_COMPANY_NAME:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Name
- };
- case PR_SURNAME:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.LastName
- };
- case PR_NICKNAME:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Nickname
- };
- case PR_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- case dispidEmail1EmailAddress:
- case PR_EMAIL_ADDRESS:
- //case dispidWorkAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Work
- };
- case PR_HOME2_TELEPHONE_NUMBER:
- case PR_HOME_TELEPHONE_NUMBER:
- case dispidEmail2EmailAddress:
- //case dispidHomeAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Home
- };
- case PR_MOBILE_TELEPHONE_NUMBER:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Mobile
- };
- case dispidFax1EmailAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Fax,
- ContactDetail.SubCategory.Work
- };
- case dispidFax2EmailAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Fax,
- ContactDetail.SubCategory.Home
- };
- case dispidFax3EmailAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Fax,
- ContactDetail.SubCategory.Other
- };
- //case dispidOtherAddress:
- // return
- // new ContactDetail.SubCategory[]
- // {
- // ContactDetail.SubCategory.Other
- // };
- case dispidEmail3EmailAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Other
- };
- default:
- return null;
- }
- }
-
- /**
- * Find the outlook property tag from category and subcategories.
- *
- * @param category The category.
- * @param subCategories The subcategories.
- *
- * @return The outlook property tag corresponding to the given category and
- * subcategories.
- */
- public static long getProperty(
- ContactDetail.Category category,
- Collection<ContactDetail.SubCategory> subCategories)
- {
- switch(category)
- {
- case Personal:
- if(subCategories.contains(ContactDetail.SubCategory.Name))
- return MAPI_MAILUSER_PROP_IDS[PR_GIVEN_NAME];
- // PR_MIDDLE_NAME:
- else if(subCategories.contains(
- ContactDetail.SubCategory.LastName))
- return MAPI_MAILUSER_PROP_IDS[PR_SURNAME];
- else if(subCategories.contains(
- ContactDetail.SubCategory.Nickname))
- return MAPI_MAILUSER_PROP_IDS[PR_NICKNAME];
- else if(subCategories.contains(
- ContactDetail.SubCategory.HomePage))
- return MAPI_MAILUSER_PROP_IDS[PR_PERSONAL_HOME_PAGE];
- else
- return MAPI_MAILUSER_PROP_IDS[PR_DISPLAY_NAME_PREFIX];
- case Organization:
- if(subCategories.contains(ContactDetail.SubCategory.Name))
- return MAPI_MAILUSER_PROP_IDS[PR_COMPANY_NAME];
- else
- return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_HOME_PAGE];
- case Email:
- if(subCategories.contains(ContactDetail.SubCategory.Work))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail1EmailAddress];
- // PR_EMAIL_ADDRESS:
- else if(subCategories.contains(
- ContactDetail.SubCategory.Home))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail2EmailAddress];
- else if(subCategories.contains(
- ContactDetail.SubCategory.Other))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail3EmailAddress];
- break;
- case Phone:
- if(subCategories.contains(ContactDetail.SubCategory.Fax))
- {
- if(subCategories.contains(ContactDetail.SubCategory.Work))
- return MAPI_MAILUSER_PROP_IDS[dispidFax1EmailAddress];
- else if(subCategories.contains(
- ContactDetail.SubCategory.Home))
- return MAPI_MAILUSER_PROP_IDS[dispidFax2EmailAddress];
- else if(subCategories.contains(
- ContactDetail.SubCategory.Other))
- return MAPI_MAILUSER_PROP_IDS[dispidFax3EmailAddress];
- }
- else if(subCategories.contains(ContactDetail.SubCategory.Work))
- return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_TELEPHONE_NUMBER];
- // PR_BUSINESS2_TELEPHONE_NUMBER:
- else if(subCategories.contains(ContactDetail.SubCategory.Home))
- return MAPI_MAILUSER_PROP_IDS[PR_HOME_TELEPHONE_NUMBER];
- // PR_HOME2_TELEPHONE_NUMBER:
- else if(subCategories.contains(
- ContactDetail.SubCategory.Mobile))
- return MAPI_MAILUSER_PROP_IDS[PR_MOBILE_TELEPHONE_NUMBER];
- break;
-// case InstantMessaging:
-// return MAPI_MAILUSER_PROP_IDS[dispidInstMsg];
-// case Address:
-// if(subCategories.contains(ContactDetail.SubCategory.Work))
-// return MAPI_MAILUSER_PROP_IDS[dispidWorkAddress];
-// else if(subCategories.contains(
-// ContactDetail.SubCategory.Home))
-// return MAPI_MAILUSER_PROP_IDS[dispidHomeAddress];
-// else if(subCategories.contains(
-// ContactDetail.SubCategory.Other))
-// return MAPI_MAILUSER_PROP_IDS[dispidOtherAddress];
-// break;
- default:
- // Silence the compiler.
- break;
- }
- return -1;
- }
-
- private static native Object[] IMAPIProp_GetProps(
- long mapiProp,
- long[] propIds, long flags)
- throws MsOutlookMAPIHResultException;
-
- public static native boolean IMAPIProp_SetPropString(
- long propId,
- String value,
- String entryId);
-
- public static native boolean IMAPIProp_DeleteProp(
- long propId,
- String entryId);
-
- /**
- * Determines whether a specific index in {@link #MAPI_MAILUSER_PROP_IDS}
- * stands for a property with a phone number value.
- *
- * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
- * property to check
- * @return <tt>true</tt> if <tt>propIndex</tt> stands for a property with a
- * phone number value; otherwise, <tt>false</tt>
- */
- private boolean isPhoneNumber(int propIndex)
- {
- switch (propIndex)
- {
- case PR_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- case PR_HOME2_TELEPHONE_NUMBER:
- case PR_HOME_TELEPHONE_NUMBER:
- case PR_MOBILE_TELEPHONE_NUMBER:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Determines whether a specific <tt>MAPI_MAILUSER</tt> property with a
- * specific <tt>value</tt> matches the {@link #query} of this
- * <tt>AsyncContactQuery</tt>.
- *
- * @param property the <tt>MAPI_MAILUSER</tt> property to check
- * @param value the value of the <tt>property</tt> to check
- * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
- * <tt>property</tt> matches the <tt>query</tt> of this
- * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
- */
- private boolean matches(int property, String value)
- {
- return
- query.matcher(value).find()
- || (isPhoneNumber(property) && phoneNumberMatches(value));
- }
-
- /**
- * Notifies this <tt>MsOutlookAddrBookContactQuery</tt> about a specific
- * <tt>MAPI_MAILUSER</tt>.
- *
- * @param iUnknown a pointer to an <tt>IUnknown</tt> instance for the
- * <tt>MAPI_MAILUSER</tt> to notify about
- * @return <tt>true</tt> if this <tt>MsOutlookAddrBookContactQuery</tt>
- * is to continue being called; otherwise, <tt>false</tt>
- * @throws MsOutlookMAPIHResultException if anything goes wrong while
- * getting the properties of the specified <tt>MAPI_MAILUSER</tt>
- */
- private boolean onMailUser(long iUnknown)
- throws MsOutlookMAPIHResultException
- {
- Object[] props
- = IMAPIProp_GetProps(
- iUnknown,
- MAPI_MAILUSER_PROP_IDS,
- MAPI_UNICODE);
- long objType
- = (props[PR_OBJECT_TYPE] instanceof Long)
- ? ((Long) props[PR_OBJECT_TYPE]).longValue()
- : 0;
-
- /*
- * If we have results from the Contacts folder(s), don't read from the
- * Address Book because there may be duplicates.
- */
- if ((MAPI_MAILUSER == objType) && (mapiMessageCount != 0))
- return false;
-
- int propIndex = 0;
- boolean matches = false;
-
- for (Object prop : props)
- {
- if ((prop instanceof String) && matches(propIndex, (String) prop))
- {
- matches = true;
- break;
- }
- propIndex++;
- }
- if (matches)
- {
- List<ContactDetail> contactDetails = getContactDetails(props);
-
- /*
- * What's the point of showing a contact who has no contact details?
- */
- if (!contactDetails.isEmpty())
- {
- String displayName = (String) props[PR_DISPLAY_NAME];
-
- if ((displayName == null) || (displayName.length() == 0))
- displayName = (String) props[PR_COMPANY_NAME];
-
- MsOutlookAddrBookSourceContact sourceContact
- = new MsOutlookAddrBookSourceContact(
- getContactSource(),
- (String) props[PR_ORIGINAL_ENTRYID],
- displayName,
- contactDetails);
-
- if (MAPI_MESSAGE == objType)
- {
- ++mapiMessageCount;
-
- try
- {
- Object[] images
- = IMAPIProp_GetProps(
- iUnknown,
- new long[] { PR_ATTACHMENT_CONTACTPHOTO },
- 0);
- Object image = images[0];
-
- if (image instanceof byte[])
- sourceContact.setImage((byte[]) image);
- }
- catch (MsOutlookMAPIHResultException ex)
- {
- /*
- * Ignore it, the image isn't as vital as the
- * SourceContact.
- */
- }
- }
-
- addQueryResult(sourceContact);
- }
- }
- return (getStatus() == QUERY_IN_PROGRESS);
- }
-
- /**
- * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent an <tt>ABPerson</tt> specified by the values of its
- * {@link #ABPERSON_PROPERTIES}.
- *
- * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
- * represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of
- * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
- */
- private List<ContactDetail> getContactDetails(Object[] values)
- {
- List<Class<? extends OperationSet>> supportedOpSets
- = new ArrayList<Class<? extends OperationSet>>(2);
- supportedOpSets.add(OperationSetBasicTelephony.class);
- // can be added as contacts
- supportedOpSets.add(OperationSetPersistentPresence.class);
-
- List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
-
- for (int i = 0; i < CONTACT_DETAIL_PROP_INDEXES.length; i++)
- {
- int property = CONTACT_DETAIL_PROP_INDEXES[i];
- Object value = values[property];
-
- if (value instanceof String)
- {
- String stringValue = (String) value;
-
- if (stringValue.length() != 0)
- {
- if(isPhoneNumber(property))
- stringValue
- = PhoneNumberI18nService.normalize(stringValue);
-
- MsOutlookAddrBookContactDetail contactDetail
- = new MsOutlookAddrBookContactDetail(
- stringValue,
- getCategory(property),
- getSubCategories(property),
- MAPI_MAILUSER_PROP_IDS[property]);
-
- // Check if this contact detail support the telephony and
- // the persistent presence operation set.
- for(int j = 0;
- j < CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
- ++j)
- {
- if(property
- == CONTACT_OPERATION_SET_ABLE_PROP_INDEXES[j])
- {
- contactDetail.setSupportedOpSets(supportedOpSets);
- // Found, then break the loop.
- j = CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
- }
- }
- contactDetails.add(contactDetail);
- }
- }
- }
-
- return contactDetails;
- }
-
- /**
- * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
- *
- * @see AsyncContactQuery#run()
- */
- protected void run()
- {
- synchronized (MsOutlookAddrBookContactQuery.class)
- {
- foreachMailUser(
- query.toString(),
- new PtrCallback()
- {
- public boolean callback(long iUnknown)
- {
- try
- {
- return onMailUser(iUnknown);
- }
- catch (MsOutlookMAPIHResultException e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug(
- MsOutlookAddrBookContactQuery.class
- .getSimpleName()
- + "#onMailUser(long)",
- e);
- }
- return false;
- }
- }
- });
- }
- }
-
- /**
- * Callback method when receiving notifications for inserted items.
- *
- * @param person The pointer to the outlook contact object.
- */
- public void inserted(long person)
- {
- try
- {
- onMailUser(person);
- }
- catch (MsOutlookMAPIHResultException e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug(
- MsOutlookAddrBookContactQuery.class.getSimpleName()
- + "#onMailUser(long)",
- e);
- }
- }
- }
-
- /**
- * Callback method when receiving notifications for updated items.
- *
- * @param person The pointer to the outlook contact object.
- */
- public void updated(long person)
- {
- Object[] props = null;
- try
- {
- props = IMAPIProp_GetProps(
- person,
- MAPI_MAILUSER_PROP_IDS,
- MAPI_UNICODE);
- }
- catch (MsOutlookMAPIHResultException e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug(
- MsOutlookAddrBookContactQuery.class.getSimpleName()
- + "#IMAPIProp_GetProps(long, long[], long)",
- e);
- }
- }
-
- if(props[PR_ORIGINAL_ENTRYID] != null)
- {
- SourceContact sourceContact
- = findSourceContactByID((String) props[PR_ORIGINAL_ENTRYID]);
-
- if(sourceContact != null
- && sourceContact instanceof MsOutlookAddrBookSourceContact)
- {
- // let's update the the details
- MsOutlookAddrBookSourceContact editableSourceContact
- = (MsOutlookAddrBookSourceContact) sourceContact;
-
- List<ContactDetail> contactDetails = getContactDetails(props);
- editableSourceContact.setDetails(contactDetails);
-
- fireContactChanged(sourceContact);
- }
- }
- }
-
- /**
- * Callback method when receiving notifications for deleted items.
- *
- * @param id The outlook contact identifier.
- */
- public void deleted(String id)
- {
- if(id != null)
- {
- SourceContact sourceContact = findSourceContactByID(id);
-
- if(sourceContact != null)
- {
- fireContactRemoved(sourceContact);
- }
- }
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.addrbook.msoutlook;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactQuery</tt> for the Address Book of Microsoft Outlook.
+ *
+ * @author Lyubomir Marinov
+ * @author Vincent Lucas
+ */
+public class MsOutlookAddrBookContactQuery
+ extends AbstractAddrBookContactQuery<MsOutlookAddrBookContactSourceService>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookContactQuery</tt>
+ * class and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MsOutlookAddrBookContactQuery.class);
+
+ private static final int dispidEmail1EmailAddress = 12;
+
+ private static final int dispidEmail2EmailAddress = 13;
+
+ private static final int dispidEmail3EmailAddress = 14;
+
+ /**
+ * The object type of a <tt>SourceContact</tt> in the Address Book of
+ * Microsoft Outlook.
+ */
+ private static final long MAPI_MAILUSER = 0x00000006;
+
+ /**
+ * The IDs of the properties of <tt>MAPI_MAILUSER</tt> which are to be
+ * queried by the <tt>MsOutlookAddrBookContactQuery</tt> instances.
+ */
+ public static final long[] MAPI_MAILUSER_PROP_IDS
+ = new long[]
+ {
+ 0x3001 /* PR_DISPLAY_NAME */,
+ 0x3003 /* PR_EMAIL_ADDRESS */,
+ 0x3A06 /* PR_GIVEN_NAME */,
+ 0x3A44 /* PR_MIDDLE_NAME */,
+ 0x3A11 /* PR_SURNAME */,
+ 0x3A08 /* PR_BUSINESS_TELEPHONE_NUMBER */,
+ 0x3A1B /* PR_BUSINESS2_TELEPHONE_NUMBER */,
+ 0x3A09 /* PR_HOME_TELEPHONE_NUMBER */,
+ 0x3A2F /* PR_HOME2_TELEPHONE_NUMBER */,
+ 0x3A1C /* PR_MOBILE_TELEPHONE_NUMBER */,
+ 0x3A1F /* PR_OTHER_TELEPHONE_NUMBER */,
+ 0x0FFE /* PR_OBJECT_TYPE */,
+ 0x00008083 /* dispidEmail1EmailAddress */,
+ 0x00008093 /* dispidEmail2EmailAddress */,
+ 0x000080A3 /* dispidEmail3EmailAddress */,
+ 0x3A16 /* PR_COMPANY_NAME */,
+ 0x0FFF /* PR_ORIGINAL_ENTRYID */,
+ 0x3A24 /* dispidFax1EmailAddress */,
+ 0x3A25 /* dispidFax2EmailAddress */,
+ 0x3A23 /* dispidFax3EmailAddress */,
+ 0x3A4F /* PR_NICKNAME */,
+ 0x3A45 /* PR_DISPLAY_NAME_PREFIX */,
+ 0x3A50 /* PR_PERSONAL_HOME_PAGE */,
+ 0x3A51 /* PR_BUSINESS_HOME_PAGE */,
+ 0x3A17 /* PR_TITLE */,
+ 0x00008062 /* dispidInstMsg */,
+ 0x3A27, // PR_BUSINESS_ADDRESS_CITY
+ 0x3A26, // PR_BUSINESS_ADDRESS_COUNTRY
+ 0x3A2A, // PR_BUSINESS_ADDRESS_POSTAL_CODE
+ 0x3A28, // PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE
+ 0x3A29, // PR_BUSINESS_ADDRESS_STREET
+
+ 0x3A59, // PR_HOME_ADDRESS_CITY
+ 0x3A5A, // PR_HOME_ADDRESS_COUNTRY
+ 0x3A5B, // PR_HOME_ADDRESS_POSTAL_CODE
+ 0x3A5C, // PR_HOME_ADDRESS_STATE_OR_PROVINCE
+ 0x3A5D // PR_HOME_ADDRESS_STREET
+ };
+
+ /**
+ * The object type of a <tt>SourceContact</tt> in a Contacts folder of
+ * Microsoft Outlook.
+ */
+ private static final long MAPI_MESSAGE = 0x00000005;
+
+ /**
+ * The flag which signals that MAPI strings should be returned in the
+ * Unicode character set.
+ */
+ public static final long MAPI_UNICODE = 0x80000000;
+
+ /**
+ * The id of the <tt>PR_ATTACHMENT_CONTACTPHOTO</tt> MAPI property.
+ */
+ private static final long PR_ATTACHMENT_CONTACTPHOTO = 0x7FFF;
+
+ /**
+ * The index of the id of the <tt>PR_BUSINESS_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_BUSINESS_TELEPHONE_NUMBER = 5;
+
+ /**
+ * The index of the id of the <tt>PR_BUSINESS2_TELEPHONE_NUMBER</tt>
+ * property in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_BUSINESS2_TELEPHONE_NUMBER = 6;
+
+ private static final int PR_COMPANY_NAME = 15;
+
+ /**
+ * The index of the id of the <tt>PR_DISPLAY_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_DISPLAY_NAME = 0;
+
+ /**
+ * The index of the id of the <tt>PR_EMAIL_ADDRESS</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_EMAIL_ADDRESS = 1;
+
+ /**
+ * The index of the id of the <tt>PR_GIVEN_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_GIVEN_NAME = 2;
+
+ /**
+ * The index of the id of the <tt>PR_HOME_TELEPHONE_NUMBER</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_HOME_TELEPHONE_NUMBER = 7;
+
+ /**
+ * The index of the id of the <tt>PR_HOME2_TELEPHONE_NUMBER</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_HOME2_TELEPHONE_NUMBER = 8;
+
+ /**
+ * The index of the id of the <tt>PR_MIDDLE_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_MIDDLE_NAME = 3;
+
+ /**
+ * The index of the id of the <tt>PR_MOBILE_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_MOBILE_TELEPHONE_NUMBER = 9;
+
+ /**
+ * The index of the id of the <tt>PR_OTHER_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_OTHER_TELEPHONE_NUMBER = 10;
+
+ /**
+ * The index of the id of the <tt>PR_OBJECT_TYPE</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_OBJECT_TYPE = 11;
+
+ /**
+ * The index of the id of the <tt>PR_SURNAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_SURNAME = 4;
+
+ /**
+ * The index of the id of the <tt>PR_ORIGINAL_ENTRYID</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_ORIGINAL_ENTRYID = 16;
+
+ /**
+ * The index of the 1st fax telephone number (business fax).
+ */
+ private static final int dispidFax1EmailAddress = 17;
+
+ /**
+ * The index of the 2nd fax telephone number (home fax).
+ */
+ private static final int dispidFax2EmailAddress = 18;
+
+ /**
+ * The index of the 3rd fax telephone number (other fax).
+ */
+ private static final int dispidFax3EmailAddress = 19;
+
+ /**
+ * The index of the nickname.
+ */
+ private static final int PR_NICKNAME = 20;
+
+ /**
+ * The index of the name prefix.
+ */
+ private static final int PR_DISPLAY_NAME_PREFIX = 21;
+
+ /**
+ * The index of the personnal home page
+ */
+ private static final int PR_PERSONAL_HOME_PAGE = 22;
+
+ /**
+ * The index of the business home page
+ */
+ private static final int PR_BUSINESS_HOME_PAGE = 23;
+
+ /**
+ * The index of the job title.
+ */
+ private static final int PR_TITLE = 24;
+
+ /**
+ * The index of the instant messaging address.
+ */
+ private static final int dispidInstMsg = 25;
+
+ /**
+ * The index of the business city of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_CITY = 26;
+
+ /**
+ * The index of the business country of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_COUNTRY = 27;
+
+ /**
+ * The index of the business postal code of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_POSTAL_CODE = 28;
+
+ /**
+ * The index of the business state or province of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE = 29;
+
+ /**
+ * The index of the business street of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_STREET = 30;
+
+ /**
+ * The index of the home city of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_CITY = 31;
+
+ /**
+ * The index of the home country of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_COUNTRY = 32;
+
+ /**
+ * The index of the home postal code of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_POSTAL_CODE = 33;
+
+ /**
+ * The index of the home state or province of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_STATE_OR_PROVINCE = 34;
+
+ /**
+ * The index of the home street of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_STREET = 35;
+
+ /**
+ * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
+ * are to be represented in <tt>SourceContact</tt> as
+ * <tt>ContactDetail</tt>s.
+ */
+ private static final int[] CONTACT_DETAIL_PROP_INDEXES
+ = new int[]
+ {
+ PR_EMAIL_ADDRESS,
+ PR_GIVEN_NAME,
+ PR_MIDDLE_NAME,
+ PR_SURNAME,
+ PR_BUSINESS_TELEPHONE_NUMBER,
+ PR_BUSINESS2_TELEPHONE_NUMBER,
+ PR_HOME_TELEPHONE_NUMBER,
+ PR_HOME2_TELEPHONE_NUMBER,
+ PR_MOBILE_TELEPHONE_NUMBER,
+ PR_OTHER_TELEPHONE_NUMBER,
+ dispidEmail1EmailAddress,
+ dispidEmail2EmailAddress,
+ dispidEmail3EmailAddress,
+ PR_COMPANY_NAME,
+ dispidFax1EmailAddress,
+ dispidFax2EmailAddress,
+ dispidFax3EmailAddress,
+ PR_NICKNAME,
+ PR_DISPLAY_NAME_PREFIX,
+ PR_PERSONAL_HOME_PAGE,
+ PR_BUSINESS_HOME_PAGE,
+ PR_TITLE,
+ dispidInstMsg,
+ PR_BUSINESS_ADDRESS_CITY,
+ PR_BUSINESS_ADDRESS_COUNTRY,
+ PR_BUSINESS_ADDRESS_POSTAL_CODE,
+ PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE,
+ PR_BUSINESS_ADDRESS_STREET,
+ PR_HOME_ADDRESS_CITY,
+ PR_HOME_ADDRESS_COUNTRY,
+ PR_HOME_ADDRESS_POSTAL_CODE,
+ PR_HOME_ADDRESS_STATE_OR_PROVINCE,
+ PR_HOME_ADDRESS_STREET
+ };
+
+ /**
+ * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
+ * represent an identifier which can be used for telephony or persistent
+ * presence.
+ */
+ private static final int[] CONTACT_OPERATION_SET_ABLE_PROP_INDEXES
+ = new int[]
+ {
+ PR_EMAIL_ADDRESS,
+ PR_BUSINESS_TELEPHONE_NUMBER,
+ PR_BUSINESS2_TELEPHONE_NUMBER,
+ PR_HOME_TELEPHONE_NUMBER,
+ PR_HOME2_TELEPHONE_NUMBER,
+ PR_MOBILE_TELEPHONE_NUMBER,
+ PR_OTHER_TELEPHONE_NUMBER,
+ dispidEmail1EmailAddress,
+ dispidEmail2EmailAddress,
+ dispidEmail3EmailAddress,
+ dispidFax1EmailAddress,
+ dispidFax2EmailAddress,
+ dispidFax3EmailAddress,
+ dispidInstMsg
+ };
+
+ static
+ {
+ System.loadLibrary("jmsoutlookaddrbook");
+ }
+
+ /**
+ * The number of <tt>SourceContact</tt>s matching this <tt>ContactQuery</tt>
+ * which have been retrieved from Contacts folders. Since each one of them
+ * may appear multiple times in the Address Book as well, no matching in the
+ * Address Book will be performed if there is at least one matching
+ * <tt>SourceContact</tt> in a Contacts folder.
+ */
+ private int mapiMessageCount;
+
+ /**
+ * Initializes a new <tt>MsOutlookAddrBookContactQuery</tt> instance to
+ * be performed by a specific
+ * <tt>MsOutlookAddrBookContactSourceService</tt>.
+ *
+ * @param msoabcss the <tt>MsOutlookAddrBookContactSourceService</tt>
+ * which is to perform the new <tt>ContactQuery</tt>
+ * @param query the <tt>Pattern</tt> for which <tt>msoabcss</tt> is being
+ * queried
+ */
+ public MsOutlookAddrBookContactQuery(
+ MsOutlookAddrBookContactSourceService msoabcss,
+ Pattern query)
+ {
+ super(msoabcss, query);
+ }
+
+ /**
+ * Calls back to a specific <tt>PtrCallback</tt> for each
+ * <tt>MAPI_MAILUSER</tt> found in the Address Book of Microsoft Outlook
+ * which matches a specific <tt>String</tt> query.
+ *
+ * @param query the <tt>String</tt> for which the Address Book of Microsoft
+ * Outlook is to be queried. <b>Warning</b>: Ignored at the time of this
+ * writing.
+ * @param callback the <tt>PtrOutlookContactCallback</tt> to be notified
+ * about the matching <tt>MAPI_MAILUSER</tt>s
+ */
+ public static native void foreachMailUser(
+ String query,
+ PtrOutlookContactCallback callback);
+
+ private static ContactDetail.Category getCategory(int propIndex)
+ {
+ switch (propIndex)
+ {
+ case PR_GIVEN_NAME:
+ case PR_MIDDLE_NAME:
+ case PR_SURNAME:
+ case PR_NICKNAME:
+ case PR_DISPLAY_NAME_PREFIX:
+ case PR_PERSONAL_HOME_PAGE:
+ return ContactDetail.Category.Personal;
+ case PR_COMPANY_NAME:
+ case PR_BUSINESS_HOME_PAGE:
+ case PR_TITLE:
+ return ContactDetail.Category.Organization;
+ case dispidEmail1EmailAddress:
+ case dispidEmail2EmailAddress:
+ case dispidEmail3EmailAddress:
+ case PR_EMAIL_ADDRESS:
+ return ContactDetail.Category.Email;
+ case PR_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ case PR_OTHER_TELEPHONE_NUMBER:
+ case dispidFax1EmailAddress:
+ case dispidFax2EmailAddress:
+ case dispidFax3EmailAddress:
+ return ContactDetail.Category.Phone;
+ case dispidInstMsg:
+ return ContactDetail.Category.InstantMessaging;
+ case PR_BUSINESS_ADDRESS_CITY:
+ case PR_BUSINESS_ADDRESS_COUNTRY:
+ case PR_BUSINESS_ADDRESS_POSTAL_CODE:
+ case PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE:
+ case PR_BUSINESS_ADDRESS_STREET:
+ case PR_HOME_ADDRESS_CITY:
+ case PR_HOME_ADDRESS_COUNTRY:
+ case PR_HOME_ADDRESS_POSTAL_CODE:
+ case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
+ case PR_HOME_ADDRESS_STREET:
+ return ContactDetail.Category.Address;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the set of <tt>ContactDetail</tt> labels to be assigned to a
+ * property specified by its index in {@link #MAPI_MAILUSER_PROP_IDS}.
+ *
+ * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
+ * property to get the <tt>ContactDetail</tt> labels of
+ * @return the set of <tt>ContactDetail</tt> labels to be assigned to the
+ * property specified by its index in <tt>MAPI_MAILUSER_PROP_IDS</tt>
+ */
+ private static ContactDetail.SubCategory[] getSubCategories(int propIndex)
+ {
+ switch (propIndex)
+ {
+ case PR_GIVEN_NAME:
+ case PR_MIDDLE_NAME:
+ case PR_COMPANY_NAME:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Name
+ };
+ case PR_SURNAME:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.LastName
+ };
+ case PR_NICKNAME:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Nickname
+ };
+ case PR_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case dispidEmail2EmailAddress:
+ case PR_EMAIL_ADDRESS:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work
+ };
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case dispidEmail1EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home
+ };
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Mobile
+ };
+ case PR_OTHER_TELEPHONE_NUMBER:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Other
+ };
+ case dispidFax1EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Fax,
+ };
+ case dispidEmail3EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Other
+ };
+ case PR_TITLE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.JobTitle
+ };
+ case PR_BUSINESS_ADDRESS_CITY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.City
+ };
+ case PR_BUSINESS_ADDRESS_COUNTRY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.Country
+ };
+ case PR_BUSINESS_ADDRESS_POSTAL_CODE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.PostalCode
+ };
+ case PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.State
+ };
+ case PR_BUSINESS_ADDRESS_STREET:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.Street
+ };
+ case PR_HOME_ADDRESS_CITY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.City
+ };
+ case PR_HOME_ADDRESS_COUNTRY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.Country
+ };
+ case PR_HOME_ADDRESS_POSTAL_CODE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.PostalCode
+ };
+ case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.State
+ };
+ case PR_HOME_ADDRESS_STREET:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.Street
+ };
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Find the outlook property tag from category and subcategories.
+ *
+ * @param category The category.
+ * @param subCategories The subcategories.
+ *
+ * @return The outlook property tag corresponding to the given category and
+ * subcategories.
+ */
+ public static long getProperty(
+ ContactDetail.Category category,
+ Collection<ContactDetail.SubCategory> subCategories)
+ {
+ switch(category)
+ {
+ case Personal:
+ if(subCategories.contains(ContactDetail.SubCategory.Name))
+ return MAPI_MAILUSER_PROP_IDS[PR_GIVEN_NAME];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.LastName))
+ return MAPI_MAILUSER_PROP_IDS[PR_SURNAME];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Nickname))
+ return MAPI_MAILUSER_PROP_IDS[PR_NICKNAME];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.HomePage))
+ return MAPI_MAILUSER_PROP_IDS[PR_PERSONAL_HOME_PAGE];
+ else
+ return MAPI_MAILUSER_PROP_IDS[PR_DISPLAY_NAME_PREFIX];
+ case Organization:
+ if(subCategories.contains(ContactDetail.SubCategory.Name))
+ return MAPI_MAILUSER_PROP_IDS[PR_COMPANY_NAME];
+ else if(subCategories.contains(ContactDetail.SubCategory.JobTitle))
+ return MAPI_MAILUSER_PROP_IDS[PR_TITLE];
+ else
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_HOME_PAGE];
+ case Email:
+ if(subCategories.contains(ContactDetail.SubCategory.Work))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail2EmailAddress];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Home))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail1EmailAddress];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Other))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail3EmailAddress];
+ break;
+ case Phone:
+ if(subCategories.contains(ContactDetail.SubCategory.Fax))
+ return MAPI_MAILUSER_PROP_IDS[dispidFax1EmailAddress];
+ else if(subCategories.contains(ContactDetail.SubCategory.Work))
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_TELEPHONE_NUMBER];
+ else if(subCategories.contains(ContactDetail.SubCategory.Home))
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_TELEPHONE_NUMBER];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Mobile))
+ return MAPI_MAILUSER_PROP_IDS[PR_MOBILE_TELEPHONE_NUMBER];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Other))
+ return MAPI_MAILUSER_PROP_IDS[PR_OTHER_TELEPHONE_NUMBER];
+ break;
+ case InstantMessaging:
+ return MAPI_MAILUSER_PROP_IDS[dispidInstMsg];
+ case Address:
+ if(subCategories.contains(ContactDetail.SubCategory.Work))
+ {
+ if(subCategories.contains(ContactDetail.SubCategory.City))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_CITY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Country))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_COUNTRY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.PostalCode))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_BUSINESS_ADDRESS_POSTAL_CODE];
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.State))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Street))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_STREET];
+ }
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.Home))
+ {
+ if(subCategories.contains(ContactDetail.SubCategory.City))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_CITY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Country))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_COUNTRY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.PostalCode))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_POSTAL_CODE];
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.State))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_HOME_ADDRESS_STATE_OR_PROVINCE];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Street))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_STREET];
+ }
+ }
+ }
+ return -1;
+ }
+
+ public static native Object[] IMAPIProp_GetProps(
+ String entryId,
+ long[] propIds, long flags)
+ throws MsOutlookMAPIHResultException;
+
+ public static native boolean IMAPIProp_SetPropString(
+ long propId,
+ String value,
+ String entryId);
+
+ public static native boolean IMAPIProp_DeleteProp(
+ long propId,
+ String entryId);
+
+ /**
+ * Removes a contact from the address book.
+ *
+ * @param id the person id.
+ *
+ * @return whether the contact was successfully removed.
+ */
+ public static native boolean deleteContact(String id);
+
+ /**
+ * Creates an empty contact from the address book.
+ *
+ * @return The id of the new contact created. Or NULL if the ceration
+ * failed.
+ */
+ public static native String createContact();
+
+ /**
+ * Determines whether a specific index in {@link #MAPI_MAILUSER_PROP_IDS}
+ * stands for a property with a phone number value.
+ *
+ * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
+ * property to check
+ * @return <tt>true</tt> if <tt>propIndex</tt> stands for a property with a
+ * phone number value; otherwise, <tt>false</tt>
+ */
+ private static boolean isPhoneNumber(int propIndex)
+ {
+ switch (propIndex)
+ {
+ case PR_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether a specific <tt>MAPI_MAILUSER</tt> property with a
+ * specific <tt>value</tt> matches the {@link #query} of this
+ * <tt>AsyncContactQuery</tt>.
+ *
+ * @param property the <tt>MAPI_MAILUSER</tt> property to check
+ * @param value the value of the <tt>property</tt> to check
+ * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
+ * <tt>property</tt> matches the <tt>query</tt> of this
+ * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
+ */
+ private boolean matches(int property, String value)
+ {
+ return
+ query.matcher(value).find()
+ || (isPhoneNumber(property) && phoneNumberMatches(value));
+ }
+
+ /**
+ * Notifies this <tt>MsOutlookAddrBookContactQuery</tt> about a specific
+ * <tt>MAPI_MAILUSER</tt>.
+ *
+ * @param id The outlook contact identifier.
+ *
+ * @return <tt>true</tt> if this <tt>MsOutlookAddrBookContactQuery</tt>
+ * is to continue being called; otherwise, <tt>false</tt>
+ * @throws MsOutlookMAPIHResultException if anything goes wrong while
+ * getting the properties of the specified <tt>MAPI_MAILUSER</tt>
+ */
+ private boolean onMailUser(String id)
+ throws MsOutlookMAPIHResultException
+ {
+ Object[] props
+ = IMAPIProp_GetProps(
+ id,
+ MAPI_MAILUSER_PROP_IDS,
+ MAPI_UNICODE);
+ long objType
+ = (props[PR_OBJECT_TYPE] instanceof Long)
+ ? ((Long) props[PR_OBJECT_TYPE]).longValue()
+ : 0;
+
+ // If we have results from the Contacts folder(s), don't read from the
+ // Address Book because there may be duplicates.
+ if ((MAPI_MAILUSER == objType) && (mapiMessageCount != 0))
+ return false;
+
+ int propIndex = 0;
+ boolean matches = false;
+
+ for (Object prop : props)
+ {
+ if ((prop instanceof String) && matches(propIndex, (String) prop))
+ {
+ matches = true;
+ break;
+ }
+ propIndex++;
+ }
+ if (matches)
+ {
+ List<ContactDetail> contactDetails = getContactDetails(props);
+
+ // What's the point of showing a contact who has no contact details?
+ if (!contactDetails.isEmpty())
+ {
+ String displayName = getDisplayName(props);
+
+ MsOutlookAddrBookSourceContact sourceContact
+ = new MsOutlookAddrBookSourceContact(
+ getContactSource(),
+ (String) props[PR_ORIGINAL_ENTRYID],
+ displayName,
+ contactDetails);
+
+ if (MAPI_MESSAGE == objType)
+ {
+ ++mapiMessageCount;
+
+ try
+ {
+ Object[] images
+ = IMAPIProp_GetProps(
+ id,
+ new long[] { PR_ATTACHMENT_CONTACTPHOTO },
+ 0);
+ Object image = images[0];
+
+ if (image instanceof byte[])
+ sourceContact.setImage((byte[]) image);
+ }
+ catch (MsOutlookMAPIHResultException ex)
+ {
+ // Ignore it, the image isn't as vital as the
+ // SourceContact.
+ }
+ }
+
+ addQueryResult(sourceContact);
+ }
+ }
+ return (getStatus() == QUERY_IN_PROGRESS);
+ }
+
+ /**
+ * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent an <tt>ABPerson</tt> specified by the values of its
+ * {@link #ABPERSON_PROPERTIES}.
+ *
+ * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
+ * represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of
+ * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
+ */
+ public static List<ContactDetail> getContactDetails(Object[] values)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new ArrayList<Class<? extends OperationSet>>(2);
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ // can be added as contacts
+ supportedOpSets.add(OperationSetPersistentPresence.class);
+
+ List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
+
+ for (int i = 0; i < CONTACT_DETAIL_PROP_INDEXES.length; i++)
+ {
+ int property = CONTACT_DETAIL_PROP_INDEXES[i];
+ Object value = values[property];
+
+ if (value instanceof String)
+ {
+ String stringValue = (String) value;
+
+ if (stringValue.length() != 0)
+ {
+ if(isPhoneNumber(property))
+ stringValue
+ = PhoneNumberI18nService.normalize(stringValue);
+
+ MsOutlookAddrBookContactDetail contactDetail
+ = new MsOutlookAddrBookContactDetail(
+ stringValue,
+ getCategory(property),
+ getSubCategories(property),
+ MAPI_MAILUSER_PROP_IDS[property]);
+
+ // Check if this contact detail support the telephony and
+ // the persistent presence operation set.
+ for(int j = 0;
+ j < CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
+ ++j)
+ {
+ if(property
+ == CONTACT_OPERATION_SET_ABLE_PROP_INDEXES[j])
+ {
+ contactDetail.setSupportedOpSets(supportedOpSets);
+ // Found, then break the loop.
+ j = CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
+ }
+ }
+ contactDetails.add(contactDetail);
+ }
+ }
+ }
+
+ return contactDetails;
+ }
+
+ /**
+ * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
+ *
+ * @see AsyncContactQuery#run()
+ */
+ protected void run()
+ {
+ synchronized (MsOutlookAddrBookContactQuery.class)
+ {
+ foreachMailUser(
+ query.toString(),
+ new PtrOutlookContactCallback());
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for inserted items.
+ *
+ * @param id The outlook contact identifier.
+ */
+ public void inserted(String id)
+ {
+ try
+ {
+ onMailUser(id);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#onMailUser(String)",
+ e);
+ }
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ *
+ * @param id The outlook contact identifier.
+ */
+ public void updated(String id)
+ {
+ SourceContact sourceContact
+ = findSourceContactByID(id);
+ if(sourceContact != null
+ && sourceContact instanceof MsOutlookAddrBookSourceContact)
+ {
+ ((MsOutlookAddrBookSourceContact) sourceContact).updated();
+ fireContactChanged(sourceContact);
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ *
+ * @param id The outlook contact identifier.
+ */
+ public void deleted(String id)
+ {
+ if(id != null)
+ {
+ SourceContact sourceContact = findSourceContactByID(id);
+
+ if(sourceContact != null)
+ {
+ fireContactRemoved(sourceContact);
+ }
+ }
+ }
+
+ /**
+ * Callback to called by the native outlook part with a contact id as
+ * argument.
+ */
+ public class PtrOutlookContactCallback
+ {
+ /**
+ * Notifies this callback about a specific contact.
+ *
+ * @param id The outlook contact identifier.
+ *
+ * @return <tt>true</tt> if this <tt>PtrCallback</tt> is to continue
+ * being called; otherwise, <tt>false</tt>
+ */
+ boolean callback(String id)
+ {
+ try
+ {
+ return onMailUser(id);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#onMailUser(String)",
+ e);
+ }
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null)
+ {
+ final MsOutlookAddrBookSourceContact sourceContact
+ = new MsOutlookAddrBookSourceContact(
+ getContactSource(),
+ id,
+ null,
+ new LinkedList<ContactDetail>());
+ addQueryResult(sourceContact);
+ }
+ }
+
+ /**
+ * Gets the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>.
+ *
+ * @param values the values of the contact properties.
+ *
+ * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>.
+ */
+ static String getDisplayName(Object[] values)
+ {
+ String displayName = (String) values[PR_NICKNAME];
+
+ if ((displayName == null) || (displayName.length() == 0))
+ {
+ String firstName = (String) values[PR_GIVEN_NAME];
+ String lastName = (String) values[PR_SURNAME];
+ if ((lastName == null) || (lastName.length() == 0))
+ lastName = (String) values[PR_MIDDLE_NAME];
+
+ if ((firstName == null) || (firstName.length() == 0))
+ displayName = lastName;
+ else
+ {
+ displayName = firstName;
+ if ((lastName != null) && (lastName.length() != 0))
+ displayName += " " + lastName;
+ }
+ }
+
+ if ((displayName == null) || (displayName.length() == 0))
+ displayName = (String) values[PR_COMPANY_NAME];
+
+ if ((displayName == null) || (displayName.length() == 0))
+ {
+ for(int i = 0; i < values.length; ++i)
+ {
+ if(values[i] instanceof String)
+ {
+ displayName = (String) values[i];
+ if ((displayName != null) && (displayName.length() != 0))
+ return displayName;
+ }
+ }
+ }
+
+ return displayName;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
index cc6abe1..85f6d6f 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
@@ -1,222 +1,265 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.plugin.addrbook.msoutlook;
-
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implements <tt>ContactSourceService</tt> for the Address Book of Microsoft
- * Outlook.
- *
- * @author Lyubomir Marinov
- * @author Vincent Lucas
- */
-public class MsOutlookAddrBookContactSourceService
- extends AsyncContactSourceService
-{
- /**
- * The <tt>Logger</tt> used by the
- * <tt>MsOutlookAddrBookContactSourceService</tt> class and its instances
- * for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(MsOutlookAddrBookContactSourceService.class);
-
- /**
- * The outlook address book prefix.
- */
- public static final String OUTLOOK_ADDR_BOOK_PREFIX
- = "net.java.sip.communicator.plugin.addrbook.OUTLOOK_ADDR_BOOK_PREFIX";
-
- private static final long MAPI_INIT_VERSION = 0;
-
- private static final long MAPI_MULTITHREAD_NOTIFICATIONS = 0x00000001;
-
- /**
- * The latest query created.
- */
- private MsOutlookAddrBookContactQuery latestQuery = null;
-
- static
- {
- try
- {
- System.loadLibrary("jmsoutlookaddrbook");
- }
- catch (Throwable ex)
- {
- logger.error("Unable to load outlook native lib", ex);
- throw new RuntimeException(ex);
- }
-
- /*
- * We have multiple reports of an "UnsatisfiedLinkError: no
- * jmsoutlookaddrbook in java.library.path" at
- * MsOutlookAddrBookContactSourceService#queryContactSource() which
- * seems strange since getting there means that we have already
- * successfully gone through the System.loadLibrary() above. Try to load
- * MsOutlookAddrBookContactQuery here and see how it goes.
- */
- try
- {
- Class.forName(MsOutlookAddrBookContactQuery.class.getName());
- }
- catch (ClassNotFoundException cnfe)
- {
- throw new RuntimeException(cnfe);
- }
- }
-
- /**
- * Initializes a new <tt>MsOutlookAddrBookContactSourceService</tt>
- * instance.
- *
- * @throws MsOutlookMAPIHResultException if anything goes wrong while
- * initializing the new <tt>MsOutlookAddrBookContactSourceService</tt>
- * instance
- */
- public MsOutlookAddrBookContactSourceService()
- throws MsOutlookMAPIHResultException
- {
- MAPIInitialize(
- MAPI_INIT_VERSION,
- MAPI_MULTITHREAD_NOTIFICATIONS,
- new NotificationsDelegate());
- }
-
- /**
- * Gets a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation.
- *
- * @return a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation
- * @see ContactSourceService#getDisplayName()
- */
- public String getDisplayName()
- {
- return "Microsoft Outlook";
- }
-
- /**
- * Gets a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MsOutlookAddrBookContactSourceService</tt> implementation.
- *
- * @return a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MsOutlookAddrBookContactSourceService</tt> implementation
- * @see ContactSourceService#getType()
- */
- public int getType()
- {
- return SEARCH_TYPE;
- }
-
- private static native void MAPIInitialize(
- long version,
- long flags,
- NotificationsDelegate callback)
- throws MsOutlookMAPIHResultException;
-
- private static native void MAPIUninitialize();
-
- /**
- * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
- * which match a specific <tt>query</tt> <tt>Pattern</tt>.
- *
- * @param query the <tt>Pattern</tt> which this
- * <tt>ContactSourceService</tt> is being queried for
- * @return a <tt>ContactQuery</tt> which represents the query of this
- * <tt>ContactSourceService</tt> implementation for the specified
- * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
- * any) will be returned
- * @see ExtendedContactSourceService#queryContactSource(Pattern)
- */
- public ContactQuery queryContactSource(Pattern query)
- {
- if(latestQuery != null)
- latestQuery.clear();
-
- latestQuery = new MsOutlookAddrBookContactQuery(this, query);
-
- latestQuery.start();
- return latestQuery;
- }
-
- /**
- * Stops this <tt>ContactSourceService</tt> implementation and prepares it
- * for garbage collection.
- *
- * @see AsyncContactSourceService#stop()
- */
- public void stop()
- {
- if(latestQuery != null)
- {
- latestQuery.clear();
- latestQuery = null;
- }
- MAPIUninitialize();
- }
-
- /**
- * Returns the global phone number prefix to be used when calling contacts
- * from this contact source.
- *
- * @return the global phone number prefix
- */
- public String getPhoneNumberPrefix()
- {
- return AddrBookActivator.getConfigService()
- .getString(OUTLOOK_ADDR_BOOK_PREFIX);
- }
-
- /**
- * Returns the index of the contact source in the result list.
- *
- * @return the index of the contact source in the result list
- */
- public int getIndex()
- {
- return -1;
- }
-
- /**
- * Delegate class to be notified for addressbook changes.
- */
- public class NotificationsDelegate
- {
- /**
- * Callback method when receiving notifications for inserted items.
- */
- public void inserted(long person)
- {
- if(latestQuery != null)
- latestQuery.inserted(person);
- }
-
- /**
- * Callback method when receiving notifications for updated items.
- */
- public void updated(long person)
- {
- if(latestQuery != null)
- latestQuery.updated(person);
- }
-
- /**
- * Callback method when receiving notifications for deleted items.
- */
- public void deleted(String id)
- {
- if(latestQuery != null)
- latestQuery.deleted(id);
- }
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.addrbook.msoutlook;
+
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactSourceService</tt> for the Address Book of Microsoft
+ * Outlook.
+ *
+ * @author Lyubomir Marinov
+ * @author Vincent Lucas
+ */
+public class MsOutlookAddrBookContactSourceService
+ extends AsyncContactSourceService
+ implements EditableContactSourceService
+{
+ /**
+ * The <tt>Logger</tt> used by the
+ * <tt>MsOutlookAddrBookContactSourceService</tt> class and its instances
+ * for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MsOutlookAddrBookContactSourceService.class);
+
+ /**
+ * The outlook address book prefix.
+ */
+ public static final String OUTLOOK_ADDR_BOOK_PREFIX
+ = "net.java.sip.communicator.plugin.addrbook.OUTLOOK_ADDR_BOOK_PREFIX";
+
+ private static final long MAPI_INIT_VERSION = 0;
+
+ private static final long MAPI_MULTITHREAD_NOTIFICATIONS = 0x00000001;
+
+ /**
+ * The latest query created.
+ */
+ private MsOutlookAddrBookContactQuery latestQuery = null;
+
+ static
+ {
+ try
+ {
+ System.loadLibrary("jmsoutlookaddrbook");
+ }
+ catch (Throwable ex)
+ {
+ logger.error("Unable to load outlook native lib", ex);
+ throw new RuntimeException(ex);
+ }
+
+ /*
+ * We have multiple reports of an "UnsatisfiedLinkError: no
+ * jmsoutlookaddrbook in java.library.path" at
+ * MsOutlookAddrBookContactSourceService#queryContactSource() which
+ * seems strange since getting there means that we have already
+ * successfully gone through the System.loadLibrary() above. Try to load
+ * MsOutlookAddrBookContactQuery here and see how it goes.
+ */
+ try
+ {
+ Class.forName(MsOutlookAddrBookContactQuery.class.getName());
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ throw new RuntimeException(cnfe);
+ }
+ }
+
+ /**
+ * Initializes a new <tt>MsOutlookAddrBookContactSourceService</tt>
+ * instance.
+ *
+ * @throws MsOutlookMAPIHResultException if anything goes wrong while
+ * initializing the new <tt>MsOutlookAddrBookContactSourceService</tt>
+ * instance
+ */
+ public MsOutlookAddrBookContactSourceService()
+ throws MsOutlookMAPIHResultException
+ {
+ MAPIInitialize(
+ MAPI_INIT_VERSION,
+ MAPI_MULTITHREAD_NOTIFICATIONS,
+ new NotificationsDelegate());
+ }
+
+ /**
+ * Gets a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation.
+ *
+ * @return a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation
+ * @see ContactSourceService#getDisplayName()
+ */
+ public String getDisplayName()
+ {
+ return "Microsoft Outlook";
+ }
+
+ /**
+ * Gets a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MsOutlookAddrBookContactSourceService</tt> implementation.
+ *
+ * @return a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MsOutlookAddrBookContactSourceService</tt> implementation
+ * @see ContactSourceService#getType()
+ */
+ public int getType()
+ {
+ return SEARCH_TYPE;
+ }
+
+ private static native void MAPIInitialize(
+ long version,
+ long flags,
+ NotificationsDelegate callback)
+ throws MsOutlookMAPIHResultException;
+
+ private static native void MAPIUninitialize();
+
+ /**
+ * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
+ * which match a specific <tt>query</tt> <tt>Pattern</tt>.
+ *
+ * @param query the <tt>Pattern</tt> which this
+ * <tt>ContactSourceService</tt> is being queried for
+ * @return a <tt>ContactQuery</tt> which represents the query of this
+ * <tt>ContactSourceService</tt> implementation for the specified
+ * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
+ * any) will be returned
+ * @see ExtendedContactSourceService#queryContactSource(Pattern)
+ */
+ public ContactQuery queryContactSource(Pattern query)
+ {
+ if(latestQuery != null)
+ latestQuery.clear();
+
+ latestQuery = new MsOutlookAddrBookContactQuery(this, query);
+
+ latestQuery.start();
+ return latestQuery;
+ }
+
+ /**
+ * Stops this <tt>ContactSourceService</tt> implementation and prepares it
+ * for garbage collection.
+ *
+ * @see AsyncContactSourceService#stop()
+ */
+ public void stop()
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.clear();
+ latestQuery = null;
+ }
+ MAPIUninitialize();
+ }
+
+ /**
+ * Returns the global phone number prefix to be used when calling contacts
+ * from this contact source.
+ *
+ * @return the global phone number prefix
+ */
+ public String getPhoneNumberPrefix()
+ {
+ return AddrBookActivator.getConfigService()
+ .getString(OUTLOOK_ADDR_BOOK_PREFIX);
+ }
+
+ /**
+ * Returns the index of the contact source in the result list.
+ *
+ * @return the index of the contact source in the result list
+ */
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Delegate class to be notified for addressbook changes.
+ */
+ public class NotificationsDelegate
+ {
+ /**
+ * Callback method when receiving notifications for inserted items.
+ */
+ public void inserted(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.inserted(id);
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ */
+ public void updated(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.updated(id);
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ */
+ public void deleted(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.deleted(id);
+ }
+ }
+
+ /**
+ * Creates a new contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @return The ID of the contact to remove. NULL if failed to create a new
+ * contact.
+ */
+ public String createContact()
+ {
+ return MsOutlookAddrBookContactQuery.createContact();
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null && latestQuery != null)
+ {
+ latestQuery.addEmptyContact(id);
+ }
+ }
+
+ /**
+ * Removes the given contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @param id The ID of the contact to remove.
+ */
+ public void deleteContact(String id)
+ {
+ if(id != null && MsOutlookAddrBookContactQuery.deleteContact(id))
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.deleted(id);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
index 6b132f3..4158e31 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
@@ -9,6 +9,7 @@ package net.java.sip.communicator.plugin.addrbook.msoutlook;
import java.util.*;
import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.util.*;
/**
* Implements a custom <tt>SourceContact</tt> for the Address Book of Microsoft
@@ -21,6 +22,21 @@ public class MsOutlookAddrBookSourceContact
implements EditableSourceContact
{
/**
+ * The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookSourceContact</tt>
+ * class and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MsOutlookAddrBookSourceContact.class);
+
+ /**
+ * Boolean used to temporarily lock the access to a single modification
+ * source (jitsi or contact). i.e. it can be useful if Jitsi modifies a
+ * batch of details and do not want to receive contact update notification
+ * which can produce concurrent changes of the details.
+ */
+ private Boolean locked = Boolean.FALSE;
+
+ /**
* Initializes a new MsOutlookAddrBookSourceContact instance.
*
* @param contactSource The ContactSourceService which is creating the new
@@ -58,7 +74,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void setDetails(List<ContactDetail> details)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
contactDetails.clear();
contactDetails.addAll(details);
@@ -71,7 +87,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void save()
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
MsOutlookAddrBookContactDetail outlookContactDetail;
@@ -101,7 +117,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void removeContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
int i = 0;
while(i < this.contactDetails.size())
@@ -145,7 +161,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void addContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
MsOutlookAddrBookContactDetail addDetail;
if(!(detail instanceof MsOutlookAddrBookContactDetail))
@@ -185,6 +201,125 @@ public class MsOutlookAddrBookSourceContact
}
/**
+ * Sets the display name for this contact.
+ *
+ * @param displayName The new display name for this contact.
+ */
+ public void setDisplayName(String displayName)
+ {
+ if(displayName != null && !displayName.equals(this.getDisplayName()))
+ {
+ // Be sure that the new determined display named is saved under all
+ // the requireed properties.
+ long[] displayNamePropIdList =
+ {
+ 0x3001, // PR_DISPLAY_NAME
+ 0x0037, // PR_SUBJECT
+ 0x803F, // Do not know, but set by the MFCMAPI application.
+ 0x0E1D // PR_NORMALIZED_SUBJECT
+ };
+
+ for(int i = 0; i < displayNamePropIdList.length; ++i)
+ {
+ MsOutlookAddrBookContactQuery.IMAPIProp_SetPropString(
+ displayNamePropIdList[i],
+ displayName,
+ this.getId());
+ }
+ }
+
+ super.setDisplayName(displayName);
+ }
+
+ /**
+ * Function called by the native part (msoutlook) when this contact has been
+ * updated.
+ */
+ public void updated()
+ {
+ // Synchronize before the GetProps in order to let other operation (i.e.
+ // save) to write/change all desired values (and not override new saved
+ // values iwth old ones).
+ synchronized(this)
+ {
+ waitUnlock();
+
+ Object[] props = null;
+ try
+ {
+ props = MsOutlookAddrBookContactQuery.IMAPIProp_GetProps(
+ this.getId(),
+ MsOutlookAddrBookContactQuery.MAPI_MAILUSER_PROP_IDS,
+ MsOutlookAddrBookContactQuery.MAPI_UNICODE);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#IMAPIProp_GetProps(long, long[], long)",
+ e);
+ }
+ }
+
+ // let's update the the details
+ List<ContactDetail> contactDetails
+ = MsOutlookAddrBookContactQuery.getContactDetails(props);
+ this.setDetails(contactDetails);
+
+ String displayName
+ = MsOutlookAddrBookContactQuery.getDisplayName(props);
+ this.setDisplayName(displayName);
+ }
+ }
+
+ /**
+ * Locks this object before adding or removing several contact details.
+ */
+ public void lock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Unlocks this object before after or removing several contact details.
+ */
+ public void unlock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.FALSE;
+ notify();
+ }
+ }
+
+ /**
+ * Waits to be unlocked. This object must be synchronized before calling
+ * this function.
+ */
+ private void waitUnlock()
+ {
+ boolean continueToWait = this.locked;
+
+ while(continueToWait)
+ {
+ try
+ {
+ wait();
+ continueToWait = false;
+ }
+ catch(InterruptedException ie)
+ {
+ // Nothing to do, we will wait until the notify.
+ }
+ }
+ }
+
+ /**
* Returns the index of this source contact in its parent.
*
* @return the index of this source contact in its parent