/*
* 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.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 straightforward implementation of the meta contact group. The group
* implements a simple algorithm of sorting its children according to their
* status.
*
* @author Emil Ivov
* @author Lubomir Marinov
*/
public class MetaContactGroupImpl
implements MetaContactGroup
{
/**
* The <tt>Logger</tt> used by the class <tt>MetaContactGroupImpl</tt> and
* its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(MetaContactGroupImpl.class);
/**
* All the subgroups that this group contains.
*/
private Set<MetaContactGroupImpl> subgroups
= new TreeSet<MetaContactGroupImpl>();
/**
* A list containing all child contacts.
*/
private final Set<MetaContactImpl> childContacts
= new TreeSet<MetaContactImpl>();
/**
* A list of the contact groups encapsulated by this MetaContactGroup
*/
private Vector<ContactGroup> protoGroups = new Vector<ContactGroup>();
/**
* An id uniquely identifying the meta contact group in this contact list.
*/
private String groupUID = null;
/**
* The name of the group (fixed for root groups since it won't show).
*/
private String groupName = null;
/**
* We use this copy for returning iterators and searching over the list
* in order to avoid creating it upon each query. The copy is updated upon
* each modification
*/
private List<MetaContact> childContactsOrderedCopy
= new LinkedList<MetaContact>();
/**
* We use this copy for returning iterators and searching over the list
* in order to avoid creating it upon each query. The copy is updated upon
* each modification
*/
private List<MetaContactGroup> subgroupsOrderedCopy
= new LinkedList<MetaContactGroup>();
/**
* The meta contact group that is currently containing us.
*/
private MetaContactGroupImpl parentMetaContactGroup = null;
/**
* The <tt>MetaContactListService</tt> implementation which manages this
* <tt>MetaContactGroup</tt> and its associated hierarchy.
*/
private final MetaContactListServiceImpl mclServiceImpl;
/**
* The user-specific key-value associations stored in this instance.
* <p>
* Like the Widget implementation of Eclipse SWT, the storage type takes
* into account that there are likely to be many
* <code>MetaContactGroupImpl</code> instances and <code>Map</code>s are
* thus likely to impose increased memory use. While an array may very well
* perform worse than a <code>Map</code> with respect to search, the
* mechanism of user-defined key-value associations explicitly states that
* it is not guaranteed to be optimized for any particular use and only
* covers the most basic cases and performance-savvy code will likely
* implement a more optimized solution anyway.
* </p>
*/
private Object[] data;
/**
* Creates an instance of the root meta contact group.
*
* @param mclServiceImpl the <tt>MetaContactListService</tt> implementation
* which is to use the new <tt>MetaContactGroup</tt> instance as its root
* @param groupName the name of the group to create
*/
MetaContactGroupImpl(
MetaContactListServiceImpl mclServiceImpl,
String groupName)
{
this(mclServiceImpl, groupName, null);
}
/**
* Creates an instance of the root meta contact group assigning it the
* specified meta contact uid. This constructor MUST NOT be used for nothing
* purposes else but restoring contacts extracted from the contactlist.xml
*
* @param mclServiceImpl the implementation of the
* <tt>MetaContactListService</tt>, to which this group belongs
* @param groupName the name of the group to create
* @param metaUID a UID that has been stored earlier or null when a new
* UID needs to be created.
*/
MetaContactGroupImpl(
MetaContactListServiceImpl mclServiceImpl,
String groupName,
String metaUID)
{
this.mclServiceImpl = mclServiceImpl;
this.groupName = groupName;
this.groupUID
= (metaUID == null)
? String.valueOf( System.currentTimeMillis())
+ String.valueOf(hashCode())
: metaUID;
}
/**
* Returns a String identifier (the actual contents is left to
* implementations) that uniquely represents this <tt>MetaContact</tt> in
* the containing <tt>MetaContactList</tt>
*
* @return a String uniquely identifying this meta contact.
*/
public String getMetaUID()
{
return groupUID;
}
/**
* Returns the MetaContactGroup currently containing this group or null if
* this is the root group
* @return a reference to the MetaContactGroup currently containing this
* meta contact group or null if this is the root group.
*/
public MetaContactGroup getParentMetaContactGroup()
{
return parentMetaContactGroup;
}
/**
* Determines whether or not this group can contain subgroups.
*
* @return always <tt>true</tt> since this is the root contact group
* and in our impl it can only contain groups.
*/
public boolean canContainSubgroups()
{
return false;
}
/**
* Returns the number of <tt>MetaContact</tt>s that this group contains.
* <p>
* @return the number of <tt>MetaContact</tt>s that this group contains.
*/
public int countChildContacts()
{
return childContacts.size();
}
/**
* Returns the number of online <tt>MetaContact</tt>s that this group
* contains.
* <p>
* @return the number of online <tt>MetaContact</tt>s that this group
* contains.
*/
public int countOnlineChildContacts()
{
int onlineContactsNumber = 0;
try
{
Iterator<MetaContact> itr = getChildContacts();
while(itr.hasNext())
{
Contact contact = itr.next().getDefaultContact();
if(contact == null)
continue;
if(contact.getPresenceStatus().isOnline())
{
onlineContactsNumber++;
}
}
}
catch(Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Failed to count online contacts.", e);
}
return onlineContactsNumber;
}
/**
* Returns the number of <tt>ContactGroups</tt>s that this group
* encapsulates
* <p>
* @return an int indicating the number of ContactGroups-s that this group
* encapsulates.
*/
public int countContactGroups()
{
return protoGroups.size();
}
/**
* Returns the number of subgroups that this <tt>MetaContactGroup</tt>
* contains.
*
* @return an int indicating the number of subgroups in this group.
*/
public int countSubgroups()
{
return subgroups.size();
}
/**
* Returns a <tt>java.util.Iterator</tt> over the <tt>MetaContact</tt>s
* contained in this <tt>MetaContactGroup</tt>.
* <p>
* In order to prevent problems with concurrency, the <tt>Iterator</tt>
* returned by this method is not over the actual list of groups but over a
* copy of that list.
* <p>
*
* @return a <tt>java.util.Iterator</tt> over an empty contacts list.
*/
public Iterator<MetaContact> getChildContacts()
{
return childContactsOrderedCopy.iterator();
}
/**
* Returns the contact with the specified identifier
*
* @param metaContactID a String identifier obtained through the
* <tt>MetaContact.getMetaUID()</tt> method. <p>
* @return the <tt>MetaContact</tt> with the specified identifier.
*/
public MetaContact getMetaContact(String metaContactID)
{
Iterator<MetaContact> contactsIter = getChildContacts();
while(contactsIter.hasNext())
{
MetaContact contact = contactsIter.next();
if (contact.getMetaUID().equals(metaContactID))
return contact;
}
return null;
}
/**
* Returns the index of metaContact according to other contacts in this or
* -1 if metaContact does not belong to this group. The returned index is
* only valid until another contact has been added / removed or a contact
* has changed its status and hence - position. In such a case a REORDERED
* event is fired.
*
* @param metaContact the <tt>MetaContact</tt> whose index we're looking
* for.
* @return the index of <tt>metaContact</tt> in the list of child contacts
* or -1 if <tt>metaContact</tt>.
*/
public int indexOf(MetaContact metaContact)
{
int i = 0;
Iterator<MetaContact> childrenIter = getChildContacts();
while (childrenIter.hasNext())
{
MetaContact current = childrenIter.next();
if (current == metaContact)
{
return i;
}
i++;
}
//if we got here then metaContact is not in this list
return -1;
}
/**
* Returns the index of metaContactGroup in relation to other subgroups in
* this group or -1 if metaContact does not belong to this group. The
* returned index is only valid until another group has been added /
* removed or renamed In such a case a REORDERED event is fired.
*
* @param metaContactGroup the <tt>MetaContactGroup</tt> whose index we're
* looking for.
* @return the index of <tt>metaContactGroup</tt> in the list of child
* contacts or -1 if <tt>metaContact</tt>.
*/
public int indexOf(MetaContactGroup metaContactGroup)
{
int i = 0;
Iterator<MetaContactGroup> childrenIter = getSubgroups();
while (childrenIter.hasNext())
{
MetaContactGroup current = childrenIter.next();
if (current == metaContactGroup)
{
return i;
}
i++;
}
//if we got here then metaContactGroup is not in this list
return -1;
}
/**
* Returns the meta contact encapsulating a contact belonging to the
* specified <tt>provider</tt> with the specified identifier.
*
* @param provider the ProtocolProviderService that the specified
* <tt>contactID</tt> is pertaining to.
* @param contactID a String identifier of the protocol specific contact
* whose container meta contact we're looking for.
* @return the <tt>MetaContact</tt> with the specified identifier.
*/
public MetaContact getMetaContact(ProtocolProviderService provider,
String contactID)
{
Iterator<MetaContact> contactsIter = getChildContacts();
while(contactsIter.hasNext())
{
MetaContact contact = contactsIter.next();
if (contact.getContact(contactID, provider) != null)
return contact;
}
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 MetaContact findMetaContactByMetaUID(String metaUID)
{
//first go through the contacts that are direct children of this method.
Iterator<MetaContact> contactsIter = getChildContacts();
while(contactsIter.hasNext())
{
MetaContact mContact = contactsIter.next();
if( mContact.getMetaUID().equals(metaUID) )
return mContact;
}
//if we didn't find it here, let's try in the subgroups
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while( groupsIter.hasNext() )
{
MetaContactGroupImpl mGroup = (MetaContactGroupImpl) groupsIter.next();
MetaContact mContact = mGroup.findMetaContactByMetaUID(metaUID);
if (mContact != null)
return mContact;
}
return null;
}
/**
* Returns a meta contact group this group or some of its subgroups,
* that has the specified metaUID. If no such meta contact group exists,
* the method would return null.
*
* @param metaUID the Meta UID of the contact group we're looking for.
* @return the MetaContactGroup with the specified UID or null if no such
* contact exists.
*/
public MetaContactGroup findMetaContactGroupByMetaUID(String metaUID)
{
if (metaUID.equals(groupUID))
return this;
//if we didn't find it here, let's try in the subgroups
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while( groupsIter.hasNext() )
{
MetaContactGroupImpl mGroup
= (MetaContactGroupImpl) groupsIter.next();
if (metaUID.equals(mGroup.getMetaUID()))
return mGroup;
else
mGroup.findMetaContactByMetaUID(metaUID);
}
return null;
}
/**
* Returns an iterator over all the protocol specific groups that this
* contact group represents.
* <p>
* In order to prevent problems with concurrency, the <tt>Iterator</tt>
* returned by this method is not over the actual list of groups but over a
* copy of that list.
* <p>
* @return an Iterator over the protocol specific groups that this group
* represents.
*/
public Iterator<ContactGroup> getContactGroups()
{
return new LinkedList<ContactGroup>( this.protoGroups ).iterator();
}
/**
* Returns a contact group encapsulated by this meta contact group, having
* the specified groupName and coming from the indicated ownerProvider.
*
* @param grpName 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 <tt>ContactGroup</tt>, 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 grpName,
ProtocolProviderService ownerProvider)
{
Iterator<ContactGroup> encapsulatedGroups = getContactGroups();
while (encapsulatedGroups.hasNext())
{
ContactGroup group = encapsulatedGroups.next();
if (group.getGroupName().equals(grpName)
&& 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.
* <p>
* @param provider a reference to the <tt>ProtocolProviderService</tt>
* whose ContactGroups we'd like to get.
* @return an <tt>Iterator</tt> over all contacts encapsulated in this
* <tt>MetaContact</tt> and originating from the specified provider.
*/
public Iterator<ContactGroup> getContactGroupsForProvider(
ProtocolProviderService provider)
{
Iterator<ContactGroup> encapsulatedGroups = getContactGroups();
LinkedList<ContactGroup> protGroups = new LinkedList<ContactGroup>();
while(encapsulatedGroups.hasNext())
{
ContactGroup group = encapsulatedGroups.next();
if(group.getProtocolProvider() == provider)
{
protGroups.add(group);
}
}
return protGroups.iterator();
}
/**
* Returns all protocol specific ContactGroups, encapsulated by this
* MetaContactGroup and coming from the provider matching the
* <tt>accountID</tt> param. If none of the contacts encapsulated by this
* MetaContact is originating from the specified account then an empty
* iterator is returned.
* <p>
* Note to implementers: In order to prevent problems with concurrency, the
* <tt>Iterator</tt> returned by this method should not be over the actual
* list of groups but rather over a copy of that list.
* <p>
* @param accountID the id of the account whose contact groups we'd like to
* retrieve.
* @return an <tt>Iterator</tt> over all contacts encapsulated in this
* <tt>MetaContact</tt> and originating from the provider with the specified
* account id.
*/
public Iterator<ContactGroup> getContactGroupsForAccountID(String accountID)
{
Iterator<ContactGroup> encapsulatedGroups = getContactGroups();
LinkedList<ContactGroup> protGroups = new LinkedList<ContactGroup>();
while(encapsulatedGroups.hasNext())
{
ContactGroup group = encapsulatedGroups.next();
if(group.getProtocolProvider().getAccountID()
.getAccountUniqueID().equals(accountID))
{
protGroups.add(group);
}
}
return protGroups.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 MetaContact findMetaContactByContact(Contact protoContact)
{
//first go through the contacts that are direct children of this method.
Iterator<MetaContact> contactsIter = getChildContacts();
while(contactsIter.hasNext())
{
MetaContact mContact = 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 subgroups
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while( groupsIter.hasNext() )
{
MetaContactGroupImpl mGroup = (MetaContactGroupImpl) groupsIter.next();
MetaContact mContact = mGroup.findMetaContactByContact(
protoContact);
if (mContact != null)
return mContact;
}
return null;
}
/**
* Returns a meta contact, a child of this group or its subgroups, with
* address equald to <tt>contactAddress</tt> and a source protocol provider
* with the matching <tt>accountID</tt>. If no such meta contact exists,
* the method would return null.
*
* @param contactAddress the address of the protocol specific contact whose
* meta contact we're looking for.
* @param accountID the ID of the account that the contact we are looking
* for must belong to.
*
* @return the MetaContactImpl that contains the specified protocol specific
* contact.
*/
public MetaContact findMetaContactByContact(String contactAddress,
String accountID)
{
//first go through the contacts that are direct children of this method.
Iterator<MetaContact> contactsIter = getChildContacts();
while(contactsIter.hasNext())
{
MetaContactImpl mContact = (MetaContactImpl) contactsIter.next();
Contact storedProtoContact = mContact.getContact(
contactAddress, accountID);
if( storedProtoContact != null)
return mContact;
}
//if we didn't find it here, let's try in the subgroups
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while( groupsIter.hasNext() )
{
MetaContactGroupImpl mGroup = (MetaContactGroupImpl) groupsIter.next();
MetaContact mContact = mGroup.findMetaContactByContact(
contactAddress, accountID);
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 subgroups
Iterator<MetaContactGroup> 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.
*
* @param index the index of the meta contact to return.
* @return the MetaContact with the specified index, <p>
* @throws IndexOutOfBoundsException in case <tt>index</tt> is not a
* valid index for this group.
*/
public MetaContact getMetaContact(int index) throws
IndexOutOfBoundsException
{
return this.childContactsOrderedCopy.get(index);
}
/**
* Adds the specified <tt>metaContact</tt> to ths local list of child
* contacts.
* @param metaContact the <tt>MetaContact</tt> to add in the local vector.
*/
void addMetaContact(MetaContactImpl metaContact)
{
//set this group as a callback in the meta contact
metaContact.setParentGroup(this);
lightAddMetaContact(metaContact);
}
/**
* Adds the <tt>metaContact</tt> to the local list of child
* contacts without setting its parent contact and without any
* synchronization. This method is meant for use _PRIMARILY_ by the
* <tt>MetaContact</tt> itself upon change in its encapsulated protocol
* specific contacts.
*
* @param metaContact the <tt>MetaContact</tt> to add in the local vector.
* @return the index at which the contact was added.
*/
int lightAddMetaContact(MetaContactImpl metaContact)
{
synchronized(childContacts)
{
this.childContacts.add(metaContact);
//no need to synch it's not a disaster if s.o. else reads the old copy.
childContactsOrderedCopy
= new LinkedList<MetaContact>(childContacts);
return childContactsOrderedCopy.indexOf(metaContact);
}
}
/**
* Removes the <tt>metaContact</tt> from the local list of child
* contacts without unsetting its parent contact and without any
* synchronization. This method is meant for use _PRIMARILY_ by the
* <tt>MetaContact</tt> itself upon change in its encapsulated protocol
* specific contacts. The method would also regenerate the ordered copy
* used for generating iterators and performing search operations over
* the group.
*
* @param metaContact the <tt>MetaContact</tt> to remove from the local
* vector.
*/
void lightRemoveMetaContact(MetaContactImpl metaContact)
{
synchronized(childContacts)
{
this.childContacts.remove(metaContact);
//no need to synch it's not a disaster if s.o. else reads the old copy.
childContactsOrderedCopy
= new LinkedList<MetaContact>(childContacts);
}
}
/**
* Removes the specified <tt>metaContact</tt> from the local list of
* contacts.
* @param metaContact the <tt>MetaContact</tt>
*/
void removeMetaContact(MetaContactImpl metaContact)
{
metaContact.unsetParentGroup(this);
lightRemoveMetaContact(metaContact);
}
/**
* Returns the <tt>MetaContactGroup</tt> with the specified index.
* <p>
* @param index the index of the group to return.
* @return the <tt>MetaContactGroup</tt> with the specified index. <p>
* @throws IndexOutOfBoundsException if <tt>index</tt> is not a valid
* index.
*/
public MetaContactGroup getMetaContactSubgroup(int index) throws
IndexOutOfBoundsException
{
return subgroupsOrderedCopy.get(index);
}
/**
* Returns the <tt>MetaContactGroup</tt> with the specified name.
*
* @param grpName the name of the group to return.
* @return the <tt>MetaContactGroup</tt> with the specified name or null
* if no such group exists.
*/
public MetaContactGroup getMetaContactSubgroup(String grpName)
{
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while(groupsIter.hasNext())
{
MetaContactGroup mcGroup = groupsIter.next();
if(mcGroup.getGroupName().equals(grpName))
return mcGroup;
}
return null;
}
/**
* Returns the <tt>MetaContactGroup</tt> with the specified groupUID.
*
* @param grpUID the uid of the group to return.
* @return the <tt>MetaContactGroup</tt> with the specified uid or null
* if no such group exists.
*/
public MetaContactGroup getMetaContactSubgroupByUID(String grpUID)
{
Iterator<MetaContactGroup> groupsIter = getSubgroups();
while(groupsIter.hasNext())
{
MetaContactGroup mcGroup = groupsIter.next();
if(mcGroup.getMetaUID().equals(grpUID))
return mcGroup;
}
return null;
}
/**
* Returns true if and only if <tt>contact</tt> is a direct child of this
* group.
* @param contact the <tt>MetaContact</tt> whose relation to this group
* we'd like to determine.
* @return <tt>true</tt> if <tt>contact</tt> is a direct child of this group
* and <tt>false</tt> otherwise.
*/
public boolean contains(MetaContact contact)
{
synchronized (childContacts)
{
return this.childContacts.contains(contact);
}
}
/**
* Returns true if and only if <tt>group</tt> is a direct subgroup of this
* <tt>MetaContactGroup</tt>.
* @param group the <tt>MetaContactGroup</tt> whose relation to this group
* we'd like to determine.
* @return <tt>true</tt> if <tt>group</tt> is a direct child of this
* <tt>MetaContactGroup</tt> and <tt>false</tt> otherwise.
*/
public boolean contains(MetaContactGroup group)
{
return this.subgroups.contains(group);
}
/**
* Returns an <tt>java.util.Iterator</tt> over the sub groups that this
* <tt>MetaContactGroup</tt> contains.
* <p>
* In order to prevent problems with concurrency, the <tt>Iterator</tt>
* returned by this method is not over the actual list of groups but over a
* copy of that list.
* <p>
* @return a <tt>java.util.Iterator</tt> containing all subgroups.
*/
public Iterator<MetaContactGroup> getSubgroups()
{
return subgroupsOrderedCopy.iterator();
}
/**
* Returns the name of this group.
* @return a String containing the name of this group.
*/
public String getGroupName()
{
return groupName;
}
/**
* Sets the name of this group.
* @param newGroupName a String containing the new name of this group.
*/
void setGroupName(String newGroupName)
{
this.groupName = newGroupName;
}
/**
* Returns a String representation of this group and the contacts it
* contains (may turn out to be a relatively long string).
* @return a String representing this group and its child contacts.
*/
@Override
public String toString()
{
StringBuffer buff = new StringBuffer(getGroupName());
buff.append(".subGroups=" + countSubgroups() + ":\n");
Iterator<MetaContactGroup> subGroups = getSubgroups();
while (subGroups.hasNext())
{
MetaContactGroup group = subGroups.next();
buff.append(group.getGroupName());
if (subGroups.hasNext())
buff.append("\n");
}
buff.append("\nProtoGroups="+countContactGroups()+":[");
Iterator<ContactGroup> contactGroups = getContactGroups();
while (contactGroups.hasNext())
{
ContactGroup contactGroup = contactGroups.next();
buff.append(contactGroup.getProtocolProvider());
buff.append(".");
buff.append(contactGroup.getGroupName());
if(contactGroups.hasNext())
buff.append(", ");
}
buff.append("]");
buff.append("\nRootChildContacts="+countChildContacts()+":[");
Iterator<MetaContact> contacts = getChildContacts();
while (contacts.hasNext())
{
MetaContact contact = contacts.next();
buff.append(contact.toString());
if(contacts.hasNext())
buff.append(", ");
}
return buff.append("]").toString();
}
/**
* Adds 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.
*/
void addProtoGroup( ContactGroup protoGroup)
{
protoGroups.add(protoGroup);
}
/**
* 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 removeProtoGroup( ContactGroup protoGroup)
{
protoGroups.remove(protoGroup);
}
/**
* 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 addSubgroup(MetaContactGroup subgroup)
{
if (logger.isTraceEnabled())
logger.trace("Adding subgroup " + subgroup.getGroupName()
+ " to" + getGroupName());
this.subgroups.add((MetaContactGroupImpl)subgroup);
((MetaContactGroupImpl)subgroup).parentMetaContactGroup = this;
this.subgroupsOrderedCopy =
new LinkedList<MetaContactGroup>(subgroups);
}
/**
* Removes the meta contact group with the specified index.
* @param index the index of the group to remove.
* @return the <tt>MetaContactGroup</tt> that has just been removed.
*/
MetaContactGroupImpl removeSubgroup(int index)
{
MetaContactGroupImpl subgroup =
(MetaContactGroupImpl)subgroupsOrderedCopy.get(index);
if (subgroups.remove(subgroup))
subgroup.parentMetaContactGroup = null;
subgroupsOrderedCopy = new LinkedList<MetaContactGroup>(subgroups);
return subgroup;
}
/**
* Removes the specified group from the list of groups in this list.
* @param group the <tt>MetaContactGroup</tt> to remove.
* @return true if the group has been successfully removed and false
* otherwise.
*/
boolean removeSubgroup(MetaContactGroup group)
{
if(subgroups.contains(group))
{
removeSubgroup(subgroupsOrderedCopy.indexOf(group));
return true;
}
else
{
return false;
}
}
/**
* Returns the implementation of the <tt>MetaContactListService</tt>, to
* which this group belongs.
* @return the implementation of the <tt>MetaContactListService</tt>
*/
final MetaContactListServiceImpl getMclServiceImpl()
{
return mclServiceImpl;
}
/**
* Implements {@link MetaContactGroup#getData(Object)}.
* @return the data value corresponding to the given key
*/
public Object getData(Object key)
{
if (key == null)
throw new NullPointerException("key");
int index = dataIndexOf(key);
return (index == -1) ? null : data[index + 1];
}
/**
* Implements {@link MetaContactGroup#setData(Object, Object)}.
* @param key the of the data
* @param value the value of the data
*/
public void setData(Object key, Object value)
{
if (key == null)
throw new NullPointerException("key");
int index = dataIndexOf(key);
if (index == -1)
{
/*
* If value is null, remove the association with key (or just don't
* add it).
*/
if (data == null)
{
if (value != null)
data = new Object[] { key, value };
}
else if (value == null)
{
int length = data.length - 2;
if (length > 0)
{
Object[] newData = new Object[length];
System.arraycopy(data, 0, newData, 0, index);
System.arraycopy(
data, index + 2, newData, index, length - index);
data = newData;
}
else
data = null;
}
else
{
int length = data.length;
Object[] newData = new Object[length + 2];
System.arraycopy(data, 0, newData, 0, length);
data = newData;
data[length++] = key;
data[length++] = value;
}
}
else
data[index + 1] = value;
}
/**
* Determines whether or not this meta group contains only groups that are
* being stored by a server.
*
* @return true if the meta group is persistent and false otherwise.
*/
public boolean isPersistent()
{
Iterator<ContactGroup> contactGroupsIter = getContactGroups();
while (contactGroupsIter.hasNext())
{
ContactGroup contactGroup = contactGroupsIter.next();
if (contactGroup.isPersistent())
return true;
}
// this is new and empty group, we can store it as user want this
if(countContactGroups() == 0)
return true;
else
return false;
}
/**
* Determines the index in <code>#data</code> of a specific key.
*
* @param key
* the key to retrieve the index in <code>#data</code> of
* @return the index in <code>#data</code> of the specified <code>key</code>
* if it is contained; <tt>-1</tt> if <code>key</code> is not
* contained in <code>#data</code>
*/
private int dataIndexOf(Object key)
{
if (data != null)
for (int index = 0; index < data.length; index += 2)
if (key.equals(data[index]))
return index;
return -1;
}
/**
* Compares this meta contact group with the specified object for order.
* Returns a negative integer, zero, or a positive integer as this
* meta contact group is less than, equal to, or greater than the specified
* object.
* <p>
* The result of this method is calculated the following way:
* <p>
* + getGroupName().compareTo(o.getGroupName()) * 10 000
* + getMetaUID().compareTo(o.getMetaUID())<br>
* <p>
* Or in other words ordering of meta groups would be first done by
* display name, and finally (in order to avoid
* equalities) be the fairly random meta contact group metaUID.
* <p>
* @param target the <code>MetaContactGroup</code> to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*/
public int compareTo(MetaContactGroup target)
{
return getGroupName().compareToIgnoreCase(target.getGroupName())
* 10000
+ getMetaUID().compareTo(target.getMetaUID());
}
}