diff options
Diffstat (limited to 'src/net/java/sip/communicator/plugin/addrbook')
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 |