/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package 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.*; /** * Our editable source contact, we store changes in the addressbook. * * @author Lyubomir Marinov */ public class MacOSXAddrBookSourceContact extends GenericSourceContact implements EditableSourceContact { /** * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookSourceContact</tt> * class and its instances for logging output. */ private static final Logger logger = 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 * the new instance * @param displayName the display name of the new instance * @param contactDetails the <tt>ContactDetail</tt>s of the new instance */ public MacOSXAddrBookSourceContact( ContactSourceService contactSource, String displayName, List<ContactDetail> contactDetails) { super(contactSource, displayName, contactDetails); // let's save the parent so we can reuse it later when editing // the detail for(ContactDetail cd : contactDetails) { if(cd instanceof MacOSXAddrBookContactDetail) { ((MacOSXAddrBookContactDetail)cd).setSourceContact(this); } } } /** * Adds a contact detail to the list of contact details. * * @param detail the <tt>ContactDetail</tt> to add */ public void addContactDetail(ContactDetail detail) { synchronized(this) { String id = (String)getData(SourceContact.DATA_ID); if(id == null) { logger.warn("No id or wrong ContactDetail " + detail); return; } String subProperty = null; int property = MacOSXAddrBookContactQuery.getProperty( detail.getCategory(), detail.getSubCategories()); if(MacOSXAddrBookContactDetail.isMultiline(detail.getCategory())) { if(detail instanceof MacOSXAddrBookContactDetail) { subProperty = ((MacOSXAddrBookContactDetail)detail) .getSubPropertyLabel(); } if(subProperty == null) { if(property == MacOSXAddrBookContactQuery .kABAddressProperty) { if(detail.containsSubCategory(SubCategory.Home)) subProperty = MacOSXAddrBookContactQuery .kABAddressHomeLabel(); else subProperty = MacOSXAddrBookContactQuery .kABAddressWorkLabel(); } else if(property == MacOSXAddrBookContactQuery .kABAIMInstantProperty || property == MacOSXAddrBookContactQuery .kABICQInstantProperty || property == MacOSXAddrBookContactQuery .kABJabberInstantProperty || property == MacOSXAddrBookContactQuery .kABMSNInstantProperty || property == MacOSXAddrBookContactQuery .kABYahooInstantProperty) { subProperty = MacOSXAddrBookContactQuery .kABAddressWorkLabel(); } } List<String> values = getValues(detail, property, subProperty, true); MacOSXAddrBookContactQuery.setProperty( id, MacOSXAddrBookContactQuery.ABPERSON_PROPERTIES[ property], subProperty, values.toArray(new Object[values.size()])); } else { MacOSXAddrBookContactQuery.setProperty( id, MacOSXAddrBookContactQuery.ABPERSON_PROPERTIES[ property], null, detail.getDetail()); } // make sure we add AddressBookContactDetail Collection<SubCategory> subCategories = detail.getSubCategories(); MacOSXAddrBookContactDetail contactDetail = new MacOSXAddrBookContactDetail( property, detail.getDetail(), detail.getCategory(), subCategories.toArray( new SubCategory[ subCategories.size()]), subProperty, id); contactDetail.setSourceContact(this); // Add the detail at the right index : group multiline properties // together , such as home/work address fields. boolean added = false; int index = 0; for(ContactDetail cd: contactDetails) { if(cd instanceof MacOSXAddrBookContactDetail) { MacOSXAddrBookContactDetail macOSXcd = (MacOSXAddrBookContactDetail) cd; if(!added && contactDetail.getProperty() == macOSXcd.getProperty() && (contactDetail.getSubPropertyLabel() == null || contactDetail.getSubPropertyLabel().equals( macOSXcd.getSubPropertyLabel()))) { added = true; } } if(!added) ++index; } contactDetails.add(index, contactDetail); } } /** * Returns the list of values that will be saved. * @param detail the current modified detail * @param property the property we change * @param subProperty the subproperty that is changed * @param addDetail should we add <tt>detail</tt> to the list of values. * @return the list of values to be saved. */ private List<String> getValues(ContactDetail detail, int property, String subProperty, boolean addDetail) { // first add existing one List<String> values = new ArrayList<String>(); List<ContactDetail> details = getContactDetails(detail.getCategory()); boolean isIM = (property == MacOSXAddrBookContactQuery.kABICQInstantProperty || property == MacOSXAddrBookContactQuery.kABAIMInstantProperty || property == MacOSXAddrBookContactQuery.kABYahooInstantProperty || property == MacOSXAddrBookContactQuery.kABMSNInstantProperty || property == MacOSXAddrBookContactQuery.kABJabberInstantProperty ); boolean isAddress = property == MacOSXAddrBookContactQuery.kABAddressProperty; boolean isHomeAddress = detail.containsSubCategory(SubCategory.Home); int lastHomeIndex = 0; int lastWorkIndex = 0; for(ContactDetail cd : details) { // if the detail exists do not added, in case of add there is // sense the detail to be added twice. In case of remove // we miss the detail if(cd.equals(detail)) continue; String det = cd.getDetail(); for(SubCategory sub : cd.getSubCategories()) { // if its an im property check also if the detail // is the same subcategory (which is icq, yahoo, ...) if(isIM && !detail.getSubCategories().contains(sub)) continue; String label = MacOSXAddrBookContactQuery. getLabel(property, sub, subProperty); if(label != null) { values.add(det); values.add(label); // For an address adds a third item for the tuple: // value, label, sub-property label. if(isAddress && cd instanceof MacOSXAddrBookContactDetail) { String subPropertyLabel = ((MacOSXAddrBookContactDetail) cd) .getSubPropertyLabel(); values.add(subPropertyLabel); if(subPropertyLabel.equals( MacOSXAddrBookContactQuery .kABAddressHomeLabel())) { lastHomeIndex = values.size(); } else if(subPropertyLabel.equals( MacOSXAddrBookContactQuery .kABAddressWorkLabel())) { lastWorkIndex = values.size(); } } } } } if(addDetail) { // now the new value to add for(SubCategory sub : detail.getSubCategories()) { String label = MacOSXAddrBookContactQuery. getLabel(property, sub, subProperty); if(label != null) { // For an address adds a third item for the tuple: // value, label, sub-property label. if(isAddress) { String subPropertyLabel = ""; int index = values.size(); if(isHomeAddress) { subPropertyLabel = MacOSXAddrBookContactQuery .kABAddressHomeLabel(); index = lastHomeIndex; if(lastWorkIndex > lastHomeIndex) { lastWorkIndex += 3; } lastHomeIndex += 3; } else { subPropertyLabel = MacOSXAddrBookContactQuery .kABAddressWorkLabel(); index = lastWorkIndex; if(lastHomeIndex > lastWorkIndex) { lastHomeIndex += 3; } lastWorkIndex += 3; } values.add(index, detail.getDetail()); values.add(index + 1, label); values.add(index + 2, subPropertyLabel); } else { values.add(detail.getDetail()); values.add(label); } } else logger.warn("Missing label fo prop:" + property + " and sub:" + sub); } } return values; } /** * Removes the given <tt>ContactDetail</tt> from the list of details for * this <tt>SourceContact</tt>. * * @param detail the <tt>ContactDetail</tt> to remove */ public void removeContactDetail(ContactDetail detail) { synchronized(this) { //remove the detail from the addressbook String id = (String)getData(SourceContact.DATA_ID); if(id != null && detail instanceof MacOSXAddrBookContactDetail) { if(MacOSXAddrBookContactDetail.isMultiline( detail.getCategory())) { String subProperty = null; if(detail instanceof MacOSXAddrBookContactDetail) { subProperty = ((MacOSXAddrBookContactDetail)detail) .getSubPropertyLabel(); } List<String> values = getValues( detail, ((MacOSXAddrBookContactDetail)detail) .getProperty(), subProperty, false); MacOSXAddrBookContactQuery.setProperty( id, MacOSXAddrBookContactQuery.ABPERSON_PROPERTIES[ ((MacOSXAddrBookContactDetail) detail) .getProperty()], subProperty, values.toArray(new Object[values.size()])); } else MacOSXAddrBookContactQuery.removeProperty( id, MacOSXAddrBookContactQuery.ABPERSON_PROPERTIES[ ((MacOSXAddrBookContactDetail) detail) .getProperty()]); } else logger.warn("No id or wrong ContactDetail " + detail); contactDetails.remove(detail); } } /** * Changes the details list with the supplied one. * @param details the details. */ public void setDetails(List<ContactDetail> details) { synchronized(this) { contactDetails.clear(); contactDetails.addAll(details); } } /** * 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); } } } } /** * 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(); // Once we have set all the details, then notify the UI that the // contact has been updated. this.updated(); } } /** * 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 */ @Override public int getIndex() { return -1; } }