Add details to metacontact.

cusax-fix
Damian Minkov 18 years ago
parent eb93b6cf10
commit 107891b595

@ -174,7 +174,23 @@ public class MclStorageManager
*/
private static final String META_CONTACT_DISPLAY_NAME_NODE_NAME
= "display-name";
/**
* The name of the XML node that contains meta contact detail.
*/
private static final String META_CONTACT_DETAIL_NAME_NODE_NAME
= "detail";
/**
* The name of the XML attribute that contains detail name.
*/
private static final String DETAIL_NAME_ATTR_NAME = "name";
/**
* The name of the XML attribute that contains detail value.
*/
private static final String DETAIL_VALUE_ATTR_NAME = "value";
/**
* The name of the XML node that contains information of a proto contact
*/
@ -763,10 +779,45 @@ private void processGroupXmlNode(
//contain any contacts matching the currently parsed account id.
if (protoContacts.size() < 1)
continue;
// Extract contact details.
Hashtable details = new Hashtable();
try
{
List detailsNodes = XMLUtils.findChildren(
(Element) currentMetaContactNode
, META_CONTACT_DETAIL_NAME_NODE_NAME);
for (int j = 0; j < detailsNodes.size(); j++)
{
Element e = (Element)detailsNodes.get(j);
String name = e.getAttribute(DETAIL_NAME_ATTR_NAME);
String value = e.getAttribute(DETAIL_VALUE_ATTR_NAME);
Object detailsObj = details.get(name);
if(detailsObj == null)
details.put(name, value);
else if(detailsObj instanceof List)
((List)detailsObj).add(value);
else if(detailsObj instanceof String)
{
ArrayList ds = new ArrayList();
ds.add(detailsObj);
ds.add(value);
details.put(name, ds);
}
}
}
catch(Exception ex)
{
// catch any exception from loading contacts
// that will prevent loading the contact
logger.error("Cannot load details for contact node " +
currentMetaContactNode, ex);
}
//pass the parsed proto contacts to the mcl service
mclServiceImpl.loadStoredMetaContact(
currentMetaGroup, uid, displayName, protoContacts,
currentMetaGroup, uid, displayName, details, protoContacts,
accountID);
}
catch(Throwable thr)
@ -1401,7 +1452,152 @@ public void metaContactRenamed(MetaContactRenamedEvent evt)
+ evt.getSourceMetaContact(), ex);
}
}
/**
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactModifiedEvent containing the corresponding contact
*/
public void metaContactModified(MetaContactModifiedEvent evt)
{
String name = evt.getModificationName();
Element metaContactNode = findMetaContactNode(
evt.getSourceMetaContact().getMetaUID());
//not sure what to do in case of null. we'll be loggin an internal err
//for now and that's all.
if(metaContactNode == null)
{
logger.error("Save after renam failed. Contact not found: "
+ evt.getSourceMetaContact());
return;
}
Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue();
boolean isChanged = false;
if(oldValue == null && newValue != null)
{
// indicates add
if(!(newValue instanceof String))
return;
Element detailElement = contactListDocument.createElement(
META_CONTACT_DETAIL_NAME_NODE_NAME);
detailElement.setAttribute(DETAIL_NAME_ATTR_NAME, name);
detailElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
(String)newValue);
metaContactNode.appendChild(detailElement);
isChanged = true;
}
else if(oldValue != null && newValue == null)
{
// indicates remove
if(oldValue instanceof List)
{
List valuesToRemove = (List)oldValue;
// indicates removing multiple values at one time
List nodes = XMLUtils.locateElements(
metaContactNode
, META_CONTACT_DETAIL_NAME_NODE_NAME
, DETAIL_NAME_ATTR_NAME
, name);
ArrayList nodesToRemove = new ArrayList();
for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(valuesToRemove.contains(
e.getAttribute(DETAIL_VALUE_ATTR_NAME)))
{
nodesToRemove.add(e);
}
}
for (int i = 0; i < nodesToRemove.size(); i++)
{
Element e = (Element)nodesToRemove.get(i);
metaContactNode.removeChild(e);
}
if(nodesToRemove.size() > 0)
isChanged = true;
}
else if(oldValue instanceof String)
{
// removing one value only
List nodes = XMLUtils.locateElements(
metaContactNode
, META_CONTACT_DETAIL_NAME_NODE_NAME
, DETAIL_NAME_ATTR_NAME
, name);
Element elementToRemove = null;
for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue))
{
elementToRemove = e;
break;
}
}
if(elementToRemove == null)
return;
metaContactNode.removeChild(elementToRemove);
isChanged = true;
}
}
else if(oldValue != null && newValue != null)
{
// indicates change
List nodes = XMLUtils.locateElements(
metaContactNode
, META_CONTACT_DETAIL_NAME_NODE_NAME
, DETAIL_NAME_ATTR_NAME
, name);
Element changedElement = null;
for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue))
{
changedElement = e;
break;
}
}
if(changedElement == null)
return;
changedElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
(String)newValue);
isChanged = true;
}
if(!isChanged)
return;
try{
scheduleContactListStorage();
}
catch (IOException ex){
/**given we're being invoked from an event dispatch thread that was
probably triggered by a net operation - we could not do much.
so ... log and @todo one day we'll have a global error dispatcher */
logger.error("Writing CL failed after rename of "
+ evt.getSourceMetaContact(), ex);
}
}
/**
* Removes the corresponding node from the xml contact list.

@ -8,7 +8,9 @@
import java.util.*;
import java.util.*;
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.util.*;
/**
@ -63,25 +65,43 @@ public class MetaContactImpl
* A sync lock for use when modifying the parentGroup field.
*/
private Object parentGroupModLock = new Object();
/**
* Hashtable containing the contact details.
* Name -> Value or Name -> (List of values).
*/
private Hashtable details = new Hashtable();
/**
* The service that is creating the contact.
*/
private MetaContactListServiceImpl mclServiceImpl = null;
/**
* Creates new meta contact with a newly generated meta contact UID.
* @param mclServiceImpl the service that creates the contact.
*/
MetaContactImpl()
MetaContactImpl(MetaContactListServiceImpl mclServiceImpl)
{
//create the uid
this.uid = String.valueOf( System.currentTimeMillis())
+ String.valueOf(hashCode());
this.mclServiceImpl = mclServiceImpl;
}
/**
* Creates a new meta contact with the specified UID. This constructor
* MUST ONLY be used when restoring contacts stored in the contactlist.xml.
* @param metaUID the meta uid that this meta contact should have.
* @param mclServiceImpl the service that creates the contact.
* @param details the already stored details for the contact.
*/
MetaContactImpl(String metaUID)
MetaContactImpl(MetaContactListServiceImpl mclServiceImpl,
String metaUID, Hashtable details)
{
this.uid = metaUID;
this.mclServiceImpl = mclServiceImpl;
this.details = details;
}
/**
@ -683,4 +703,118 @@ public MetaContactGroup getParentMetaContactGroup()
return getParentGroup();
}
/**
* Adds a custom detail to this contact.
* @param name name of the detail.
* @param value the value of the detail.
*/
public void addDetail(String name, String value)
{
ArrayList values = (ArrayList)details.get(name);
if(values == null)
values = new ArrayList();
values.add(value);
details.put(name, values);
mclServiceImpl.fireMetaContactEvent(
new MetaContactModifiedEvent(
this,
name,
null,
value));
}
/**
* Remove the given detail.
* @param name of the detail to be removed.
* @param value value of the detail to be removed.
*/
public void removeDetail(String name, String value)
{
ArrayList values = (ArrayList)details.get(name);
if(values == null)
return;
values.remove(value);
mclServiceImpl.fireMetaContactEvent(
new MetaContactModifiedEvent(
this,
name,
value,
null));
}
/**
* Remove all details with given name.
* @param name of the details to be removed.
*/
public void removeDetails(String name)
{
Object removed = details.remove(name);
mclServiceImpl.fireMetaContactEvent(
new MetaContactModifiedEvent(
this,
name,
removed,
null));
}
/**
* Change the detail.
* @param name of the detail to be changed.
* @param oldValue the old value of the detail.
* @param newValue the new value of the detail.
*/
public void changeDetail(String name, String oldValue, String newValue)
{
ArrayList values = (ArrayList)details.get(name);
if(values == null)
return;
int changedIx = -1;
for (int i = 0; i < values.size(); i++)
{
if(values.get(i).equals(oldValue))
{
changedIx = i;
break;
}
}
if(changedIx == -1)
return;
values.set(changedIx, newValue);
mclServiceImpl.fireMetaContactEvent(
new MetaContactModifiedEvent(
this,
name,
oldValue,
newValue));
}
/**
* Get all details with given name.
* @param name the name of the details we are searching.
*/
public List getDetails(String name)
{
ArrayList values = (ArrayList)details.get(name);
if(values == null)
values = new ArrayList();
else
values = (ArrayList)values.clone();
return values;
}
}

@ -687,7 +687,7 @@ public void createMetaContact(
+ " is not an instance of MetaContactGroupImpl");
}
MetaContactImpl newMetaContact = new MetaContactImpl();
MetaContactImpl newMetaContact = new MetaContactImpl(this);
this.addNewContactToMetaContact(provider, metaContactGroup, newMetaContact,
contactID, false); //don't fire a PROTO_CONT_ADDED event we'll
@ -839,7 +839,7 @@ public void moveContact(Contact contact,
throws MetaContactListException
{
/** first create the new meta contact */
MetaContactImpl metaContactImpl = new MetaContactImpl();
MetaContactImpl metaContactImpl = new MetaContactImpl(this);
MetaContactGroupImpl newParentMetaGroupImpl
= (MetaContactGroupImpl)newParentMetaGroup;
@ -1475,7 +1475,7 @@ private void addContactGroupToMetaGroup(ContactGroup protoGroup,
continue;
MetaContactImpl newMetaContact = new MetaContactImpl();
MetaContactImpl newMetaContact = new MetaContactImpl(this);
newMetaContact.addProtoContact(contact);
@ -1900,7 +1900,8 @@ public void subscriptionCreated(SubscriptionEvent evt)
return;
}
MetaContactImpl newMetaContact = new MetaContactImpl();
MetaContactImpl newMetaContact = new MetaContactImpl(
MetaContactListServiceImpl.this);
newMetaContact.addProtoContact(evt.getSourceContact());
@ -2002,7 +2003,8 @@ public void subscriptionMoved(SubscriptionMovedEvent evt)
//parent group and move the source contact to it.
else
{
MetaContactImpl newMetaContact = new MetaContactImpl();
MetaContactImpl newMetaContact = new MetaContactImpl(
MetaContactListServiceImpl.this);
newMetaContact.setDisplayName(evt
.getSourceContact().getDisplayName());
newParentGroup.addMetaContact(newMetaContact);
@ -2165,7 +2167,8 @@ private MetaContactGroup handleGroupCreatedEvent(
{
Contact contact = (Contact) contactsIter.next();
MetaContactImpl newMetaContact = new MetaContactImpl();
MetaContactImpl newMetaContact = new MetaContactImpl(
MetaContactListServiceImpl.this);
newMetaContact.addProtoContact(contact);
@ -2344,7 +2347,7 @@ private void fireMetaContactEvent(MetaContact sourceContact,
*
* @param event the event to dispatch.
*/
private void fireMetaContactEvent(MetaContactPropertyChangeEvent event)
void fireMetaContactEvent(MetaContactPropertyChangeEvent event)
{
logger.trace("Will dispatch the following mcl property change event: "
+ event);
@ -2368,6 +2371,10 @@ else if (event instanceof MetaContactRenamedEvent)
{
listener.metaContactRenamed( (MetaContactRenamedEvent) event);
}
else if (event instanceof MetaContactModifiedEvent)
{
listener.metaContactModified( (MetaContactModifiedEvent) event);
}
}
}
@ -2580,6 +2587,7 @@ ContactGroup loadStoredContactGroup(MetaContactGroupImpl containingMetaGroup,
* to load.
* @param metaUID the unique identifier of the meta contact.
* @param displayName the display name of the meta contact.
* @param details the details for the contact to create.
* @param protoContacts a list containing descriptors of proto contacts
* encapsulated by the meta contact that we're about to create.
* @param accountID the identifier of the account that the contacts
@ -2588,6 +2596,7 @@ ContactGroup loadStoredContactGroup(MetaContactGroupImpl containingMetaGroup,
void loadStoredMetaContact(MetaContactGroupImpl parentGroup,
String metaUID,
String displayName,
Hashtable details,
List protoContacts,
String accountID)
{
@ -2597,7 +2606,7 @@ void loadStoredMetaContact(MetaContactGroupImpl parentGroup,
if(newMetaContact == null)
{
newMetaContact = new MetaContactImpl(metaUID);
newMetaContact = new MetaContactImpl(this, metaUID, details);
newMetaContact.setDisplayName(displayName);
}

@ -413,6 +413,14 @@ public void metaContactRenamed(MetaContactRenamedEvent evt)
}
}
}
/**
* Implements <tt>MetaContactListListener.metaContactModified</tt> method.
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactModifiedEvent containing the corresponding contact
*/
public void metaContactModified(MetaContactModifiedEvent evt)
{}
/**
* Implements <tt>MetaContactListListener.protoContactAdded</tt> method.

@ -158,6 +158,14 @@ public void metaContactRenamed(MetaContactRenamedEvent evt)
{
this.refreshContact(evt.getSourceMetaContact());
}
/**
* Handles the <tt>MetaContactModifiedEvent</tt>.
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactModifiedEvent containing the corresponding contact
*/
public void metaContactModified(MetaContactModifiedEvent evt)
{}
/**
* Handles the <tt>ProtoContactEvent</tt>. Refreshes the list when a

@ -118,4 +118,38 @@ public Contact getContact( String contactAddress,
* @return a String representation of this <tt>MetaContact</tt>.
*/
public String toString();
/**
* Adds a custom detail to this contact.
* @param name name of the detail.
* @param value the value of the detail.
*/
public void addDetail(String name, String value);
/**
* Remove the given detail.
* @param name of the detail to be removed.
* @param value value of the detail to be removed.
*/
public void removeDetail(String name, String value);
/**
* Remove all details with given name.
* @param name of the details to be removed.
*/
public void removeDetails(String name);
/**
* Change the detail.
* @param name of the detail to be changed.
* @param oldValue the old value of the detail.
* @param newValue the new value of the detail.
*/
public void changeDetail(String name, String oldValue, String newValue);
/**
* Get all details with given name.
* @param name the name of the details we are searching.
*/
public List getDetails(String name);
}

@ -101,5 +101,11 @@ public interface MetaContactListListener
* event.
*/
public void childContactsReordered(MetaContactGroupEvent evt);
/**
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactModifiedEvent containing the corresponding contact
*/
public void metaContactModified(MetaContactModifiedEvent evt);
}

@ -0,0 +1,47 @@
/*
* 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.service.contactlist.event;
import net.java.sip.communicator.service.contactlist.*;
/**
* Indicates that a meta contact has chaned.
* @author Damian Minkov
*/
public class MetaContactModifiedEvent
extends MetaContactPropertyChangeEvent
{
/**
* Name of the modification.
*/
private String modificationName;
/**
* Creates an instance of this event using the specified arguments.
* @param source the <tt>MetaContact</tt> that this event is about.
* @param modificationName name of the modifiaction
* @param oldValue the new value for the modification of this meta contact.
* @param newValue the old value for the modification of this meta contact.
*/
public MetaContactModifiedEvent(MetaContact source,
String modificationName,
Object oldValue,
Object newValue)
{
super(source, META_CONTACT_MODIFIED, oldValue, newValue);
this.modificationName = modificationName;
}
/**
* Returns the modification name of the source meta contact.
* @return the modification name for the meta contact.
*/
public String getModificationName()
{
return modificationName;
}
}

@ -49,6 +49,12 @@ public abstract class MetaContactPropertyChangeEvent
* addition of a protocol specific contact to an existing MetaContact.
*/
public static final String PROTO_CONTACT_MOVED = "ProtoContactMoved";
/**
* Indicates that the meta contact has been modified. The old and new value
* arguments contain the old and new values of the modification.
*/
public static final String META_CONTACT_MODIFIED = "MetaContactModifiedEvent";
/**
* Creates an instnace of this event.

@ -8,6 +8,7 @@
import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
@ -391,6 +392,35 @@ public static Element findChild(Element parent, String tagName)
return null;
}
/**
* Returns the children elements with the specified tagName for the specified
* parent element.
* @param parent The parent whose children we're looking for.
* @param tagName the name of the child to find
* @return List of the children with the specified name
* @throws NullPointerException if parent or tagName are null
*/
public static List findChildren(Element parent, String tagName)
{
if(parent == null || tagName == null)
throw new NullPointerException("Parent or tagname were null! "
+ "parent = " + parent + "; tagName = " + tagName);
ArrayList result = new ArrayList();
NodeList nodes = parent.getChildNodes();
Node node;
int len = nodes.getLength();
for(int i = 0; i < len; i++)
{
node = nodes.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE
&& ((Element)node).getNodeName().equals(tagName))
result.add(node);
}
return result;
}
/**
* Looks through all child elements of the specified root (recursively)
@ -440,4 +470,55 @@ public static Element locateElement(Element root,
return null;
}
/**
* Looks through all child elements of the specified root (recursively)
* and returns the elements that corresponds to all parameters.
*
* @param root the Element where the search should begin
* @param tagName the name of the node we're looking for
* @param keyAttributeName the name of an attribute that the node has to
* have
* @param keyAttributeValue the value that attribute must have
* @return list of Elements in the tree under root that match the specified
* paameters.
* @throws NullPointerException if any of the arguments is null.
*/
public static List locateElements(Element root,
String tagName,
String keyAttributeName,
String keyAttributeValue)
{
ArrayList result = new ArrayList();
NodeList nodes = root.getChildNodes();
Node node;
int len = nodes.getLength();
for(int i = 0; i < len; i++)
{
node = nodes.item(i);
if(node.getNodeType() != Node.ELEMENT_NODE)
continue;
// is this the node we're looking for?
if(node.getNodeName().equals(tagName))
{
String attr = ((Element)node).getAttribute(keyAttributeName);
if( attr!= null
&& attr.equals(keyAttributeValue))
result.add(node);
}
//look inside.
List childs = locateElements( (Element) node, tagName
, keyAttributeName, keyAttributeValue);
if (childs != null)
result.addAll(childs);
}
return result;
}
}

@ -240,4 +240,51 @@ public void verifyCompareToForAllContactsInGroupAndSubgroups(
}
}
/**
* Test creating, changing and removing metacontact details.
*/
public void testDetails()
{
String name = "test_detail_name";
String detail_1 = "detail_1";
String detail_2 = "detail_2";
String detail_3 = "detail_3";
metaContact.addDetail(name, detail_1);
List ds = metaContact.getDetails(name);
assertTrue( "Must contain one detail",
1 == ds.size());
assertTrue("The result details does not contain the desired",
ds.contains(detail_1));
metaContact.changeDetail(name, detail_1, detail_2);
ds = metaContact.getDetails(name);
assertEquals( "Must contain one detail",
1 , ds.size());
assertTrue("The result details does not contain the desired",
ds.contains(detail_2));
metaContact.removeDetail(name, detail_2);
ds = metaContact.getDetails(name);
assertEquals( "Must contain no details",
0 , ds.size());
metaContact.addDetail(name, detail_1);
metaContact.addDetail(name, detail_2);
metaContact.addDetail(name, detail_3);
ds = metaContact.getDetails(name);
assertEquals( "Must contain three detail",
3 , ds.size());
assertTrue("The result details does not contain the desired",
ds.contains(detail_1));
assertTrue("The result details does not contain the desired",
ds.contains(detail_2));
assertTrue("The result details does not contain the desired",
ds.contains(detail_3));
metaContact.removeDetails(name);
ds = metaContact.getDetails(name);
assertEquals( "Must contain no details",
0 , ds.size());
}
}

@ -1148,6 +1148,12 @@ public void metaContactRenamed(MetaContactRenamedEvent evt)
collectedMetaContactEvents.add(evt);
}
/**
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactListEvent containing the corresponding contact
*/
public void metaContactModified(MetaContactModifiedEvent evt)
{}
/**
* Indicates that a protocol specific <tt>Contact</tt> instance has been

Loading…
Cancel
Save