From d100493139446a9fc27e40932b1bb9dabd1e0a8f Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Fri, 17 Aug 2012 08:43:13 +0000 Subject: Adds get/set data to SourceContacts. Moves custom buttons to ExternalContactSource. Adds listener to refresh contact sources. --- .../impl/callhistory/CallHistorySourceContact.java | 4 +- .../impl/contactlist/MetaContactImpl.java | 127 +-------- .../impl/gui/main/contactlist/SearchFilter.java | 2 + .../impl/gui/main/contactlist/TreeContactList.java | 3 + .../contactsource/ExternalContactSource.java | 308 ++++++++++++++++++++- .../contactlist/contactsource/SourceUIContact.java | 249 ++--------------- .../contactsource/GenericSourceContact.java | 2 + .../service/contactsource/SourceContact.java | 32 +++ .../contactsource/contactsource.manifest.mf | 1 + src/net/java/sip/communicator/util/DataObject.java | 139 ++++++++++ 10 files changed, 519 insertions(+), 348 deletions(-) create mode 100644 src/net/java/sip/communicator/util/DataObject.java (limited to 'src/net/java/sip/communicator') diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java b/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java index d2ff179..9144727 100644 --- a/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java +++ b/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java @@ -20,7 +20,9 @@ import net.java.sip.communicator.util.*; * * @author Yana Stamcheva */ -public class CallHistorySourceContact implements SourceContact +public class CallHistorySourceContact + extends DataObject + implements SourceContact { /** * The parent CallHistoryContactSource, where this contact is diff --git a/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java b/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java index 1397b36..8b1573d 100644 --- a/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java +++ b/src/net/java/sip/communicator/impl/contactlist/MetaContactImpl.java @@ -23,6 +23,7 @@ import org.jitsi.service.fileaccess.*; * @author Lubomir Marinov */ public class MetaContactImpl + extends DataObject implements MetaContact { /** @@ -121,22 +122,6 @@ public class MetaContactImpl }; /** - * The user-specific key-value associations stored in this instance. - *

- * Like the Widget implementation of Eclipse SWT, the storage type takes - * into account that there are likely to be many - * MetaContactImpl instances and Maps are thus - * likely to impose increased memory use. While an array may very well - * perform worse than a Map 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. - *

- */ - private Object[] data; - - /** * Creates new meta contact with a newly generated meta contact UID. */ MetaContactImpl() @@ -1251,98 +1236,6 @@ public class MetaContactImpl } /** - * Implements {@link MetaContact#getData(Object)}. Gets the user data - * associated with this instance and a specific key. - * - * @param key the key of the user data associated with this instance to be - * retrieved - * @return an Object which represents the value associated with - * this instance and the specified key; null if no - * association with the specified key exists in this instance - * @see MetaContact#getData(Object) - */ - 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 MetaContact#setData(Object, Object)}. Sets a - * user-specific association in this instance in the form of a key-value - * pair. If the specified key is already associated in this - * instance with a value, the existing value is overwritten with the - * specified value. - *

- * The user-defined association created by this method and stored in this - * instance is not serialized by this instance and is thus only meant for - * runtime use. - *

- *

- * The storage of the user data is implementation-specific and is thus not - * guaranteed to be optimized for execution time and memory use. - *

- * - * @param key the key to associate in this instance with the specified value - * @param value the value to be associated in this instance with the - * specified key - * @see MetaContact#setData(Object, Object) - */ - 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; - } - - /** * Updates the capabilities for the given contact. * * @param contact the Contact, which capabilities have changed @@ -1432,24 +1325,6 @@ public class MetaContactImpl } /** - * Determines the index in #data of a specific key. - * - * @param key - * the key to retrieve the index in #data of - * @return the index in #data of the specified key - * if it is contained; -1 if key is not - * contained in #data - */ - 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; - } - - /** * Gets the sync lock for use when modifying {@link #parentGroup}. * * @return the sync lock for use when modifying {@link #parentGroup} diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java index c66bb8f..aa36661 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java @@ -251,6 +251,8 @@ public class SearchFilter false, true); } + else + ExternalContactSource.removeUIContact(sourceContact); } /** diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java index c7fd377..29c9b71 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java @@ -195,7 +195,10 @@ public class TreeContactList sourceUI.getUIGroup(), false); } else + { + ExternalContactSource.removeUIContact(sourceContact); uiContact = null; + } } /** diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java index afa0ee9..d60a214 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java @@ -8,8 +8,18 @@ package net.java.sip.communicator.impl.gui.main.contactlist.contactsource; import javax.swing.*; +import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.main.contactlist.*; import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.customcontactactions.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.swing.*; +import org.osgi.framework.*; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; /** * The ExternalContactSource is the UI abstraction of the @@ -20,6 +30,13 @@ import net.java.sip.communicator.service.contactsource.*; public class ExternalContactSource { /** + * The data key of the SourceContactDescriptor object used to store a + * reference to this object in its corresponding Sourcecontact. + */ + public static final String UI_CONTACT_DATA_KEY + = SourceUIContact.class.getName() + ".uiContactDescriptor"; + + /** * The SourceUIGroup containing all contacts from this source. */ private final SourceUIGroup sourceUIGroup; @@ -30,6 +47,17 @@ public class ExternalContactSource private final ContactSourceService contactSource; /** + * The current custom action contact. + */ + private static SourceContact customActionContact; + + /** + * The list of action buttons for this source contact. + */ + private static Map, SIPCommButton> + customActionButtons; + + /** * Creates an ExternalContactSource based on the given * ContactSourceService. * @@ -75,7 +103,222 @@ public class ExternalContactSource */ public UIContact createUIContact(SourceContact sourceContact) { - return new SourceUIContact(sourceContact, sourceUIGroup); + SourceUIContact descriptor + = new SourceUIContact(sourceContact, sourceUIGroup); + + sourceContact.setData(UI_CONTACT_DATA_KEY, descriptor); + + return descriptor; + } + + /** + * Removes the UIContact from the given sourceContact. + * @param sourceContact the SourceContact, which corresponding UI + * contact we would like to remove + */ + public static void removeUIContact(SourceContact sourceContact) + { + sourceContact.setData(UI_CONTACT_DATA_KEY, null); + } + + /** + * Returns the UIContact corresponding to the given + * SourceContact. + * @param sourceContact the SourceContact, which corresponding UI + * contact we're looking for + * @return the UIContact corresponding to the given + * MetaContact + */ + public static UIContact getUIContact(SourceContact sourceContact) + { + return (UIContact) sourceContact.getData(UI_CONTACT_DATA_KEY); + } + + /** + * Returns all custom action buttons for this meta contact. + * + * @return a list of all custom action buttons for this meta contact + */ + public static Collection getContactCustomActionButtons( + final SourceContact sourceContact) + { + customActionContact = sourceContact; + + if (customActionButtons == null) + initCustomActionButtons(); + + Iterator> customActionsIter + = customActionButtons.keySet().iterator(); + + Collection availableCustomActionButtons + = new LinkedList(); + + while (customActionsIter.hasNext()) + { + ContactAction contactAction + = customActionsIter.next(); + + SIPCommButton actionButton = customActionButtons.get(contactAction); + + if (isContactActionVisible( contactAction, + sourceContact)) + { + availableCustomActionButtons.add(actionButton); + } + } + + return availableCustomActionButtons; + } + + /** + * Indicates if the given ContactAction should be visible for the + * given SourceContact. + * + * @param contactAction the ContactAction to verify + * if the given action should be visible + * @return true if the given ContactAction is visible for + * the given SourceContact, false - otherwise + */ + private static boolean isContactActionVisible( + ContactAction contactAction, + SourceContact contact) + { + if (contactAction.isVisible(contact)) + return true; + + return false; + } + + /** + * Initializes custom action buttons for this contact source. + */ + private static void initCustomActionButtons() + { + customActionButtons = new LinkedHashMap + , SIPCommButton>(); + + CustomContactActionsChangeListener changeListener + = new CustomContactActionsChangeListener(); + + for (CustomContactActionsService ccas + : getContactActionsServices()) + { + ccas.addCustomContactActionsListener(changeListener); + + Iterator> actionIterator + = ccas.getCustomContactActions(); + + while (actionIterator!= null && actionIterator.hasNext()) + { + final ContactAction ca = actionIterator.next(); + + SIPCommButton actionButton = customActionButtons.get(ca); + + if (actionButton == null) + { + actionButton = new SIPCommButton( + new ImageIcon(ca.getIcon()).getImage(), + new ImageIcon(ca.getPressedIcon()).getImage(), + null); + + actionButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + List contactDetails + = SourceUIContact.getContactDetails( + customActionContact); + contactDetails.add( + new SourceUIContact.SourceContactDetail( + customActionContact.getDisplayName(), + customActionContact)); + + UIContactDetailCustomAction contactAction + = new UIContactDetailCustomAction(ca); + + if (contactDetails.size() > 1) + { + ChooseUIContactDetailPopupMenu + detailsPopupMenu + = new ChooseUIContactDetailPopupMenu( + (JButton) e.getSource(), + contactDetails, + contactAction); + + detailsPopupMenu.showPopupMenu(); + } + else if (contactDetails.size() == 1) + { + JButton button = (JButton) e.getSource(); + Point location = new Point(button.getX(), + button.getY() + button.getHeight()); + + SwingUtilities.convertPointToScreen( + location, GuiActivator.getContactList()); + + location.y = location.y + + GuiActivator.getContactList() + .getPathBounds( + GuiActivator.getContactList() + .getSelectionPath()).y; + + contactAction.actionPerformed( + contactDetails.get(0), + location.x, + location.y); + } + } + }); + + customActionButtons.put(ca, actionButton); + } + } + } + } + + /** + * Returns a list of all custom contact action services. + * + * @return a list of all custom contact action services. + */ + @SuppressWarnings ("unchecked") + private static List> + getContactActionsServices() + { + List> + contactActionsServices + = new ArrayList>(); + + ServiceReference[] serRefs = null; + try + { + // get all registered provider factories + serRefs + = GuiActivator.bundleContext.getServiceReferences( + CustomContactActionsService.class.getName(), null); + } + catch (InvalidSyntaxException e) + {} + + if (serRefs != null) + { + for (ServiceReference serRef : serRefs) + { + CustomContactActionsService customActionService + = (CustomContactActionsService) + GuiActivator.bundleContext.getService(serRef); + + if (customActionService.getContactSourceClass() + .equals(SourceContact.class)) + { + contactActionsServices.add( + (CustomContactActionsService) + customActionService); + } + } + } + return contactActionsServices; } /** @@ -209,4 +452,67 @@ public class ExternalContactSource return null; } } + + /** + * Listens for updates on actions and when received update the source contact. + */ + private static class CustomContactActionsChangeListener + implements CustomContactActionsListener + { + /** + * Update for custom action has occurred. + * @param event the event containing the source which was updated. + */ + public void updated(CustomContactActionsEvent event) + { + if(!(event.getSource() instanceof SourceContact)) + return; + + ContactNode contactNode + = getUIContact((SourceContact)event.getSource()).getContactNode(); + + if (contactNode != null) + GuiActivator.getContactList().nodeChanged(contactNode); + } + } + + /** + * An implementation of UIContactDetail for a custom action. + */ + private static class UIContactDetailCustomAction + implements UIContactDetailAction + { + /** + * The contact action. + */ + private final ContactAction contactAction; + + /** + * Creates an instance of UIContactDetailCustomAction. + */ + public UIContactDetailCustomAction( + ContactAction contactAction) + { + this.contactAction = contactAction; + } + + /** + * Performs the action on button click. + */ + public void actionPerformed(UIContactDetail contactDetail, int x, int y) + { + try + { + contactAction.actionPerformed( + (SourceContact) contactDetail.getDescriptor(), x, y); + } + catch (OperationFailedException e) + { + new ErrorDialog(null, + GuiActivator.getResources() + .getI18NString("service.gui.ERROR"), + e.getMessage()); + } + } + } } diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java index 4d9c6b1..0098beb 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java @@ -7,7 +7,6 @@ package net.java.sip.communicator.impl.gui.main.contactlist.contactsource; import java.awt.*; -import java.awt.event.*; import java.util.*; import java.util.List; @@ -55,12 +54,6 @@ public class SourceUIContact private final List searchStrings = new LinkedList(); /** - * The list of action buttons for this source contact. - */ - private Map, SIPCommButton> - customActionButtons; - - /** * Creates an instance of SourceUIContact by specifying the * SourceContact, on which this abstraction is based and the * parent UIGroup. @@ -203,6 +196,18 @@ public class SourceUIContact */ public List getContactDetails() { + return getContactDetails(sourceContact); + } + + /** + * Returns a list of all contained UIContactDetails. + * + * @param sourceContact the source contact we get details from. + * @return a list of all contained UIContactDetails + */ + public static List getContactDetails( + SourceContact sourceContact) + { List resultList = new LinkedList(); @@ -213,7 +218,7 @@ public class SourceUIContact { ContactDetail detail = details.next(); - resultList.add(new SourceContactDetail(detail, null)); + resultList.add(new SourceContactDetail(detail, null, sourceContact)); } return resultList; } @@ -244,7 +249,8 @@ public class SourceUIContact if ((supportedOperationSets != null) && supportedOperationSets.contains(opSetClass)) { - resultList.add(new SourceContactDetail(detail, opSetClass)); + resultList.add(new SourceContactDetail( + detail, opSetClass, sourceContact)); } } return resultList; @@ -277,13 +283,15 @@ public class SourceUIContact public void setContactNode(ContactNode contactNode) { this.contactNode = contactNode; + if (contactNode == null) + ExternalContactSource.removeUIContact(sourceContact); } /** * The implementation of the UIContactDetail interface for the * external source ContactDetails. */ - private class SourceContactDetail extends UIContactDetail + protected static class SourceContactDetail extends UIContactDetail { /** * Creates an instance of SourceContactDetail by specifying @@ -294,7 +302,8 @@ public class SourceUIContact * preferred protocol provider */ public SourceContactDetail( ContactDetail detail, - Class opSetClass) + Class opSetClass, + SourceContact sourceContact) { super( detail.getContactAddress(), detail.getContactAddress(), @@ -324,8 +333,10 @@ public class SourceUIContact * for it. * * @param displayName the display name + * @param sourceContact the source contact */ - public SourceContactDetail(String displayName) + public SourceContactDetail(String displayName, + SourceContact sourceContact) { super( displayName, displayName, @@ -461,218 +472,16 @@ public class SourceUIContact } /** - * Returns all custom action buttons for this meta contact. + * Returns all custom action buttons for this notification contact. * - * @return a list of all custom action buttons for this meta contact + * @return a list of all custom action buttons for this notification contact */ public Collection getContactCustomActionButtons() { - if (customActionButtons == null) - initCustomActionButtons(); - - Iterator> customActionsIter - = customActionButtons.keySet().iterator(); - - Collection availableCustomActionButtons - = new LinkedList(); - - while (customActionsIter.hasNext()) - { - ContactAction contactAction - = customActionsIter.next(); - - SIPCommButton actionButton = customActionButtons.get(contactAction); - - if (isContactActionVisible( contactAction, - sourceContact)) - { - availableCustomActionButtons.add(actionButton); - } - } - - return availableCustomActionButtons; - } - - /** - * Indicates if the given ContactAction should be visible for the - * given SourceContact. - * - * @param contactAction the ContactAction to verify - * if the given action should be visible - * @return true if the given ContactAction is visible for - * the given SourceContact, false - otherwise - */ - private static boolean isContactActionVisible( - ContactAction contactAction, - SourceContact contact) - { - if (contactAction.isVisible(contact)) - return true; - - return false; - } - - /** - * Initializes custom action buttons for this contact source. - */ - private void initCustomActionButtons() - { - customActionButtons = new LinkedHashMap - , SIPCommButton>(); - - for (CustomContactActionsService ccas - : getContactActionsServices()) - { - Iterator> actionIterator - = ccas.getCustomContactActions(); - - while (actionIterator!= null && actionIterator.hasNext()) - { - final ContactAction ca = actionIterator.next(); - - SIPCommButton actionButton = customActionButtons.get(ca); - - if (actionButton == null) - { - actionButton = new SIPCommButton( - new ImageIcon(ca.getIcon()).getImage(), - new ImageIcon(ca.getPressedIcon()).getImage(), - null); - - actionButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - List contactDetails - = getContactDetails(); - contactDetails.add(new SourceContactDetail( - sourceContact.getDisplayName())); - - UIContactDetailCustomAction contactAction - = new UIContactDetailCustomAction(ca); - - if (contactDetails.size() > 1) - { - ChooseUIContactDetailPopupMenu - detailsPopupMenu - = new ChooseUIContactDetailPopupMenu( - (JButton) e.getSource(), - contactDetails, - contactAction); - - detailsPopupMenu.showPopupMenu(); - } - else if (contactDetails.size() == 1) - { - JButton button = (JButton) e.getSource(); - Point location = new Point(button.getX(), - button.getY() + button.getHeight()); - - SwingUtilities.convertPointToScreen( - location, GuiActivator.getContactList()); - - location.y = location.y - + GuiActivator.getContactList() - .getPathBounds( - GuiActivator.getContactList() - .getSelectionPath()).y; - - contactAction.actionPerformed( - contactDetails.get(0), - location.x, - location.y); - } - } - }); - - customActionButtons.put(ca, actionButton); - } - } - } - } - - /** - * Returns a list of all custom contact action services. - * - * @return a list of all custom contact action services. - */ - @SuppressWarnings ("unchecked") - private static List> - getContactActionsServices() - { - List> - contactActionsServices - = new ArrayList>(); - - ServiceReference[] serRefs = null; - try - { - // get all registered provider factories - serRefs - = GuiActivator.bundleContext.getServiceReferences( - CustomContactActionsService.class.getName(), null); - } - catch (InvalidSyntaxException e) - {} - - if (serRefs != null) - { - for (ServiceReference serRef : serRefs) - { - CustomContactActionsService customActionService - = (CustomContactActionsService) - GuiActivator.bundleContext.getService(serRef); - - if (customActionService.getContactSourceClass() - .equals(SourceContact.class)) - { - contactActionsServices.add( - (CustomContactActionsService) - customActionService); - } - } - } - return contactActionsServices; - } + if (sourceContact != null) + return ExternalContactSource + .getContactCustomActionButtons(sourceContact); - /** - * An implementation of UIContactDetail for a custom action. - */ - private static class UIContactDetailCustomAction - implements UIContactDetailAction - { - /** - * The contact action. - */ - private final ContactAction contactAction; - - /** - * Creates an instance of UIContactDetailCustomAction. - */ - public UIContactDetailCustomAction( - ContactAction contactAction) - { - this.contactAction = contactAction; - } - - /** - * Performs the action on button click. - */ - public void actionPerformed(UIContactDetail contactDetail, int x, int y) - { - try - { - contactAction.actionPerformed( - (SourceContact) contactDetail.getDescriptor(), x, y); - } - catch (OperationFailedException e) - { - new ErrorDialog(null, - GuiActivator.getResources() - .getI18NString("service.gui.ERROR"), - e.getMessage()); - } - } + return null; } } diff --git a/src/net/java/sip/communicator/service/contactsource/GenericSourceContact.java b/src/net/java/sip/communicator/service/contactsource/GenericSourceContact.java index cf84e06..ab382ee 100644 --- a/src/net/java/sip/communicator/service/contactsource/GenericSourceContact.java +++ b/src/net/java/sip/communicator/service/contactsource/GenericSourceContact.java @@ -9,6 +9,7 @@ package net.java.sip.communicator.service.contactsource; import java.util.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; /** * Implements a generic SourceContact for the purposes of the support @@ -17,6 +18,7 @@ import net.java.sip.communicator.service.protocol.*; * @author Lyubomir Marinov */ public class GenericSourceContact + extends DataObject implements SourceContact { /** diff --git a/src/net/java/sip/communicator/service/contactsource/SourceContact.java b/src/net/java/sip/communicator/service/contactsource/SourceContact.java index 9d4536e..d013d7f 100644 --- a/src/net/java/sip/communicator/service/contactsource/SourceContact.java +++ b/src/net/java/sip/communicator/service/contactsource/SourceContact.java @@ -91,4 +91,36 @@ public interface SourceContact * @return the byte array of the image or null if no image is available */ public byte[] getImage(); + + /** + * Gets the user data associated with this instance and a specific key. + * + * @param key the key of the user data associated with this instance to be + * retrieved + * @return an Object which represents the value associated with + * this instance and the specified key; null if no + * association with the specified key exists in this instance + */ + public Object getData(Object key); + + /** + * Sets a user-specific association in this instance in the form of a + * key-value pair. If the specified key is already associated in + * this instance with a value, the existing value is overwritten with the + * specified value. + *

+ * The user-defined association created by this method and stored in this + * instance is not serialized by this instance and is thus only meant for + * runtime use. + *

+ *

+ * The storage of the user data is implementation-specific and is thus not + * guaranteed to be optimized for execution time and memory use. + *

+ * + * @param key the key to associate in this instance with the specified value + * @param value the value to be associated in this instance with the + * specified key + */ + public void setData(Object key, Object value); } diff --git a/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf b/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf index 7b06df7..4ae0097 100644 --- a/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf +++ b/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf @@ -4,5 +4,6 @@ Bundle-Vendor: jitsi.org Bundle-Version: 0.0.1 System-Bundle: yes Import-Package: org.osgi.framework, + net.java.sip.communicator.util, net.java.sip.communicator.service.protocol Export-Package: net.java.sip.communicator.service.contactsource diff --git a/src/net/java/sip/communicator/util/DataObject.java b/src/net/java/sip/communicator/util/DataObject.java new file mode 100644 index 0000000..21c5903 --- /dev/null +++ b/src/net/java/sip/communicator/util/DataObject.java @@ -0,0 +1,139 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.util; + +/** + * Object which can store user specific key-values. + * + * @author Damian Minkov + */ +public class DataObject +{ + /** + * The user-specific key-value associations stored in this instance. + *

+ * Like the Widget implementation of Eclipse SWT, the storage type takes + * into account that there are likely to be many + * DataObject instances and Maps are thus + * likely to impose increased memory use. While an array may very well + * perform worse than a Map 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. + *

+ */ + private Object[] data; + + /** + * Gets the user data + * associated with this instance and a specific key. + * + * @param key the key of the user data associated with this instance to be + * retrieved + * @return an Object which represents the value associated with + * this instance and the specified key; null if no + * association with the specified key exists in this instance + */ + public Object getData(Object key) + { + if (key == null) + throw new NullPointerException("key"); + + int index = dataIndexOf(key); + + return (index == -1) ? null : data[index + 1]; + } + + /** + * Determines the index in #data of a specific key. + * + * @param key + * the key to retrieve the index in #data of + * @return the index in #data of the specified key + * if it is contained; -1 if key is not + * contained in #data + */ + 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; + } + + /** + * Sets a + * user-specific association in this instance in the form of a key-value + * pair. If the specified key is already associated in this + * instance with a value, the existing value is overwritten with the + * specified value. + *

+ * The user-defined association created by this method and stored in this + * instance is not serialized by this instance and is thus only meant for + * runtime use. + *

+ *

+ * The storage of the user data is implementation-specific and is thus not + * guaranteed to be optimized for execution time and memory use. + *

+ * + * @param key the key to associate in this instance with the specified value + * @param value the value to be associated in this instance with the + * specified key + */ + 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; + } +} -- cgit v1.1