diff --git a/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java b/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java index f2c2a9211..a0f1db139 100644 --- a/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java +++ b/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java @@ -1,42 +1,53 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ package net.java.sip.communicator.impl.contactlist; import java.util.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; /** - * A Default implementation of a MetaContactGroup. Note that this implementation - * is only meant to be used for non-root contact groups and can only contain - * contacts. All subgroup retrieving methods would returns null/0 values. - * Root contact groups are to be represented by the RootMetaContactGroupImpl. - *
+ * A straightforward implementation of the meta contact group. + * * @author Emil Ivov */ public class MetaContactGroupImpl implements MetaContactGroup { + private static final Logger logger = + Logger.getLogger(MetaContactGroupImpl.class); /** - * All child contacts for this group. + * All the subgroups that this group contains. */ - private Vector childContacts = new Vector(); + private Vector subgroups = new Vector(); /** - * An empty list that we'll be using in order to return an empty iterator - * of the (non-existing) sub groups. + * A list containing all child contacts. */ - private final List dummySubgroupsList = new LinkedList(); + private Vector childContacts = new Vector(); /** - * All protocol specific contact groups encapsulated by this - * MetaContactGoup. + * A list of the contact groups encapsulated by this MetaContactGroup */ - private Hashtable protoGroups = new Hashtable(); - + private Vector protoGroups = new Vector(); + /** + * The name of the group (fixed for root groups since it won't show). + */ private String groupName = null; - protected MetaContactGroupImpl(String groupName) + /** + * Creates an instance of the root meta contact group. + * + * @param groupName the name of the group to create + */ + MetaContactGroupImpl(String groupName) { this.groupName = groupName; } @@ -44,8 +55,8 @@ protected MetaContactGroupImpl(String groupName) /** * Determines whether or not this group can contain subgroups. * - * @return Always false since only the root contact group may contain sub - * groups in our implementation. + * @return always true since this is the root contact group + * and in our imple it can only contain groups. */ public boolean canContainSubgroups() { @@ -53,22 +64,31 @@ public boolean canContainSubgroups() } /** - * Returns the number of MetaContacts that this group contains + * Returns the number of MetaContacts that this group contains. *
- * @return an int indicating the number of MetaContact-s that this group - * contains. + * @return the number of MetaContacts that this group contains. */ public int countChildContacts() { return childContacts.size(); } + /** + * Returns the number of subgroups that this MetaContactGroup + * contains. + * + * @return an int indicating the number of subgroups in this group. + */ + public int countSubgroups() + { + return subgroups.size(); + } + /** * Returns a java.util.Iterator over the MetaContacts * contained in this MetaContactGroup. * - * @return a java.util.Iterator over the MetaContacts - * in this group. + * @return a java.util.Iterator over an empty contacts list. */ public Iterator getChildContacts() { @@ -96,6 +116,191 @@ public MetaContact getMetaContact(String metaContactID) return null; } + /** + * Returns a meta contact, a child of this group or its subgroups, that + * has the specified metaUID. If no such meta contact exists, the method + * would return null. + * + * @param metaUID the Meta UID of the contact we're looking for. + * @return the MetaContact with the specified UID or null if no such + * contact exists. + */ + public MetaContactImpl findMetaContactByMetaUID(String metaUID) + { + //first go through the contacts that are direct children of this method. + Iterator contactsIter = getChildContacts(); + + while(contactsIter.hasNext()) + { + MetaContactImpl mContact = (MetaContactImpl)contactsIter.next(); + + if( mContact.getMetaUID().equals(metaUID) ) + return mContact; + } + + //if we didn't find it here, let's try in the subougroups + Iterator groupsIter = getSubgroups(); + + while( groupsIter.hasNext() ) + { + MetaContactGroupImpl mGroup = (MetaContactGroupImpl)groupsIter.next(); + + MetaContactImpl mContact = mGroup.findMetaContactByMetaUID(metaUID); + + if (mContact != null) + return mContact; + } + + return null; + } + + /** + * Returns an iterator over all the protocol specific groups that this + * contact group represents. + * @return an Iterator over the protocol specific groups that this group + * represents. + */ + public Iterator getContactGroups() + { + return this.protoGroups.iterator(); + } + + /** + * Returns a contact group encapsulated by this meta contact group, having + * the specified groupName and coming from the indicated ownerProvider. + * + * @param groupName the name of the contact group who we're looking for. + * @param ownerProvider a reference to the ProtocolProviderService that + * the contact we're looking for belongs to. + * @return a reference to a ContactGroup, encapsulated by this + * MetaContactGroup, carrying the specified name and originating from the + * specified ownerProvider or null if no such contact group was found. + */ + public ContactGroup getContactGroup(String groupName, + ProtocolProviderService ownerProvider) + { + Iterator encapsulatedGroups = getContactGroups(); + + while (encapsulatedGroups.hasNext()) + { + ContactGroup group = (ContactGroup)encapsulatedGroups.next(); + + if (group.getGroupName().equals(groupName) + && group.getProtocolProvider() == ownerProvider) + { + return group; + } + } + return null; + } + + /** + * Returns all protocol specific ContactGroups, encapsulated by this + * MetaContactGroup and coming from the indicated ProtocolProviderService. + * If none of the contacts encapsulated by this MetaContact is originating + * from the specified provider then an empty iterator is returned. + *
+ * @param provider a reference to the ProtocolProviderService + * whose ContactGroups we'd like to get. + * @return an Iterator over all contacts encapsulated in this + * MetaContact and originating from the specified provider. + */ + public Iterator getContactGroupsForProvider( + ProtocolProviderService provider) + { + Iterator encapsulatedGroups = getContactGroups(); + LinkedList protoGroups = new LinkedList(); + + while(encapsulatedGroups.hasNext()) + { + ContactGroup group = (ContactGroup)encapsulatedGroups.next(); + + if(group.getProtocolProvider() == provider) + protoGroups.add(group); + } + return protoGroups.iterator(); + } + + /** + * Returns a meta contact, a child of this group or its subgroups, that + * has the specified protocol specific contact. If no such meta contact + * exists, the method would return null. + * + * @param protoContact the protocol specific contact whos meta contact we're + * looking for. + * @return the MetaContactImpl that contains the specified protocol specific + * contact. + */ + public MetaContactImpl findMetaContactByContact(Contact protoContact) + { + //first go through the contacts that are direct children of this method. + Iterator contactsIter = getChildContacts(); + + while(contactsIter.hasNext()) + { + MetaContactImpl mContact = (MetaContactImpl)contactsIter.next(); + + Contact storedProtoContact = mContact.getContact( + protoContact.getAddress(), protoContact.getProtocolProvider()); + + if( storedProtoContact != null) + return mContact; + } + + //if we didn't find it here, let's try in the subougroups + Iterator groupsIter = getSubgroups(); + + while( groupsIter.hasNext() ) + { + MetaContactGroupImpl mGroup = (MetaContactGroupImpl)groupsIter.next(); + + MetaContactImpl mContact = mGroup.findMetaContactByContact( + protoContact); + + if (mContact != null) + return mContact; + } + + return null; + } + + /** + * Returns a meta contact group, encapsulated by this group or its + * subgroups, that has the specified protocol specific contact. If no such + * meta contact group exists, the method would return null. + * + * @param protoContactGroup the protocol specific contact group whose meta + * contact group we're looking for. + * @return the MetaContactImpl that contains the specified protocol specific + * contact. + */ + public MetaContactGroupImpl findMetaContactGroupByContactGroup( + ContactGroup protoContactGroup) + { + //first check here, in this meta group + if(protoGroups.contains(protoContactGroup)) + return this; + + + //if we didn't find it here, let's try in the subougroups + Iterator groupsIter = getSubgroups(); + + while( groupsIter.hasNext() ) + { + MetaContactGroupImpl mGroup = (MetaContactGroupImpl)groupsIter.next(); + + MetaContactGroupImpl foundMetaContactGroup = mGroup + .findMetaContactGroupByContactGroup( protoContactGroup ); + + if (foundMetaContactGroup != null) + return foundMetaContactGroup; + } + + return null; + } + + + /** * Returns the meta contact on the specified index. * @@ -111,76 +316,79 @@ public MetaContact getMetaContact(int index) throws } /** - * Returns the MetaContactGroup with the specified index. - * - * @param index the index of the group to return. - * @return always null since only the root contact group may contain sub - * gorups in our implementation. - * @throws IndexOutOfBoundsException if index is not a valid - * index. + * Adds the specified metaContact to ths local list of child + * contacts. + * @param metaContact the MetaContact to add in the local vector. */ - public MetaContactGroup getMetaContactSubgroup(int index) throws - IndexOutOfBoundsException + void addMetaContact(MetaContact metaContact) { - return null; + this.childContacts.add(metaContact); } /** - * Returns the name of this group. - * @return a String containing the name of this group. + * Removes the specified metaContact from the local list of + * contacts. + * @param metaContact the MetaContact */ - public String getGroupName() + void removeMetaContact(MetaContact metaContact) { - return groupName; + this.childContacts.remove( metaContact ); } /** - * Returns the MetaContactGroup with the specified name. - * - * @param groupName the name of the group to return. - * @return always null since only the root contact group may contain - * subgroups in our implementation. + * Returns the MetaContactGroup with the specified index. + *
+ * @param index the index of the group to return. + * @return the MetaContactGroup with the specified index.
+ * @throws IndexOutOfBoundsException if index is not a valid + * index. */ - public MetaContactGroup getMetaContactSubgroup(String groupName) + public MetaContactGroup getMetaContactSubgroup(int index) throws + IndexOutOfBoundsException { - return null; + return (MetaContactGroup)subgroups.get(index); } /** - * Returns the number of subgroups that this MetaContactGroup - * contains. + * Returns the MetaContactGroup with the specified name. * - * @return always 0 since only the root contact group may contain subgroups - * in our implementation. + * @param groupName the name of the group to return. + * @return the MetaContactGroup with the specified name or null + * if no such group exists. */ - public int countSubgroups() + public MetaContactGroup getMetaContactSubgroup(String groupName) { - return 0; + Iterator groupsIter = getSubgroups(); + + while(groupsIter.hasNext()) + { + MetaContactGroup mcGroup = (MetaContactGroup)groupsIter.next(); + + if(mcGroup.getGroupName().equals(groupName)) + return mcGroup; + } + + return null; } /** * Returns an java.util.Iterator over the sub groups that this * MetaContactGroup contains. - * - * @return an Iterator over the empty subgroups list. + *
+ * @return a java.util.Iterator containing all subgroups. */ public Iterator getSubgroups() { - return dummySubgroupsList.iterator(); + return subgroups.iterator(); } /** - * Adds the specified group to the list of protocol specific - * groups merged by this MetaContactGroup. - * @param owner the ProtocolProviderService where the specified group came - * from. - * @param group the ContactGroup to add merge into this - * MetaContactGroup. + * Returns the name of this group. + * @return a String containing the name of this group. */ - public void addProtoGroup(ProtocolProviderService owner, - ContactGroup group) + public String getGroupName() { - this.protoGroups.put(owner, group); + return groupName; } /** @@ -188,12 +396,22 @@ public void addProtoGroup(ProtocolProviderService owner, * contains (may turn out to be a relatively long string). * @return a String representing this group and its child contacts. */ - public String toString() - { - StringBuffer buff = new StringBuffer("MetaContactGroup."); + public String toString() + { + + StringBuffer buff = new StringBuffer(getGroupName()); + buff.append(".subGroups=" + countSubgroups() + ":\n"); + + Iterator subGroups = getSubgroups(); + while (subGroups.hasNext()) + { + MetaContactGroupImpl group = (MetaContactGroupImpl)subGroups.next(); + buff.append(group.toString()); + if (subGroups.hasNext()) + buff.append("\n"); + } - buff.append(getGroupName()); - buff.append(", childContacts="+countChildContacts()+":["); + buff.append("\nRootChildContacts="+countChildContacts()+":["); Iterator contacts = getChildContacts(); while (contacts.hasNext()) @@ -207,38 +425,57 @@ public String toString() } /** - * Verifies whether a protocol specific ContactGroup with the specified - * name and originating from the specified provider is encapsulated by this - * MetaContactGroup and if so returns it. - *
- *
- * @return the ContactGroup with the specified name and provider - * or null if no such group exists. + * Addes the specified group to the list of protocol specific groups + * that we're encapsulating in this meta contact group. + * @param protoGroup the root to add to the groups merged in this meta contact + * group. */ -// ContactGroup findContactGroup(String groupName, ) -// { -// /** @todo implement findContactGroup() */ -// } + void addProtoGroup( ContactGroup protoGroup) + { + protoGroups.add(protoGroup); + } /** - * Adds the specified metaContact to ths local list of child - * contacts. - * @param metaContact the MetaContact to add in the local vector. + * Removes the specified group from the list of protocol specific groups + * that we're encapsulating in this meta contact group. + * @param protoGroup the group to remove from the groups merged in this meta + * contact group. */ - void addMetaContact(MetaContact metaContact) + void removeProtoGroup( ContactGroup protoGroup) { - this.childContacts.add(metaContact); + protoGroups.remove(protoGroup); } /** - * Removes the specified metaContact from the local list of - * contacts. - * @param metaContact the MetaContact + * Adds the specified meta group to the subgroups of this one. + * @param subgroup the MetaContactGroup to register as a subgroup to this + * root meta contact group. */ - void removeMetaContact(MetaContact metaContact) + void addSubgroup(MetaContactGroup subgroup) { - this.childContacts.remove( metaContact ); + logger.trace("Adding subgroup " + subgroup.getGroupName() + + " to" + getGroupName()); + this.subgroups.add(subgroup); } + /** + * Removes the meta contact group with the specified index. + * @param index the index of the group to remove. + * @return the MetaContactGroup that has just been removed. + */ + MetaContactGroupImpl removeSubgroup(int index) + { + return (MetaContactGroupImpl)subgroups.remove(index); + } + /** + * Removes the specified group from the list of groups in this list. + * @param group the MetaContactGroup to remove. + * @return true if the group has been successfully removed and false + * otherwise. + */ + boolean removeSubgroup(MetaContactGroup group) + { + return subgroups.remove(group); + } } diff --git a/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java b/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java index 80f22493c..47ad62035 100644 --- a/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java +++ b/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java @@ -1,3 +1,9 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ package net.java.sip.communicator.impl.contactlist; import java.util.*; @@ -57,19 +63,48 @@ public int getContactCount() * @return a Contact encapsulated in this MetaContact * and originating from the specified provider. */ - public Contact getContactForProvider(ProtocolProviderService provider) + public Iterator getContactsForProvider(ProtocolProviderService provider) { Iterator contactsIter = protoContacts.iterator(); + LinkedList providerContacts = new LinkedList(); while (contactsIter.hasNext()) { Contact contact = (Contact)contactsIter.next(); if(contact.getProtocolProvider() == provider) + providerContacts.add( contact ); + } + + return providerContacts.iterator(); + } + + /** + * Returns a contact encapsulated by this meta contact, having the specified + * contactAddress and coming from the indicated ownerProvider. + * @param contactAddress the address of the contact who we're looking for. + * @param ownerProvider a reference to the ProtocolProviderService that + * the contact we're looking for belongs to. + * @return a reference to a Contact, encapsulated by this + * MetaContact, carrying the specified address and originating from the + * specified ownerProvider or null if no such contact exists.. + */ + public Contact getContact(String contactAddress, + ProtocolProviderService ownerProvider) + { + Iterator contactsIter = protoContacts.iterator(); + + while (contactsIter.hasNext()) + { + Contact contact = (Contact)contactsIter.next(); + + if( contact.getProtocolProvider() == ownerProvider + && contact.getAddress().equals(contactAddress)) return contact; } return null; + } /** @@ -171,4 +206,33 @@ boolean removeProtoContact(Contact contact) } + /** + * Removes all proto contacts that belong to the specified provider. + * + * @param contact the contact to remove + * + * @return true if this MetaContact was modified and false + * otherwise. + */ + boolean removeContactsForProvider(ProtocolProviderService provider) + { + boolean modified = false; + Iterator contactsIter = protoContacts.iterator(); + + while(contactsIter.hasNext()) + { + Contact contact = (Contact)contactsIter.next(); + + if (contact.getProtocolProvider() == provider) + { + contactsIter.remove(); + modified = true; + } + } + + return modified; + } + + + } diff --git a/src/net/java/sip/communicator/impl/contactlist/MetaContactListServiceImpl.java b/src/net/java/sip/communicator/impl/contactlist/MetaContactListServiceImpl.java index ae204f195..99e7fdc32 100644 --- a/src/net/java/sip/communicator/impl/contactlist/MetaContactListServiceImpl.java +++ b/src/net/java/sip/communicator/impl/contactlist/MetaContactListServiceImpl.java @@ -6,47 +6,21 @@ */ package net.java.sip.communicator.impl.contactlist; -import java.util.Iterator; -import java.util.Vector; - -import net.java.sip.communicator.service.contactlist.MetaContact; -import net.java.sip.communicator.service.contactlist.MetaContactGroup; -import net.java.sip.communicator.service.contactlist.MetaContactListException; -import net.java.sip.communicator.service.contactlist.MetaContactListService; -import net.java.sip.communicator.service.contactlist.event.MetaContactEvent; -import net.java.sip.communicator.service.contactlist.event.MetaContactGroupEvent; -import net.java.sip.communicator.service.contactlist.event.MetaContactListListener; -import net.java.sip.communicator.service.protocol.Contact; -import net.java.sip.communicator.service.protocol.ContactGroup; -import net.java.sip.communicator.service.protocol.OperationSetPersistentPresence; -import net.java.sip.communicator.service.protocol.ProtocolProviderService; -import net.java.sip.communicator.service.protocol.event.ServerStoredGroupEvent; -import net.java.sip.communicator.service.protocol.event.ServerStoredGroupListener; -import net.java.sip.communicator.service.protocol.event.SubscriptionEvent; -import net.java.sip.communicator.service.protocol.event.SubscriptionListener; -import net.java.sip.communicator.util.Logger; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceEvent; -import org.osgi.framework.ServiceListener; -import org.osgi.framework.ServiceReference; +import java.util.*; + +import org.osgi.framework.*; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.contactlist.event.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; /** - * An almost dummy implementation of the MetaContactListService that would only - * connect to protocol service providers and build its contact list accordingly - * only basing itself on the contact list stored by the icq service. - *
- * In its current form, the purpose of this implementation is to provide a tool - * for retrieving any contact list so that other modules such as the user - * interface may use it. + * An implementation of the MetaContactListService that would connect to + * protocol service providers and build its contact list accordingly + * basing itself on the contact list stored by the various protocol provider + * services and the contact list instance saved on the hard disk. *
- * Because of its experimental-patch nature, the implementa would only function - * properly if the underlying service providers have already been loaded at the - * time this one gets started. - * - * @todo might be a good idea to say that the implementation does not support - * subgroups. * * @author Emil Ivov */ @@ -57,12 +31,6 @@ public class MetaContactListServiceImpl private static final Logger logger = Logger .getLogger(MetaContactListServiceImpl.class); - /** - * Listeners interested in events dispatched upond modification of the meta - * contact list. - */ - private Vector contactListListeners = new Vector(); - /** * The BundleContext that we got from the OSGI bus. */ @@ -76,8 +44,13 @@ public class MetaContactListServiceImpl /** * The root of the meta contact list. */ - RootMetaContactGroupImpl rootMetaGroup = new RootMetaContactGroupImpl(); + MetaContactGroupImpl rootMetaGroup + = new MetaContactGroupImpl("RootMetaContactGroup"); + /** + * Listeners interested in events dispatched upond modification of the meta + * contact list. + */ private Vector metaContactListListeners = new Vector(); /** @@ -107,12 +80,11 @@ public MetaContactListServiceImpl() { * them. *
* - * @param bc - * the currently valid osgi bundle context. + * @param bc the currently valid osgi bundle context. */ - public void start(BundleContext bc) { - logger - .debug("Starting the meta contact list implementation."); + public void start(BundleContext bc) + { + logger.debug("Starting the meta contact list implementation."); this.bundleContext = bc; // start listening for newly register or removed protocol providers @@ -152,12 +124,12 @@ public void start(BundleContext bc) { * Adds a listener for MetaContactListChangeEvents posted after * the tree changes. * - * @param l - * the listener to add + * @param l the listener to add */ - public void addContactListListener( - MetaContactListListener l) { - synchronized (metaContactListListeners) { + public void addContactListListener(MetaContactListListener l) + { + synchronized (metaContactListListeners) + { this.metaContactListListeners.add(l); } } @@ -339,10 +311,70 @@ public void removeMetaContact(MetaContact metaContact) */ public void removeMetaContactGroup( MetaContactGroup groupToRemove) - throws MetaContactListException { - /** @todo implement removeMetaContactGroup() */ - System.out - .println("@todo implement removeMetaContactGroup()"); + throws MetaContactListException + { + /** @todo implement removeMetaContactGroup() */ + + } + + /** + * Removes the protocol specific group from the specified meta contact group + * and removes from meta contacts all proto contacts that belong to the + * same provider as the group which is being removed. + * @param metaContainer the MetaContactGroup that we'd like to remove a + * contact group from. + * @param groupToRemove the ContactGroup that we'd like removed. + * @param sourceProvider the ProtocolProvider that the contact group belongs + * to. + */ + public void removeContactGroupFromMetaContactGroup( + MetaContactGroupImpl metaContainer, + ContactGroup groupToRemove, + ProtocolProviderService sourceProvider) + { + metaContainer.removeProtoGroup(groupToRemove); + + //go through all meta contacts and remove all contats that belong to the + //same provider and are therefore children of the group that is being + //removed + removeAllContactsForProvider(metaContainer, sourceProvider); + + fireMetaContactGroupEvent( metaContainer, sourceProvider, + MetaContactGroupEvent.CONTACT_GROUP_REMOVED_FROM_META_GROUP); + + } + + /** + * Goes through the specified group and removes from all meta contacts, + * protocol specific contacts belonging to the specified provider + * + * @param parent the MetaContactGroup whose children we should go through + * @param sourceProvider the ProtocolProviderService whose contacts we'd + * like deleted from parent. + */ + private void removeAllContactsForProvider( + MetaContactGroupImpl parent, ProtocolProviderService sourceProvider) + { + Iterator childrenContactsIter = parent.getChildContacts(); + + //first go through all direct children. + while(childrenContactsIter.hasNext()) + { + MetaContactImpl child = (MetaContactImpl)childrenContactsIter.next(); + + child.removeContactsForProvider(sourceProvider); + + //if this was the last proto contact inside this meta contact, + //then remove the meta contact as well. + int eventID = MetaContactEvent.PROTO_CONTACT_REMOVED; + if (child.getContactCount() == 0){ + parent.removeMetaContact(child); + eventID = MetaContactEvent.META_CONTACT_REMOVED; + } + + fireMetaContactEvent(child, sourceProvider, parent, eventID); + } + } /** @@ -355,9 +387,9 @@ public void removeMetaContactGroup( * for. */ public MetaContactGroup findMetaContactGroupByContactGroup - (ContactGroup contactGroup) { - /** @todo implement findMetaContactByContact() */ - return null; + (ContactGroup contactGroup) + { + return rootMetaGroup.findMetaContactGroupByContactGroup(contactGroup); } /** @@ -369,13 +401,11 @@ public void removeMetaContactGroup( * * @return the MetaContact containing the speicified contact or null if no * such contact is present in this contact list. - * @param contact - * the protocol specific contact that we're looking - * for. + * @param contact the protocol specific contact that we're looking + * for. */ public MetaContact findMetaContactByContact(Contact contact) { - /** @todo implement findMetaContactByContact() */ - return null; + return rootMetaGroup.findMetaContactByContact(contact); } /** @@ -386,9 +416,9 @@ public MetaContact findMetaContactByContact(Contact contact) { * @return the MetaContact with the speicified string identifier or null if * no such meta contact was found. */ - public MetaContact findMetaContactByID(String metaContactID) { - /** @todo implement findMetaContactByID() */ - return null; + public MetaContact findMetaContactByMetaUID(String metaContactID) { + + return rootMetaGroup.findMetaContactByMetaUID(metaContactID); } /** @@ -399,29 +429,49 @@ public MetaContact findMetaContactByID(String metaContactID) { * @param presenceOpSet * the presence operation set whose contact list we'd like to * synchronize with the local contact list. - * @param provider - * the provider that the operation set belongs to. */ private void synchronizeOpSetWithLocalContactList( - ProtocolProviderService provider, OperationSetPersistentPresence presenceOpSet) { ContactGroup rootProtoGroup = presenceOpSet .getServerStoredContactListRoot(); logger.trace("subgroups: " - + rootProtoGroup.countSubGroups()); + + rootProtoGroup.countSubgroups()); logger.trace("child contacts: " + rootProtoGroup.countContacts()); + addContactGroupToMetaGroup(rootProtoGroup, rootMetaGroup, true); + + presenceOpSet + .addSubsciptionListener(new ContactListSubscriptionListener()); + + presenceOpSet + .addServerStoredGroupChangeListener(new ContactListGroupListener()); + } + + /** + * Creates meta contacts and meta contact groups for all childredn of the + * specified contactGroup and adds them to metaGroup + * @param protoGroup the ContactGroup to add. + *
+ * @param metaGroup the MetaContactGroup where ContactGroup + * should be added. + * @param fireEvents indicates whether or not events are to be fired upon + * adding subcontacts and subgroups. When this method is called recursively, + * the parameter should will be false in order to generate a minimal number + * of events for the whole addition and not an event per every subgroup + * and child contact. + */ + private void addContactGroupToMetaGroup( ContactGroup protoGroup, + MetaContactGroupImpl metaGroup, + boolean fireEvents) + { // first register the root group - rootMetaGroup.addProtoGroup(provider, presenceOpSet - .getServerStoredContactListRoot()); + metaGroup.addProtoGroup(protoGroup); // register subgroups and contacts - // this implementation only supports one level of groups apart from - // the root group - so let's keep this simple and do it in a nested loop - Iterator subgroupsIter = rootProtoGroup.subGroups(); + Iterator subgroupsIter = protoGroup.subGroups(); while (subgroupsIter.hasNext()) { ContactGroup group = (ContactGroup) subgroupsIter @@ -433,53 +483,34 @@ private void synchronizeOpSetWithLocalContactList( MetaContactGroupImpl newMetaGroup = new MetaContactGroupImpl( group.getGroupName()); - newMetaGroup.addProtoGroup(provider, group); + metaGroup.addSubgroup(newMetaGroup); - Iterator contactsIter = group.contacts(); - while (contactsIter.hasNext()) { - Contact contact = (Contact) contactsIter - .next(); - MetaContactImpl newMetaContact = new MetaContactImpl(); + addContactGroupToMetaGroup(group, newMetaGroup, false); - newMetaContact.addProtoContact(contact); - - /** @todo this shouldn't be that simple */ - newMetaContact.setDisplayName(contact - .getDisplayName()); - - newMetaGroup.addMetaContact(newMetaContact); - } - - rootMetaGroup.addSubgroup(newMetaGroup); - - this.fireMetaContactGroupEvent(newMetaGroup, - provider, - MetaContactEvent.METACONTACT_ADDED); + if(fireEvents) + this.fireMetaContactGroupEvent(newMetaGroup, + group.getProtocolProvider(), + MetaContactGroupEvent.META_CONTACT_GROUP_ADDED); } - // now add all contacts, located in the root group - Iterator contactsIter = rootProtoGroup.contacts(); + // now add all contacts, located in this group + Iterator contactsIter = protoGroup.contacts(); while (contactsIter.hasNext()) { Contact contact = (Contact) contactsIter.next(); MetaContactImpl newMetaContact = new MetaContactImpl(); newMetaContact.addProtoContact(contact); - /** @todo this shouldn't be that simple */ newMetaContact.setDisplayName(contact.getDisplayName()); - rootMetaGroup.addMetaContact(newMetaContact); + metaGroup.addMetaContact(newMetaContact); - this.fireMetaContactEvent(newMetaContact, - provider, rootMetaGroup, - MetaContactEvent.METACONTACT_ADDED); - } - - presenceOpSet - .addSubsciptionListener(new ContactListSubscriptionListener()); + if( fireEvents ) + this.fireMetaContactEvent(newMetaContact, + protoGroup.getProtocolProvider(), metaGroup, + MetaContactEvent.META_CONTACT_ADDED); - presenceOpSet - .addServerStoredGroupChangeListener(new ContactListGroupListener()); + } } /** @@ -506,7 +537,7 @@ private void handleProviderAdded( //If we have a persistent presence op set - then retrieve its contat //list and merge it with the local one. if( opSetPersPresence != null ){ - synchronizeOpSetWithLocalContactList(provider, opSetPersPresence); + synchronizeOpSetWithLocalContactList(opSetPersPresence); } else logger.debug("Service did not have a pers. pres. op. set."); @@ -585,18 +616,31 @@ private class ContactListSubscriptionListener implements SubscriptionListener { - public void subscriptionCreated(SubscriptionEvent evt) { - + /** + * Creates a meta contact for the source contact indicated by the + * specified SubscriptionEvent, or updates an existing one if there + * is one. The method would also generate the corresponding + * MetaContactEvent. + * + * @param evt the SubscriptionEvent that we'll be handling. + */ + public void subscriptionCreated(SubscriptionEvent evt) + { logger.trace("Subscription created: " + evt); - MetaContactGroupImpl parentGroup = - (MetaContactGroupImpl)rootMetaGroup.getMetaContactSubgroup(evt - .getParentGroup().getGroupName()); + MetaContactGroupImpl parentGroup = (MetaContactGroupImpl) + findMetaContactGroupByContactGroup( evt.getParentGroup() ); + + if(parentGroup == null) + { + logger.error("Received a subscription for a group that we " + +"hadn't seen before! " + evt); + return; + } MetaContactImpl newMetaContact = new MetaContactImpl(); - newMetaContact.addProtoContact(evt - .getSourceContact()); + newMetaContact.addProtoContact(evt.getSourceContact()); newMetaContact.setDisplayName(evt .getSourceContact().getDisplayName()); @@ -606,11 +650,11 @@ public void subscriptionCreated(SubscriptionEvent evt) { fireMetaContactEvent(newMetaContact, evt.getSourceProvider(), parentGroup, - MetaContactEvent.METACONTACT_ADDED); + MetaContactEvent.META_CONTACT_ADDED); } - public void subscriptionFailed(SubscriptionEvent evt) { + public void subscriptionFailed(SubscriptionEvent evt) { logger.trace("Subscription failed: " + evt); } @@ -618,18 +662,35 @@ public void subscriptionRemoved(SubscriptionEvent evt) { logger.trace("Subscription removed: " + evt); - MetaContact metaContact - = findMetaContactByContact(evt.getSourceContact()); + MetaContactImpl metaContact = (MetaContactImpl) + findMetaContactByContact(evt.getSourceContact()); - MetaContactGroup metaContactGroup - = findMetaContactGroupByContactGroup(evt.getParentGroup()); + MetaContactGroupImpl metaContactGroup = (MetaContactGroupImpl) + findMetaContactGroupByContactGroup(evt.getParentGroup()); - //TODO: remove contact from metacontact + metaContact.removeProtoContact(evt.getSourceContact()); - fireMetaContactEvent(metaContact, - evt.getSourceProvider(), - metaContactGroup, - MetaContactEvent.METACONTACT_REMOVED); + //if this was the last protocol specific contact in this meta + //contact then remove the meta contact as well. + if(metaContact.getContactCount() == 0) + { + metaContactGroup.removeMetaContact(metaContact); + + fireMetaContactEvent(metaContact, + evt.getSourceProvider(), + metaContactGroup, + MetaContactEvent.META_CONTACT_REMOVED); + } + else + { + //this was not the las proto contact so only generate the + //corresponding event. + fireMetaContactEvent(metaContact, + evt.getSourceProvider(), + metaContactGroup, + MetaContactEvent.PROTO_CONTACT_REMOVED); + + } } } @@ -648,9 +709,7 @@ public void groupCreated(ServerStoredGroupEvent evt) { MetaContactGroupImpl newMetaGroup = new MetaContactGroupImpl( evt.getSrouceGroup().getGroupName()); - newMetaGroup.addProtoGroup( - evt.getSourceProvider(), evt - .getSrouceGroup()); + newMetaGroup.addProtoGroup(evt.getSrouceGroup()); Iterator contactsIter = evt.getSrouceGroup() .contacts(); @@ -672,26 +731,46 @@ public void groupCreated(ServerStoredGroupEvent evt) { fireMetaContactGroupEvent(newMetaGroup, evt.getSourceProvider(), - MetaContactGroupEvent.METACONTACT_GROUP_ADDED); + MetaContactGroupEvent.META_CONTACT_GROUP_ADDED); } + /** + * Updates the local contact list by removing the meta contact group + * corresponding to the group indicated by the delivered evt + * @param evt the ServerStoredGroupEvent contining the group that has + * been removed. + */ public void groupRemoved(ServerStoredGroupEvent evt) { logger.trace("ContactGroup removed: " + evt); - MetaContactGroup metaContactGroup - = findMetaContactGroupByContactGroup(evt.getSrouceGroup()); + MetaContactGroupImpl metaContactGroup = (MetaContactGroupImpl) + findMetaContactGroupByContactGroup(evt.getSrouceGroup()); - if (metaContactGroup != null) { + if (metaContactGroup == null) { + logger.error("Received a RemovedGroup event for an orphan grp: " + + evt.getSrouceGroup()); + return; + } + + + removeContactGroupFromMetaContactGroup(metaContactGroup, + evt.getSrouceGroup(), evt.getSourceProvider()); + + //do not remove the meta contact group even if this is the las + //protocol specific contact group. Contrary to contacts, meta + //contact groups are to only be remove upon user indication or + //otherwise it would be difficult for a user to create a new grp. - removeMetaContactGroup(metaContactGroup); - fireMetaContactGroupEvent(metaContactGroup, - evt.getSourceProvider(), - MetaContactGroupEvent.METACONTACT_GROUP_REMOVED); - } } + /** + * Nothing to do here really. Oh yes .... we should actually trigger + * a MetaContactGroup event indicating the change for interested parties + * but that's all. + * @param evt the ServerStoredGroupEvent containing the source group. + */ public void groupNameChanged(ServerStoredGroupEvent evt) { logger.trace("ContactGroup renamed: " + evt); @@ -699,7 +778,8 @@ public void groupNameChanged(ServerStoredGroupEvent evt) { MetaContactGroup metaContactGroup = findMetaContactGroupByContactGroup(evt.getSrouceGroup()); - //TODO: change the name of the MetaContactGroup + fireMetaContactGroupEvent(metaContactGroup, evt.getSourceProvider(), + MetaContactGroupEvent.CONTACT_GROUP_RENAMED_IN_META_GROUP); } } @@ -735,10 +815,18 @@ private void fireMetaContactEvent(MetaContact source, while (listeners.hasNext()) { MetaContactListListener l = (MetaContactListListener) listeners .next(); - if (eventID == MetaContactEvent.METACONTACT_ADDED) - l.metaContactAdded(evt); - else if (eventID == MetaContactEvent.METACONTACT_REMOVED) - l.metaContactRemoved(evt); + switch (eventID) + { + case MetaContactEvent.META_CONTACT_ADDED: + l.metaContactAdded(evt);break; + case MetaContactEvent.META_CONTACT_REMOVED: + l.metaContactRemoved(evt);break; + case MetaContactEvent.PROTO_CONTACT_REMOVED: + case MetaContactEvent.PROTO_CONTACT_ADDED: + l.metaContactModified(evt);break; + default: + logger.error("Unknown event type " + eventID); + } } } } @@ -773,10 +861,20 @@ private void fireMetaContactGroupEvent( while (listeners.hasNext()) { MetaContactListListener l = (MetaContactListListener) listeners .next(); - if (eventID == MetaContactGroupEvent.METACONTACT_GROUP_ADDED) - l.metaContactGroupAdded(evt); - else if (eventID == MetaContactGroupEvent.METACONTACT_GROUP_REMOVED) - l.metaContactGroupRemoved(evt); + + switch (eventID) + { + case MetaContactGroupEvent.META_CONTACT_GROUP_ADDED: + l.metaContactGroupAdded(evt);break; + case MetaContactGroupEvent.META_CONTACT_GROUP_REMOVED: + l.metaContactGroupRemoved(evt);break; + case MetaContactGroupEvent + .CONTACT_GROUP_REMOVED_FROM_META_GROUP: + l.metaContactGroupModified(evt);break; + default: + logger.error("Unknown event type ("+eventID + +") for event: " + evt); + } } } } diff --git a/src/net/java/sip/communicator/service/contactlist/MetaContact.java b/src/net/java/sip/communicator/service/contactlist/MetaContact.java index ca6e43eb5..98bebb011 100644 --- a/src/net/java/sip/communicator/service/contactlist/MetaContact.java +++ b/src/net/java/sip/communicator/service/contactlist/MetaContact.java @@ -38,6 +38,19 @@ public interface MetaContact */ public Iterator getContacts(); + /** + * Returns a contact encapsulated by this meta contact, having the specified + * contactAddress and coming from the indicated ownerProvider. + * @param contactAddress the address of the contact who we're looking for. + * @param ownerProvider a reference to the ProtocolProviderService that + * the contact we're looking for belongs to. + * @return a reference to a Contact, encapsulated by this + * MetaContact, carrying the specified address and originating from the + * specified ownerProvider or null if no such contact exists.. + */ + public Contact getContact( String contactAddress, + ProtocolProviderService ownerProvider); + /** * Returns the number of protocol speciic Contacts that this * MetaContact contains. @@ -47,17 +60,17 @@ public interface MetaContact public int getContactCount(); /** - * Returns a Contact, encapsulated by this MetaContact and coming from the - * specified ProtocolProviderService. If none of the contacts encapsulated - * by this MetaContact is originating from the specified provider then - * null is returned. + * Returns all protocol specific Contacts, encapsulated by this MetaContact + * and coming from the indicated ProtocolProviderService. If none of the + * contacts encapsulated by this MetaContact is originating from the + * specified provider then an empty iterator is returned. *
* @param provider a reference to the ProtocolProviderService - * that we'd like to get a Contact for. - * @return a Contact encapsulated in this + * whose contacts we'd like to get. + * @return an Iterator over all contacts encapsulated in this * MetaContact and originating from the specified provider. */ - public Contact getContactForProvider(ProtocolProviderService provider); + public Iterator getContactsForProvider(ProtocolProviderService provider); /** * Returns a String identifier (the actual contents is left to diff --git a/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java b/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java index 19a2178c5..91b2ab924 100644 --- a/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java +++ b/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.service.contactlist; import java.util.*; +import net.java.sip.communicator.service.protocol.*; /** * MetaContactGroups are used to merge groups (often originating @@ -21,6 +22,42 @@ */ public interface MetaContactGroup { + /** + * Returns an iterator over all the protocol specific groups that this + * contact group represents. + * @return an Iterator over the protocol specific groups that this group + * represents. + */ + public Iterator getContactGroups(); + + /** + * Returns all protocol specific ContactGroups, encapsulated by this + * MetaContactGroup and coming from the indicated ProtocolProviderService. + * If none of the contacts encapsulated by this MetaContact is originating + * from the specified provider then an empty iterator is returned. + *
+ * @param provider a reference to the ProtocolProviderService + * whose ContactGroups we'd like to get. + * @return an Iterator over all contacts encapsulated in this + * MetaContact and originating from the specified provider. + */ + public Iterator getContactGroupsForProvider(ProtocolProviderService provider); + + /** + * Returns a contact group encapsulated by this meta contact group, having + * the specified groupName and coming from the indicated ownerProvider. + * + * @param groupName the name of the contact group who we're looking for. + * @param ownerProvider a reference to the ProtocolProviderService that + * the contact we're looking for belongs to. + * @return a reference to a ContactGroup, encapsulated by this + * MetaContactGroup, carrying the specified name and originating from the + * specified ownerProvider. + */ + public ContactGroup getContactGroup(String groupName, + ProtocolProviderService ownerProvider); + + /** * Returns a java.util.Iterator over the MetaContacts * contained in this MetaContactGroup. @@ -68,12 +105,12 @@ public interface MetaContactGroup /** * Returns the contact with the specified identifier - * @param metaContactID a String identifier obtained through the + * @param metaUID a String identifier obtained through the * MetaContact.getMetaUID() method. *
* @return the MetaContact with the specified idnetifier. */ - public MetaContact getMetaContact(String metaContactID); + public MetaContact getMetaContact(String metaUID); /** * Returns the meta contact on the specified index. diff --git a/src/net/java/sip/communicator/service/contactlist/MetaContactListService.java b/src/net/java/sip/communicator/service/contactlist/MetaContactListService.java index fe7741498..9df633622 100644 --- a/src/net/java/sip/communicator/service/contactlist/MetaContactListService.java +++ b/src/net/java/sip/communicator/service/contactlist/MetaContactListService.java @@ -49,7 +49,6 @@ * only load their mocking protocol provider implementations during the test * run. *
- * @todo expections * @author Emil Ivov */ public interface MetaContactListService @@ -80,11 +79,26 @@ public interface MetaContactListService * we need to find the MetaContact that is the author of an incoming message * and the corresponding ProtocolProviderService has only provided a * Contact as its author. + * + * @param contact the contact whose encapsulating meta contact we're looking + * for. * @return the MetaContact containing the speicified contact or null * if no such contact is present in this contact list. */ public MetaContact findMetaContactByContact(Contact contact); + /** + * Returns the MetaContactGroup encapsulating the specified protocol contact + * group or null if no such MetaContactGroup was found. + * + * @param group the group whose encapsulating meta group we're looking for. + * @return the MetaContact containing the speicified contact or null + * if no such contact is present in this contact list. + */ + public MetaContactGroup findMetaContactGroupByContactGroup( + ContactGroup group); + + /** * Returns the MetaContact that corresponds to the specified metaContactID. * @@ -92,7 +106,7 @@ public interface MetaContactListService * @return the MetaContact with the speicified string identifier or null * if no such meta contact was found. */ - public MetaContact findMetaContactByID(String metaContactID); + public MetaContact findMetaContactByMetaUID(String metaContactID); /** * Adds a listener for MetaContactListChangeEvents posted after