added foundations of icq support

cusax-fix
Emil Ivov 20 years ago
parent 2dde9f4937
commit fbeb769df3

@ -0,0 +1,23 @@
package net.java.sip.communicator.impl.protocol.icq;
import net.java.sip.communicator.service.protocol.*;
/**
* The ICQ implementation of the service.protocol.ContactGroup interface. There
* are two types of groups possible here. <code>RootContactGroupIcqImpl</code>
* which is the root node of the ContactList itself and
* <code>ContactGroupIcqImpl</code> which represents standard icq groups. The
* reason for having those 2 is that generally, ICQ groups may not contain
* subgroups. A contact list on the other hand may not directly contain buddies.
*
*
* The reason for having an abstract class is only - being able to esily
* recognize our own (ICQ) contacts.
* @author Emil Ivov
*/
public abstract class AbstractContactGroupIcqImpl
implements ContactGroup
{
}

@ -0,0 +1,156 @@
/*
* 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.protocol.icq;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.configuration.*;
/**
* The ICQ implementation of the ProtocolAccountManager.
* @author Emil Ivov
*/
public class AccountManagerIcqImpl
implements AccountManager
{
private Hashtable registeredAccounts = new Hashtable();
/**
* Creates an instance of the IcqAccountManager.
*/
protected void IcqAccountManager()
{
}
/**
* Returns a copy of the list containing all accounts currently
* registered in this protocol provider.
*
* @return a copy of the llist containing all accounts currently installed
* in the protocol provider.
*/
public ArrayList getRegisteredAcounts()
{
return new ArrayList(registeredAccounts.keySet());
}
/**
* Returns the ServiceReference for the protocol provider corresponding to
* the specified accountID or null if the accountID is unknown.
* @param accountID the accountID of the protocol provider we'd like to get
* @return a ServiceReference object to the protocol provider with the
* specified account id and null if the account id is unknwon to the
* account manager.
*/
public ServiceReference getProviderForAccount(AccountID accountID)
{
ServiceRegistration registration
= (ServiceRegistration)registeredAccounts.get(accountID);
return (registration == null )
? null
: registration.getReference();
}
/**
* Initializaed and creates an accoung corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* <code>context</code> BundleContext parameter.
*
* @param context the BundleContext parameter where the newly created
* ProtocolProviderService would have to be registered.
* @param accountIDStr the user identifier for the new account
* @param accountProperties a set of protocol (or implementation)
* specific properties defining the new account.
* @return the AccountID of the newly created account
*/
public AccountID installAccount( BundleContext context,
String accountIDStr,
Map accountProperties)
{
if(context == null)
throw new NullPointerException("The specified BundleContext was null");
if(accountIDStr == null)
throw new NullPointerException("The specified AccountID was null");
if(accountIDStr == null)
throw new NullPointerException("The specified property map was null");
AccountID accountID = new IcqAccountID(accountIDStr, accountProperties);
//make sure we haven't seen this account id before.
if( registeredAccounts.containsKey(accountID) )
throw new IllegalStateException(
"An account for id " + accountIDStr + " was already installed!");
Hashtable properties = new Hashtable();
properties.put(
AccountManager.PROTOCOL_PROPERTY_NAME, ProtocolNames.ICQ);
properties.put(
AccountManager.ACCOUNT_ID_PROPERTY_NAME, accountIDStr);
ProtocolProviderServiceIcqImpl icqProtocolProvider
= new ProtocolProviderServiceIcqImpl();
icqProtocolProvider.initialize(accountIDStr, accountProperties);
ServiceRegistration registration
= context.registerService( ProtocolProviderService.class.getName(),
icqProtocolProvider,
properties);
registeredAccounts.put(accountID, registration);
return accountID;
}
/**
* Removes the specified account from the list of accounts that this
* account manager is handling. If the specified accountID is unknown to the
* AccountManager, the call has no effect and false is returned. This method
* is persistent in nature and once called the account corresponding to the
* specified ID will not be loaded during future runs of the project.
*
* @param accountID the ID of the account to remove.
* @return true if an account with the specified ID existed and was removed
* and false otherwise.
*/
public boolean uninstallAccount(AccountID accountID)
{
ServiceRegistration registration
= (ServiceRegistration)registeredAccounts.remove(accountID);
if(registration == null)
return false;
//kill the service
registration.unregister();
return true;
}
/**
* Loads all previously installed accounts that were stored in the
* configuration service. The method is only loading accounts the first
* time it gets called.
*
* @param context the context where icq protocol providers shouold be
* registered
* @param configurationService ConfigurationService
*/
void loadStoredAccounts(BundleContext context,
ConfigurationService configurationService)
{
/** @todo implement loadStoredAccounts() */
//make sure we haven't already done so.
//load all accounts stored in the configuration service
}
}

@ -0,0 +1,66 @@
package net.java.sip.communicator.impl.protocol.icq;
import org.osgi.framework.*;
import net.java.sip.communicator.service.protocol.*;
import java.util.Hashtable;
import net.java.sip.communicator.service.configuration.*;
/**
* Loads the ICQ account manager and registers it with service in the OSGI
* bundle context.
*
* @author Emil Ivov
*/
public class Activator
implements BundleActivator
{
ServiceRegistration icqAccManRegistration = null;
/**
* Called when this bundle is started so the Framework can perform the
* bundle-specific activities necessary to start this bundle.
*
* @param context The execution context of the bundle being started.
* @throws Exception If this method throws an exception, this bundle is
* marked as stopped and the Framework will remove this bundle's
* listeners, unregister all services registered by this bundle, and
* release all services used by this bundle.
*/
public void start(BundleContext context) throws Exception
{
Hashtable hashtable = new Hashtable();
hashtable.put(AccountManager.PROTOCOL_PROPERTY_NAME, "ICQ");
AccountManagerIcqImpl icqAccountManager =
new AccountManagerIcqImpl();
ServiceReference confReference
= context.getServiceReference(ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService)context.getService(confReference);
//load all icq providers
icqAccountManager.loadStoredAccounts(context, configurationService);
//reg the icq account man.
icqAccManRegistration = context.registerService(
AccountManager.class.getName(),
icqAccountManager,
hashtable);
}
/**
* Called when this bundle is stopped so the Framework can perform the
* bundle-specific activities necessary to stop the bundle.
*
* @param context The execution context of the bundle being stopped.
* @throws Exception If this method throws an exception, the bundle is
* still marked as stopped, and the Framework will remove the bundle's
* listeners, unregister all services registered by the bundle, and
* release all services used by the bundle.
*/
public void stop(BundleContext context) throws Exception
{
icqAccManRegistration.unregister();
}
}

@ -0,0 +1,405 @@
/*
* 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.protocol.icq;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.kano.joustsim.oscar.oscar.service.ssi.*;
import net.kano.joustsim.*;
/**
* The ICQ implementation of the ContactGroup interface. Intances of this class
* (contrary to <code>RootContactGroupIcqImpl</code>) may only contain buddies
* and cannot have sub groups. Note that instances of this class only use the
* corresponding joust sim source group for reading their names and only
* initially fill their <code>buddies</code> <code>java.util.List</code> with
* the ContactIcqImpl objects corresponding to those contained in the source
* group at the moment it is being created. They would, however, never try to
* sync or update their contents ulteriorly. This would have to be done through
* the addContact()/removeContact() methods.
*
* @author Emil Ivov
*/
public class ContactGroupIcqImpl
extends AbstractContactGroupIcqImpl
{
private List buddies = new LinkedList();
/**
* The JoustSIM Group corresponding to this contact group.
*/
private MutableGroup joustSimSourceGroup = null;
/**
* a list that would always remain empty. We only use it so that we're able
* to extract empty iterators
*/
private List dummyGroupsList = new LinkedList();
/**
* A variable that we use as a means of detecting changes in the name
* of this group.
*/
private String nameCopy = null;
/**
* Creates an ICQ group using the specified <code>joustSimGroup</code> as
* a source. The newly created group will always return the name of the
* underlying joustSimGroup and would thus automatically adapt to changes.
* It would, however, not receive or try to poll for modifications of the
* buddies it contains and would therefore have to be updated manually by
* ServerStoredContactListImpl.
* <p>
* Note that we MUST NOT use the list of buddies obtained through the
* getBuddiesCopy() of the joustSimGroup arg as we'd later need to be able
* to directly compare ( == ) instances of buddies we've stored and others
* that are returned by the framework.
* <p>
* @param joustSimGroup the JoustSIM Group correspoinding to the group
* @param groupMembers the group members that we should add to the group.
*
* we're creating.
*/
ContactGroupIcqImpl(MutableGroup joustSimGroup, List groupMembers)
{
this.joustSimSourceGroup = joustSimGroup;
//store a copy of the name now so that we can detect changes in the
//name of the underlying joustSimSourceGroup
initNameCopy();
//do not use the buddies in the joustSimGroup since we want to keep
//their real addresses and we can only get a list of copies from the
//group itself.
for (int i = 0; i < groupMembers.size(); i++)
{
addContact( new ContactIcqImpl((Buddy)groupMembers.get(i)) );
}
}
/**
* Returns the number of <code>Contact</code> members of this
* <code>ContactGroup</code>
*
* @return an int indicating the number of <code>Contact</code>s,
* members of this <code>ContactGroup</code>.
*/
public int countContacts()
{
return buddies.size();
}
/**
* Adds the specified contact at the specified position.
* @param contact the new contact to add to this group
* @param index the position where the new contact should be added.
*/
void addContact(int index, ContactIcqImpl contact)
{
buddies.add(index, contact);
}
/**
* Adds the specified contact to the end of this group.
* @param contact the new contact to add to this group
*/
void addContact(ContactIcqImpl contact)
{
addContact(countContacts(), contact);
}
/**
* Removes the specified contact from this contact group
* @param contact the contact to remove.
*/
void removeContact(ContactIcqImpl contact)
{
removeContact(buddies.indexOf(contact));
}
/**
* Removes the contact with the specified index.
* @param index the index of the cntact to remove
*/
void removeContact(int index)
{
buddies.remove(index);
}
/**
* Removes all buddies in this group and reinsterts them as specified
* by the <code>newOrder</code> param. Contacts not contained in the
* newOrder list are left at the end of this group.
*
* @param newOrder a list containing all contacts in the order that is
* to be applied.
*
*/
void reorderContacts(List newOrder)
{
buddies.removeAll(newOrder);
buddies.addAll(0, newOrder);
}
/**
* Returns an Iterator over all contacts, member of this
* <code>ContactGroup</code>.
*
* @return a java.util.Iterator over all contacts inside this
* <code>ContactGroup</code>. In case the group doesn't contain any
* memebers it will return an empty iterator.
*/
public Iterator contacts()
{
return buddies.iterator();
}
/**
* Returns the <code>Contact</code> with the specified index.
*
* @param index the index of the <code>Contact</code> to return.
* @return the <code>Contact</code> with the specified index.
*/
public Contact getContact(int index)
{
return (ContactIcqImpl) buddies.get(index);
}
/**
* Returns the <code>Contact</code> with the specified address or
* identifier.
* @param id the addres or identifier of the <code>Contact</code> we are
* looking for.
* @return the <code>Contact</code> with the specified id or address.
*/
public Contact getContact(String id)
{
return this.findContact(id);
}
/**
* Returns the name of this group.
* @return a String containing the name of this group.
*/
public String getGroupName()
{
return joustSimSourceGroup.getName();
}
/**
* Determines whether the group may contain subgroups or not.
*
* @return always false since only the root group may contain subgroups.
*/
public boolean canContainSubgroups()
{
return false;
}
/**
* Returns the subgroup with the specified index (i.e. always null since
* this group may not contain subgroups).
*
* @param index the index of the <code>ContactGroup</code> to retrieve.
* @return always null
*/
public ContactGroup getGroup(int index)
{
return null;
}
/**
* Returns the subgroup with the specified name.
* @param groupName the name of the <code>ContactGroup</code> to retrieve.
* @return the <code>ContactGroup</code> with the specified index.
*/
public ContactGroup getGroup(String groupName)
{
return null;
}
/**
* Returns an empty iterator. Subgroups may only be present in the root
* group.
*
* @return an empty iterator
*/
public Iterator subGroups()
{
return dummyGroupsList.iterator();
}
/**
* Returns the number of subgroups contained by this group, which is
* always 0 since sub groups in the icq protocol may only be contained
* by the root group - <code>RootContactGroupIcqImpl</code>.
* @return a 0 int.
*/
public int countSubGroups()
{
return 0;
}
/**
* Returns a hash code value for the object, which is actually the hashcode
* value of the groupname.
*
* @return a hash code value for this ContactGroup.
*/
public int hashCode()
{
return getGroupName().hashCode();
}
/**
* Returns the JoustSIM group that this class is encapsulating.
* @return the JoustSIM group corresponding to this SC group.
*/
MutableGroup getJoustSimSourceGroup()
{
return joustSimSourceGroup;
}
/**
* Indicates whether some other object is "equal to" this group. A group is
* considered equal to another group if it hase the same sets of (equal)
* contacts.
* <p>
*
* @param obj the reference object with which to compare.
* @return <code>true</code> if this object is the same as the obj
* argument; <code>false</code> otherwise.
*/
public boolean equals(Object obj)
{
if( obj == this )
return true;
if (obj == null
|| !(obj instanceof ContactGroupIcqImpl) )
return false;
if(!((ContactGroup)obj).getGroupName().equals(getGroupName()))
return false;
//since ICQ does not support having two groups with the same name
// at this point we could bravely state that the groups are the same
// and not bother to compare buddies. (gotta check that though)
return true;
}
/**
* Returns a string representation of this group, in the form
* IcqGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}.
* @return a String representation of the object.
*/
public String toString()
{
StringBuffer buff = new StringBuffer("IcqGroup.");
buff.append(getGroupName());
buff.append(", childContacts="+countContacts()+":[");
Iterator contacts = contacts();
while (contacts.hasNext())
{
ContactIcqImpl contact = (ContactIcqImpl) contacts.next();
buff.append(contact.toString());
if(contacts.hasNext())
buff.append(", ");
}
return buff.append("]").toString();
}
/**
* Returns the icq contact encapsulating the specified joustSim buddy or null
* if no such buddy was found.
*
* @param joustSimBuddy the buddy whose encapsulating contact we're looking
* for.
* @return the <code>ContactIcqImpl</code> corresponding to the specified
* joustSimBuddy or null if no such contact was found.
*/
ContactIcqImpl findContact(Buddy joustSimBuddy)
{
Iterator contacts = contacts();
while (contacts.hasNext())
{
ContactIcqImpl item = (ContactIcqImpl) contacts.next();
if(item.getJoustSimBuddy() == joustSimBuddy)
return item;
}
return null;
}
/**
* Returns the index of icq contact encapsulating the specified joustSim
* buddy or -1 if no such buddy was found.
*
* @param joustSimBuddy the buddy whose encapsulating contact's index we're
* looking for.
* @return the index of the contact corresponding to the specified
* joustSimBuddy or null if no such contact was found.
*/
int findContactIndex(Buddy joustSimBuddy)
{
Iterator contacts = contacts();
int i = 0;
while (contacts.hasNext())
{
ContactIcqImpl item = (ContactIcqImpl) contacts.next();
if(item.getJoustSimBuddy() == joustSimBuddy)
return i;
i++;
}
return -1;
}
/**
* Returns the icq contact encapsulating with the spcieified screen name or
* null if no such contact was found.
*
* @param screenName the screenName (or icq UIN) for the contact we're
* looking for.
* @return the <code>ContactIcqImpl</code> corresponding to the specified
* screnname or null if no such contact existed.
*/
ContactIcqImpl findContact(String screenName)
{
Iterator contacts = contacts();
while (contacts.hasNext())
{
ContactIcqImpl item = (ContactIcqImpl) contacts.next();
if(item.getJoustSimBuddy().getScreenname()
.equals(new Screenname(screenName)))
return item;
}
return null;
}
/**
* Sets the name copy field that we use as a means of detecing changes in
* the group name.
*/
void initNameCopy()
{
this.nameCopy = getGroupName();
}
/**
* Returns the name of the group as it was at the last call of initNameCopy.
* @return a String containing a copy of the name of this group as it was
* last time when we called <code>initNameCopy</code>.
*/
String getNameCopy()
{
return this.nameCopy;
}
}

@ -0,0 +1,156 @@
package net.java.sip.communicator.impl.protocol.icq;
import net.java.sip.communicator.service.protocol.*;
import net.kano.joscar.snaccmd.*;
import net.kano.joustsim.oscar.oscar.service.ssi.*;
import net.java.sip.communicator.service.protocol.icqconstants.*;
/**
* The ICQ implementation of the service.protocol.Contact interface.
* @author Emil Ivov
*/
public class ContactIcqImpl
implements Contact
{
Buddy joustSimBuddy = null;
private boolean isLocal = false;
private byte[] image = null;
private PresenceStatus icqStatus = IcqStatusEnum.OFFLINE;
/**
* Creates an IcqContactImpl
* @param buddy the JoustSIM object that we will be encapsulating.
* @param isLocal specifies whether this is the representation of the local
* contact (i.e. the user we are using to sign on icq)
*/
ContactIcqImpl(Buddy buddy, boolean isLocal)
{
this.joustSimBuddy = buddy;
this.isLocal = isLocal;
}
/**
* Creates an IcqContactImpl for a non local contact
* @param buddy FullUserInfo
*/
ContactIcqImpl(Buddy buddy)
{
this(buddy, false );
}
/**
* Returns the ICQ uin (or AIM screen name)of this contact
* @return the ICQ uin (or AIM screen name)of this contact
*/
public String getUIN()
{
return joustSimBuddy.getScreenname().getFormatted();
}
/**
* Returns the ICQ uin (or AIM screen name)of this contact
* @return the ICQ uin (or AIM screen name)of this contact
*/
public String getAddress(){
return getUIN();
}
/**
* Determines whether or not this Contact instance represents the user used
* by this protocol provider to connect to the service.
*
* @return true if this Contact represents us (the local user) and false
* otherwise.
*/
public boolean isLocal()
{
return isLocal;
}
public byte[] getImage()
{
return image;
}
/**
* Returns a hashCode for this contact. The returned hashcode is actually
* that of the Contact's UIN
* @return the hashcode of this Contact
*/
public int hashCode()
{
return getUIN().hashCode();
}
/**
* Indicates whether some other object is "equal to" this one.
* <p>
*
* @param obj the reference object with which to compare.
* @return <code>true</code> if this object is the same as the obj
* argument; <code>false</code> otherwise.
*/
public boolean equals(Object obj)
{
if (obj == null
|| !(obj instanceof ContactIcqImpl)
|| !((ContactIcqImpl)obj).getUIN().equals(getUIN()))
return false;
return true;
}
/**
* Returns the joust sim buddy that this Contact is encapsulating.
* @return Buddy
*/
Buddy getJoustSimBuddy()
{
return joustSimBuddy;
}
/**
* Returns a string representation of this contact, containing most of its
* representative details.
*
* @return a string representation of this contact.
*/
public String toString()
{
StringBuffer buff = new StringBuffer("IcqContact[ uin=");
buff.append(getAddress()).append(", alias=")
.append(getJoustSimBuddy().getAlias()).append("]");
return buff.toString();
}
/**
* Sets the status that this contact is currently in. The method is to
* only be called as a result of a status update received from the AIM
* server.
*
* @param status the IcqStatusEnum that this contact is currently in.
*/
void updatePresenceStatus(PresenceStatus status)
{
this.icqStatus = status;
}
/**
* Returns the status of the contact as per the last status update we've
* received for it. Note that this method is not to perform any network
* operations and will simply return the status received in the last
* status update message. If you want a reliable way of retrieving someone's
* status, you should use the <code>queryContactStatus()</code> method in
* <code>OperationSetPresence</code>.
* @return the PresenceStatus that we've received in the last status update
* pertaining to this contact.
*/
public PresenceStatus getPresenceStatus()
{
return icqStatus;
}
}

@ -0,0 +1,19 @@
package net.java.sip.communicator.impl.protocol.icq;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The ICQ implementation of a sip-communicator AccountID
*
* @author Emil Ivov
*/
public class IcqAccountID
extends AccountID
{
IcqAccountID(String accountID, Map accountProperties)
{
super(accountID, accountProperties);
}
}

@ -0,0 +1,429 @@
/*
* 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.protocol.icq;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.kano.joustsim.oscar.*;
import net.kano.joustsim.*;
import net.kano.joustsim.oscar.oscar.service.icbm.*;
import net.java.sip.communicator.util.*;
/**
* An implementation of the protocol provider service over the AIM/ICQ protocol
*
* @author Emil Ivov
*/
public class ProtocolProviderServiceIcqImpl
implements ProtocolProviderService
{
private static final Logger logger =
Logger.getLogger(ProtocolProviderServiceIcqImpl.class);
/**
* The hashtable with the operation sets that we support locally.
*/
private Hashtable supportedOperationSets = new Hashtable();
private DefaultAppSession session = null;
private AimSession aimSession = null;
private AimConnection aimConnection = null;
private IcbmService icbmService = null;
/**
* Listener that catches all connection events originating from joscar
* during connection to icq.
*/
private AimConnStateListener aimConnStateListener = null;
/**
* Listener that catches all incoming and outgoing chat events generated
* by joscar.
*/
private AimIcbmListener aimIcbmListener = null;
/**
* indicates whether or not the provider is initialized and ready for use.
*/
private boolean isInitialized = false;
private Object initializationLock = new Object();
/**
* A list of all listeners registered for
* <code>RegistrationStateChangeEvent</code>s.
*/
private List registrationListeners = new ArrayList();
/**
* Returns the state of the registration of this protocol provider
* @return the <code>RegistrationState</code> that this provider is
* currently in or null in case it is in a unknown state.
*/
public RegistrationState getRegistrationState()
{
State connState = getAimConnection().getState();
return joustSimStateToRegistrationState(connState);
}
/**
* Converts the specified joust sim connection state to a corresponding
* RegistrationState.
* @param joustSimConnState the joust sim connection state.
* @return a RegistrationState corresponding best to the specified
* joustSimState.
*/
private RegistrationState joustSimStateToRegistrationState(
State joustSimConnState)
{
if(joustSimConnState == State.ONLINE)
return RegistrationState.REGISTERED;
else if (joustSimConnState == State.CONNECTING)
return RegistrationState.REGISTERING;
else if( joustSimConnState == State.AUTHORIZING)
return RegistrationState.AUTHENTICATING;
else if (joustSimConnState == State.CONNECTINGAUTH)
return RegistrationState.AUTHENTICATING;
else if (joustSimConnState == State.SIGNINGON)
return RegistrationState.REGISTERING;
else if (joustSimConnState == State.DISCONNECTED
|| joustSimConnState == State.NOTCONNECTED)
return RegistrationState.UNREGISTERED;
else if (joustSimConnState == State.FAILED)
return RegistrationState.CONNECTION_FAILED;
else{
logger.warn("Unknown state " + joustSimConnState
+ ". Defaulting to " + RegistrationState.UNREGISTERED);
return RegistrationState.UNREGISTERED;
}
}
/**
* Starts the registration process. Connection details such as
* registration server, user name/number are provided through the
* configuration service through implementation specific properties.
*
* @param authority the security authority that will be used for resolving
* any security challenges that may be returned during the
* registration or at any moment while wer're registered.
*
*/
public void register(SecurityAuthority authority)
{
aimConnection.connect();
}
/**
* Ends the registration of this protocol provider with the service.
*/
public void unregister()
{
aimConnection.disconnect(true);
}
/**
* Indicates whether or not this provider is signed on the icq service
* @return true if the provider is currently signed on (and hence online)
* and false otherwise.
*/
public boolean isRegistered()
{
return getRegistrationState().equals(RegistrationState.REGISTERED);
}
/**
* Returns the short name of the protocol that the implementation of this
* provider is based upon (like SIP, Jabber, ICQ/AIM, or others for
* example).
*
* @return a String containing the short name of the protocol this
* service is taking care of.
*/
public String getProtocolName()
{
return ProtocolNames.ICQ;
}
/**
* Returns an array containing all operation sets supported by the
* current implementation.
*
* @return an array of OperationSet-s supported by this protocol
* provider implementation.
*/
public Map getSupportedOperationSets()
{
return supportedOperationSets;
}
/**
* Initialized the service implementation, and puts it in a sate where it
* could interoperate with other services. It is strongly recomended that
* properties in this Map be mapped to property names as specified by
* <code>AccountProperties</code>.
*
* @param screenname the account id/uin/screenname of the account that we're
* about to create
* @param initializationProperties all properties needed fo initializing the
* account.
*
* @see net.java.sip.communicator.service.protocol.AccountProperties
*/
protected void initialize(String screenname, Map initializationProperties)
{
synchronized(initializationLock)
{
//extract the necessary properties and validate them
String password =
(String)initializationProperties.get(AccountProperties.PASSWORD);
//init the necessary objects
session = new DefaultAppSession();
aimSession = session.openAimSession(new Screenname(screenname));
aimConnection = aimSession.openConnection(
new AimConnectionProperties(new Screenname(screenname),
password));
aimConnStateListener = new AimConnStateListener();
aimConnection.addStateListener(aimConnStateListener);
aimIcbmListener = new AimIcbmListener();
//initialize all the supported operation sets
OperationSetPersistentPresence persistentPresence =
new OperationSetPersistentPresenceIcqImpl(this, screenname);
supportedOperationSets.put(
OperationSetPersistentPresence.class.getName(),
persistentPresence);
isInitialized = true;
}
}
/**
* Makes the service implementation close all open sockets and release
* any resources that it might have taken and prepare for
* shutdown/garbage collection.
*/
public void shutdown()
{
/** @todo is there anything else to add here? */
synchronized(initializationLock){
icbmService = null;
session = null;
aimSession = null;
aimConnection = null;
aimConnStateListener = null;
aimIcbmListener = null;
isInitialized = false;
}
}
/**
* Returns true if the provider service implementation is initialized and
* ready for use by other services, and false otherwise.
*
* @return true if the provider is initialized and ready for use and false
* otherwise
*/
public boolean isInitialized()
{
return isInitialized;
}
/**
* Removes the specified registration state change listener so that it does
* not receive any further notifications upon changes of the
* RegistrationState of this provider.
*
* @param listener the listener to register for
* <code>RegistrationStateChangeEvent</code>s.
*/
public void removeRegistrationStateChangeListener(
RegistrationStateChangeListener listener)
{
registrationListeners.remove(listener);
}
/**
* Registers the specified listener with this provider so that it would
* receive notifications on changes of its state or other properties such
* as its local address and display name.
*
* @param listener the listener to register.
*/
public void addRegistrationStateChangeListener(RegistrationStateChangeListener listener)
{
registrationListeners.add(listener);
}
/**
* Creates a RegistrationStateChange event corresponding to the specified
* old and new joust sim states and notifies all currently registered
* listeners.
*
* @param oldJoustSimState the state that the joust sim connection had
* before the change occurred
* @param newJoustSimState the state that the underlying joust sim
* connection is currently in.
*/
private void fireRegistrationStateChanged( State oldJoustSimState,
State newJoustSimState)
{
RegistrationState oldRegistrationState
= joustSimStateToRegistrationState(oldJoustSimState);
RegistrationState newRegistrationState
= joustSimStateToRegistrationState(newJoustSimState);
RegistrationStateChangeEvent event =
new RegistrationStateChangeEvent(
this, oldRegistrationState, newRegistrationState);
logger.debug("Dispatching " + event + " to "
+ registrationListeners.size()+ " listeners.");
for (int i = 0; i < registrationListeners.size(); i++)
{
RegistrationStateChangeListener listener
= (RegistrationStateChangeListener)registrationListeners.get(i);
listener.registrationStateChanged(event);
}
logger.trace("Done.");
}
/**
* This class handles connection state events that have originated in this
* provider's aim connection. Events are acted upon accordingly and,
* if necessary, forwarded to registered listeners (asynchronously).
*/
private class AimConnStateListener implements StateListener
{
public void handleStateChange(StateEvent event)
{
State newState = event.getNewState();
State oldState = event.getOldState();
AimConnection conn = event.getAimConnection();
logger.debug("ICQ protocol provider " + getProtocolName()
+ "changed registration status from "
+ oldState + " to " + newState);
if (newState == State.ONLINE)
{
icbmService = conn.getIcbmService();
icbmService.addIcbmListener(aimIcbmListener);
}
else if (newState == State.DISCONNECTED
|| newState == State.FAILED)
{
logger.debug("The aim Connection was disconnected!");
}
//now tell all interested parties about what happened.
fireRegistrationStateChanged(oldState, newState);
}
}
/**
* Returns the <code>AimSession</code> opened by this provider.
* @return a reference to the <code>AimSession</code> that this provider
* last opened.
*/
protected AimSession getAimSession()
{
return aimSession;
}
/**
* Returns the <code>AimConnection</code>opened by this provider
* @return a reference to the <code>AimConnection</code> last opened by this provider.
*/
protected AimConnection getAimConnection()
{
return aimConnection;
}
public class AimIcbmListener implements IcbmListener
{
public void newConversation(IcbmService service, Conversation conv)
{
System.out.println("Received a new conversation event");
conv.addConversationListener(new AimConversationListener());
}
public void buddyInfoUpdated(IcbmService service, Screenname buddy,
IcbmBuddyInfo info)
{
System.out.println("Got a BuddINFO event");
}
}
public class AimConversationListener implements ConversationListener{
public void sentOtherEvent(Conversation conversation,
ConversationEventInfo event)
{
System.out.println("reveived ConversationEventInfo:" + event);
}
// This may be called without ever calling conversationOpened
public void conversationClosed(Conversation c)
{
System.out.println("conversation closed");
}
public void gotOtherEvent(Conversation conversation,
ConversationEventInfo event)
{
System.out.println("goet other event");
if(event instanceof TypingInfo)
{
TypingInfo ti = (TypingInfo)event;
System.out.println("got typing info and state is: " + ti.getTypingState());
}
else if (event instanceof MessageInfo)
{
MessageInfo ti = (MessageInfo)event;
System.out.println("got message info for msg: " + ti.getMessage());
}
}
public void canSendMessageChanged(Conversation c, boolean canSend)
{
System.out.println("can send message event");
}
// This may never be called
public void conversationOpened(Conversation c)
{
System.out.println("conversation opened event");
}
// This may be called after conversationClosed is called
public void sentMessage(Conversation c, MessageInfo minfo)
{
System.out.println("sent message event");
}
// This may be called after conversationClosed is called.
public void gotMessage(Conversation c, MessageInfo minfo)
{
System.out.println("got message event" + minfo.getMessage().getMessageBody());
}
}
}

@ -0,0 +1,222 @@
package net.java.sip.communicator.impl.protocol.icq;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* A dummy ContactGroup implementation representing the ContactList root for
* ICQ contact lists.
* @author Emil Ivov
*/
public class RootContactGroupIcqImpl
extends AbstractContactGroupIcqImpl
{
private String ROOT_CONTACT_GROUP_NAME = "ContactListRoot";
private List subGroups = new LinkedList();
/**
* An empty list that we use when returning an iterator.
*/
private List dummyContacts = new LinkedList();
/**
* The ContactListRoot in ICQ is the only group that can contain subgroups.
*
* @return true (always)
*/
public boolean canContainSubgroups()
{
return true;
}
/**
* Returns the name of this group which is always
* <code>ROOT_CONTACT_GROUP_NAME</code>.
*
* @return a String containing the name of this group.
*/
public String getGroupName()
{
return ROOT_CONTACT_GROUP_NAME;
}
/**
* Adds the specified group at the specified position in the list of sub
* groups.
*
* @param index the position at which the specified group should be added.
* @param group the ContactGroup to add
*/
void addSubGroup(int index, ContactGroupIcqImpl group)
{
subGroups.add(index, group);
}
/**
* Adds the specified group to the end of the list of sub groups.
* @param group the group to add.
*/
void addSubGroup(ContactGroupIcqImpl group)
{
addSubGroup(countContacts(), group);
}
/**
* Removes the specified from the list of sub groups
* @param group the group to remove.
*/
void removeSubGroup(ContactGroupIcqImpl group)
{
removeSubGroup(subGroups.indexOf(group));
}
/**
* Removes the sub group with the specified index.
* @param index the index of the group to remove
*/
void removeSubGroup(int index)
{
subGroups.remove(index);
}
/**
* Removes all contact sub groups and reinsterts them as specified
* by the <code>newOrder</code> param. Contact groups not contained in the
* newOrder list are left at the end of this group.
*
* @param newOrder a list containing all contact groups in the order that is
* to be applied.
*
*/
void reorderSubGroups(List newOrder)
{
subGroups.removeAll(newOrder);
subGroups.addAll(0, newOrder);
}
/**
* Returns the number of subgroups contained by this
* <code>RootContactGroupIcqImpl</code>.
*
* @return an int indicating the number of subgroups that this
* ContactGroup contains.
*/
public int countSubGroups()
{
return subGroups.size();
}
/**
* Returns the subgroup with the specified index.
*
* @param index the index of the <code>ContactGroup</code> to retrieve.
* @return the <code>ContactGroup</code> with the specified index.
*/
public ContactGroup getGroup(int index)
{
return (ContactGroupIcqImpl)subGroups.get(index);
}
/**
* Returns the subgroup with the specified name.
* @param groupName the name of the <code>ContactGroup</code> to retrieve.
* @return the <code>ContactGroup</code> with the specified index.
*/
public ContactGroup getGroup(String groupName)
{
Iterator subgroups = subGroups();
while (subgroups.hasNext())
{
ContactGroupIcqImpl grp = (ContactGroupIcqImpl)subgroups.next();
if (grp.getGroupName().equals(groupName))
return grp;
}
return null;
}
/**
* Returns the <code>Contact</code> with the specified address or
* identifier.
* @param id the addres or identifier of the <code>Contact</code> we are
* looking for.
* @return the <code>Contact</code> with the specified id or address.
*/
public Contact getContact(String id)
{
//no contacts in the root group for this icq impl.
return null;
}
/**
* Returns an iterator over the sub groups that this
* <code>ContactGroup</code> contains.
*
* @return a java.util.Iterator over the <code>ContactGroup</code>
* children of this group (i.e. subgroups).
*/
public Iterator subGroups()
{
return subGroups.iterator();
}
/**
* Returns the number, which is always 0, of <code>Contact</code> members
* of this <code>ContactGroup</code>
* @return an int indicating the number of <code>Contact</code>s, members
* of this <code>ContactGroup</code>.
*/
public int countContacts()
{
return 0;
}
/**
* Returns an Iterator over all contacts, member of this
* <code>ContactGroup</code>.
* @return a java.util.Iterator over all contacts inside this
* <code>ContactGroup</code>
*/
public Iterator contacts()
{
return dummyContacts.iterator();
}
/**
* A dummy impl of the corresponding interface method - always returns null.
*
* @param index the index of the <code>Contact</code> to return.
* @return the <code>Contact</code> with the specified index, i.e. always
* null.
*/
public Contact getContact(int index)
{
return null;
}
/**
* Returns a string representation of the root contact group that contains
* all subgroups and subcontacts of this group.
*
* @return a string representation of this root contact group.
*/
public String toString()
{
StringBuffer buff = new StringBuffer(getGroupName());
buff.append(".subGroups="+countSubGroups()+":\n");
Iterator subGroups = subGroups();
while (subGroups.hasNext())
{
ContactGroup group = (ContactGroup) subGroups.next();
buff.append(group.toString());
if(subGroups.hasNext())
buff.append("\n");
}
return buff.toString();
}
}

@ -0,0 +1,884 @@
/*
* 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.protocol.icq;
import net.java.sip.communicator.service.protocol.*;
import net.kano.joustsim.oscar.oscar.service.ssi.*;
import java.util.*;
import net.java.sip.communicator.util.*;
import net.kano.joustsim.Screenname;
import net.kano.joscar.snaccmd.ssi.SsiItem;
import net.java.sip.communicator.service.protocol.event.*;
import net.kano.joscar.ssiitem.*;
import net.kano.joustsim.oscar.*;
/**
* This class encapsulates the net.kano BuddyList class. Once created, it will
* register itself as a listener to the encapsulated BuddyList and modify it's
* local copy of Contacts and ContactGroups every time an event is generated
* by the underlying joustsim framework. The class would also generate
* corresponding sip-communicator events to all events coming from joustsim.
*
* @author Emil Ivov
*/
public class ServerStoredContactListIcqImpl
implements BuddyInfoTrackerListener
{
private static final Logger logger =
Logger.getLogger(ServerStoredContactListIcqImpl.class);
/**
* The joustsim buddy list that we encapsulate
*/
private MutableBuddyList buddyList = null;
/**
* Our joustsim buddy list event listener
*/
private BuddyListListener buddyListListener = new BuddyListListener();
/**
* Our joustsim group change listener.
*/
private GroupChangeListener jsimGroupChangeListener
= new GroupChangeListener();
/**
* A joust sim item change listener.
*/
private JoustSimItemChangeListener jsimItemChangeListener
= new JoustSimItemChangeListener();
/**
* Our joustsim buddy change listener.
*/
private JoustSimBuddyListener jsimBuddyListener
= new JoustSimBuddyListener();
/**
* The root contagroup. The container for all ICQ buddies and groups.
*/
private RootContactGroupIcqImpl rootGroup
= new RootContactGroupIcqImpl();
/**
* Listeners that others registered with us for contact list events.
*/
private Vector contactListListeners = new Vector();
/**
* The joust sim service that deals with server stored information.
*/
private SsiService jSimSsiService = null;
/**
* The operation set that created us and that we could use when dispatching
* subscription events.
*/
private OperationSetPersistentPresenceIcqImpl parentOperationSet = null;
/**
* The icqProvider that is on top of us.
*/
private ProtocolProviderServiceIcqImpl icqProvider = null;
/**
* Listeners that would receive event notifications for changes in group
* names or other properties, removal or creation of groups.
*/
private Vector serverStoredGroupListeners = new Vector();
/**
* Creates a ServerStoredContactList wrapper for the specified BuddyList.
*/
ServerStoredContactListIcqImpl()
{
//don't add the sub ICQ groups to rootGroup here as we'll be having
//event notifications for every one of them through the
//RetroactiveBuddyListListener
}
/**
* Returns the root group of the contact list.
*
* @return the root ContactGroup for the ContactList
*/
public ContactGroup getRootGroup()
{
return rootGroup;
}
/**
* Registers the specified group listener so that it would receive events
* on group modification/creation/destruction.
* @param l the ServerStoredGroupListener to register for group events
*/
void addGroupListener(ServerStoredGroupListener l)
{
synchronized(serverStoredGroupListeners){
this.serverStoredGroupListeners.add(l);
}
}
/**
* Removes the specified group listener so that it won't receive further
* events on group modification/creation/destruction.
* @param l the ServerStoredGroupListener to unregister
*/
void removeGroupListener(ServerStoredGroupListener l)
{
synchronized(serverStoredGroupListeners){
this.serverStoredGroupListeners.remove(l);
}
}
/**
* Creates the corresponding event and notifies all
* <code>ServerStoredGroupListener</code>s that the source group has been
* removed, changed, renamed or whatever happened to it.
* @param group the ContactGroup that has been created/modified/removed
* @param eventID the id of the event to generate.
*/
private void fireGroupEvent(ContactGroupIcqImpl group, int eventID)
{
ServerStoredGroupEvent evt = new ServerStoredGroupEvent(group, eventID,
icqProvider, parentOperationSet);
logger.trace("Will dispatch the following grp event: " + evt);
synchronized (serverStoredGroupListeners){
Iterator listeners = this.serverStoredGroupListeners.iterator();
while (listeners.hasNext())
{
ServerStoredGroupListener l
= (ServerStoredGroupListener) listeners.next();
if (eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT)
l.groupRemoved(evt);
else if (eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT)
l.groupNameChanged(evt);
else if (eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT)
l.groupCreated(evt);
}
}
}
private void fireGroupsReordered()
{
/** @todo implement fireGroupsReordered *///no need of args since it
//could only mean one thing
}
/**
* Make the parent persistent presence operation set dispatch a contact
* added event.
* @param parentGroup the group where the new contact was added
* @param contact the contact that was added
* @param index the index at which it was added.
*/
private void fireContactAdded( ContactGroupIcqImpl parentGroup,
ContactIcqImpl contact,
int index)
{
//bail out if no one's listening
if(parentOperationSet == null){
logger.debug("No presence op. set available. Bailing out.");
return;
}
//dispatch
parentOperationSet.fireSubscriptionEvent(
SubscriptionEvent.SUBSCRIPTION_CREATED, contact, parentGroup);
}
/**
* Make the parent persistent presence operation set dispatch a contact
* removed event.
* @param parentGroup the group where that the removed contact belonged to.
* @param contact the contact that was removed.
*/
private void fireContactRemoved( ContactGroupIcqImpl parentGroup,
ContactIcqImpl contact)
{
//bail out if no one's listening
if(parentOperationSet == null){
logger.debug("No presence op. set available. Bailing out.");
return;
}
//dispatch
parentOperationSet.fireSubscriptionEvent(
SubscriptionEvent.SUBSCRIPTION_CREATED, contact, parentGroup);
}
private void fireContactsReordered( ContactGroupIcqImpl parentGroup)
{
/** @todo implement fireContactsReordered() */
}
/**
* Returns the index of the ContactGroup containing the specified joust sim
* group.
* @param joustSimGroup the joust sim group we're looking for.
* @return the index of the ContactGroup containing the specified
* joustSimGroup or -1 if no containing ContactGroup exists.
*/
public int findContactGroupIndex(Group joustSimGroup)
{
Iterator contactGroups = rootGroup.subGroups();
int index = 0;
for (; contactGroups.hasNext(); index++)
{
ContactGroupIcqImpl contactGroup
= (ContactGroupIcqImpl) contactGroups.next();
if (joustSimGroup == contactGroup.getJoustSimSourceGroup())
return index;
}
return -1;
}
/**
* Returns the ContactGroup corresponding to the specified joust sim group.
* @param joustSimGroup the joust sim group we're looking for.
* @return the ContactGroup corresponding to the specified joustSimGroup
* null if no containing ContactGroup exists.
*/
public ContactGroupIcqImpl findContactGroup(Group joustSimGroup)
{
Iterator contactGroups = rootGroup.subGroups();
while(contactGroups.hasNext())
{
ContactGroupIcqImpl contactGroup
= (ContactGroupIcqImpl) contactGroups.next();
if (joustSimGroup == contactGroup.getJoustSimSourceGroup())
return contactGroup;
}
return null;
}
/**
* Returns the Contact with the specified screenname (or icq UIN) or null if
* no such screenname was found.
*
* @param screenName the screen name (or ICQ UIN) of the contact to find.
* @return the <code>Contact</code> carrying the specified
* <code>screenName</code> or <code>null</code> if no such contact exits.
*/
public ContactIcqImpl findContactByScreenName(String screenName)
{
Iterator contactGroups = rootGroup.subGroups();
ContactIcqImpl result = null;
while(contactGroups.hasNext())
{
ContactGroupIcqImpl contactGroup
= (ContactGroupIcqImpl) contactGroups.next();
result = contactGroup.findContact(screenName);
if (result != null)
return result;
}
return null;
}
/**
* Returns the ContactGroup containing the specified contact or null
* if no such group or contact exist.
*
* @param child the contact whose parent group we're looking for.
* @return the <code>ContactGroup</code> containing the specified
* <code>contact</code> or <code>null</code> if no such groupo or contact
* exist.
*/
public ContactGroupIcqImpl findContactGroup(ContactIcqImpl child)
{
Iterator contactGroups = rootGroup.subGroups();
while(contactGroups.hasNext())
{
ContactGroupIcqImpl contactGroup
= (ContactGroupIcqImpl) contactGroups.next();
if( contactGroup.findContact(child.getJoustSimBuddy())!= null)
return contactGroup;
}
return null;
}
/**
* Adds a new contact with the specified screenname to the list under a
* default location.
* @param screenname the screenname or icq uin of the contact to add.
*/
public void addContact(String screenname)
{
ContactGroupIcqImpl parent =
(ContactGroupIcqImpl)getRootGroup().getGroup(0);
addContact(parent, screenname);
}
/**
* Adds a new contact with the specified screenname to the list under the
* specified group.
* @param screenname the screenname or icq uin of the contact to add.
* @param parent the group under which we want the new contact placed.
*/
public void addContact(ContactGroupIcqImpl parent, String screenname)
{
logger.trace("Addint contact " + screenname
+ " to parent=" + parent.getGroupName());
//if the contact is already in the contact list, only broadcast an event
final ContactIcqImpl existingContact
= findContactByScreenName(screenname);
//if the contact already exists - just issue an event.
if( existingContact != null)
{
logger.debug("Contact " + screenname + " already exists. Gen. evt.");
//broadcast the event in a separate thread so that we don't
//block the calling thread.
new Thread(){
public void run(){
parentOperationSet.fireSubscriptionEvent(
SubscriptionEvent.SUBSCRIPTION_CREATED,
existingContact,
findContactGroup(existingContact));
}
}.start();
return;
}
logger.trace("Adding the contact to the specified group.");
//extract the top level group
AddMutableGroup group = parent.getJoustSimSourceGroup();
group.addBuddy(screenname);
}
/**
* Creates the specified group on the server stored contact list.
* @param groupName a String containing the name of the new group.
*/
public void createGroup(String groupName)
{
logger.trace("Creating group: " + groupName);
buddyList.addGroup(groupName);
logger.trace("Group " +groupName+ " created.");
}
/**
* Removes the specified group from the icq buddy list.
* @param groupToRemove the group that we'd like removed.
*/
public void removeGroup(ContactGroupIcqImpl groupToRemove)
{
buddyList.deleteGroupAndBuddies(
groupToRemove.getJoustSimSourceGroup());
}
/**
* Renames the specified group according to the specified new name..
* @param groupToRename the group that we'd like removed.
* @param newName the new name of the group
*/
public void renameGroup(ContactGroupIcqImpl groupToRename, String newName)
{
groupToRename.getJoustSimSourceGroup().rename(newName);
}
/**
* Moves the specified <code>contact</code> to the group indicated by
* <code>newParent</code>.
* @param contact the contact that we'd like moved under the new group.
* @param newParent the group where we'd like the parent placed.
*/
public void moveContact(ContactIcqImpl contact,
ContactGroupIcqImpl newParent)
{
List contactsToMove = new ArrayList();
contactsToMove.add(contact);
buddyList.moveBuddies(contactsToMove,
newParent.getJoustSimSourceGroup());
}
/**
* Sets a reference to the currently active and valid instance of
* the JoustSIM SsiService that this list is to use for retrieving
* server stored information
* @param joustSimSsiService a valid reference to the currently active JoustSIM
* SsiService.
* @param parentOperationSet the operation set that created us and that
* we could use for dispatching subscription events
* @param icqProvider the icqProvider that has instantiated us.
*/
void init( SsiService joustSimSsiService,
OperationSetPersistentPresenceIcqImpl parentOperationSet,
ProtocolProviderServiceIcqImpl icqProvider)
{
this.jSimSsiService = joustSimSsiService;
jSimSsiService.addItemChangeListener(jsimItemChangeListener);
this.buddyList = jSimSsiService.getBuddyList();
buddyList.addRetroactiveLayoutListener(buddyListListener);
this.parentOperationSet = parentOperationSet;
this.icqProvider = icqProvider;
}
private class BuddyListListener
implements BuddyListLayoutListener
{
/**
* Called by joustsim as a notification of the fact that the server has
* sent the specified group and that it is actually a member from
* our contact list. We copy the group locally and generate the
* corresponding sip-communicator events
*
* @param list the BuddyList where this is happening.
* @param oldItems we don't use it
* @param newItems we don't use it
* @param group the new Group that has been added
* @param buddies the members of the new group.
*/
public void groupAdded(BuddyList list, List oldItems, List newItems,
Group group, List buddies)
{
logger.trace("Group added: " + group.getName());
logger.trace("Buddies: " + buddies);
ContactGroupIcqImpl newGroup
= new ContactGroupIcqImpl((MutableGroup)group, buddies);
//add a joust sim buddy listener to all of the buddies in this group
for(int i = 0; i < buddies.size(); i++)
((Buddy)buddies.get(i)).addBuddyListener(jsimBuddyListener);
//elements in the newItems list may include groups that have not
//yet been reported through this method. In order to make sure that
//we keep the order specified by the server, we try to add after a
//newItems member that has a corresponding ContactGroup entry in our
//contact list, and add the new entry after it
int groupIndex = newItems.indexOf(group);
assert groupIndex != -1:group + " was not present in newItems"
+ newItems;
int insertPos = 0;
if (groupIndex == 0)
{
//this is the first group so insert at 0.
rootGroup.addSubGroup(insertPos, newGroup);
}
else
{
for (; groupIndex >= 0; groupIndex--)
{
int prevContactGroupIndex
= findContactGroupIndex( (Group) newItems.get(groupIndex));
//if we've found the nearest previous group that we already
//know of we should insert the new group behind it.
if (prevContactGroupIndex != -1)
insertPos = prevContactGroupIndex + 1;
}
rootGroup.addSubGroup(insertPos, newGroup);
}
//register a listener for name changes of this group
group.addGroupListener(jsimGroupChangeListener);
//tell listeners about the added group
fireGroupEvent(newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
}
/**
* Called by joust sim when a group is removed.
*
* @param list the <code>BuddyList</code> owning the removed group.
* @param oldItems the list of items as it was before removing the group.
* @param newItems the list of items as it is after the group is removed.
* @param group the group that was removed.
*/
public void groupRemoved(BuddyList list, List oldItems, List newItems,
Group group)
{
logger.trace("Group Removed: " + group.getName());
int index = findContactGroupIndex(group);
ContactGroupIcqImpl removedGroup
= (ContactGroupIcqImpl) rootGroup.getGroup(index);
if (index == -1)
{
logger.debug("non existing group: " + group.getName());
return;
}
group.removeGroupListener(jsimGroupChangeListener);
rootGroup.removeSubGroup(index);
fireGroupEvent(removedGroup,
ServerStoredGroupEvent.GROUP_REMOVED_EVENT);
}
/**
* Called by joust sim to notify us that a new buddy has been added
* to the contact list.
*
* @param list the <code>BuddyList</code> owning the newly added buddy.
* @param joustSimGroup the parent group of the added buddy.
* @param oldItems unused
* @param newItems unused
* @param buddy the newly added <code>buddy</code>
*/
public void buddyAdded(BuddyList list, Group joustSimGroup, List oldItems,
List newItems, Buddy buddy)
{
ContactIcqImpl newContact
= new ContactIcqImpl(buddy);
ContactGroupIcqImpl parentGroup = findContactGroup(joustSimGroup);
if (parentGroup == null)
{
logger.debug("no parent group "
+ joustSimGroup + " found for buddy: " + buddy);
return;
}
int buddyIndex = newItems.indexOf(buddy);
if( buddyIndex == -1 ){
logger.debug(buddy+" was not present in newItems"+newItems);
}
//elements in the newItems list may include buddies that have not
//yet been reported through this method. In order to make sure that
//we keep the order specified by the server, we try to add after a
//newItems member that has a corresponding ContactGroup entry in our
//contact list, and add the new entry after it
int insertPos = 0;
if (buddyIndex == 0)
{
//this is the first group so insert at 0.
parentGroup.addContact(insertPos, newContact);
}
else
{
for (; buddyIndex >= 0; buddyIndex--)
{
int prevContactIndex = parentGroup.findContactIndex(
(Buddy) newItems.get(buddyIndex));
//if we've found the nearest previous group that we already
//know of we should insert the new group behind it.
if (prevContactIndex != -1)
insertPos = prevContactIndex + 1;
}
parentGroup.addContact(insertPos, newContact);
}
//register a listener for name changes of this buddy
buddy.addBuddyListener(jsimBuddyListener);
//tell listeners about the added group
fireContactAdded(parentGroup, newContact, insertPos);
}
/**
b * Called by joust sim when a buddy is removed
*
* @param list the <code>BuddyList</code> containing the buddy
* @param group the joust sim group that the buddy is removed from.
* @param oldItems unused
* @param newItems unused
* @param buddy Buddy
*/
public void buddyRemoved(BuddyList list, Group group, List oldItems,
List newItems, Buddy buddy)
{
ContactGroupIcqImpl parentGroup = findContactGroup(group);
ContactIcqImpl contactToRemove = parentGroup.findContact(buddy);
parentGroup.removeContact(contactToRemove);
buddy.removeBuddyListener(jsimBuddyListener);
fireContactRemoved(parentGroup, contactToRemove);
}
/**
* Called by joust sim when contacts in a group have been reordered.
* Removes all Contacts from the concerned group and reinserts them
* in the right order.
*
* @param list the <code>BuddyList</code> where all this happens
* @param group the group whose buddies have been reordered.
* @param oldBuddies unused
* @param newBuddies the list containing the buddies in their new order.
*/
public void buddiesReordered(BuddyList list, Group group,
List oldBuddies, List newBuddies)
{
ContactGroupIcqImpl contactGroup = findContactGroup(group);
if (contactGroup == null)
{
logger.debug(
"buddies reordered event received for unknown group"
+ group);
}
List reorderedContacts = new ArrayList();
Iterator newBuddiesIter = newBuddies.iterator();
while (newBuddiesIter.hasNext())
{
Buddy buddy = (Buddy) newBuddiesIter.next();
ContactIcqImpl contact = contactGroup.findContact(buddy);
//make sure that this was not an empty buddy.
if (contact == null)
continue;
reorderedContacts.add(contact);
}
contactGroup.reorderContacts(reorderedContacts);
fireContactsReordered(contactGroup);
}
/**
* Called by joust sim to indicate that the server stored groups
* have been reordered. We filter this list for contact groups that
* we've already heard of and pass it to the root contact group
* so that it woul reorder its subgroups.
*
* @param list the <code>BuddyList</code> where all this is happening
* @param oldOrder unused
* @param newOrder the order in which groups are now stored by the
* AIM/ICQ server.
*/
public void groupsReordered(BuddyList list, List oldOrder,
List newOrder)
{
List reorderedGroups = new ArrayList();
Iterator newOrderIter = newOrder.iterator();
while (newOrderIter.hasNext())
{
Group group = (Group) newOrderIter.next();
ContactGroupIcqImpl contactGroup = findContactGroup(group);
//make sure that this was not an empty buddy.
if (contactGroup == null)
continue;
reorderedGroups.add(contactGroup);
}
rootGroup.reorderSubGroups(reorderedGroups);
fireGroupsReordered();
}
}
/**
* Proxies events notifying of a change in the group name.
*/
private class GroupChangeListener
implements GroupListener
{
/**
* Verifies whether the concerned group really exists and fires
* a corresponding event
* @param group the group that changed name.
* @param oldName the name, before it changed
* @param newName the current name of the group.
*/
public void groupNameChanged(Group group, String oldName,
String newName)
{
logger.trace("Group name for "+group.getName()+"changed from="
+ oldName + " to=" + newName);
ContactGroupIcqImpl contactGroup = findContactGroup(group);
if (contactGroup == null)
{
logger.debug(
"group name changed event received for unknown group"
+ group);
}
//check whether the name has really changed (the joust sim stack
//would call this method even when the name has not really changed
//and values of oldName and newName would almost always be null)
if (contactGroup.getGroupName()
.equals( contactGroup.getNameCopy() )){
logger.trace("Group name hasn't really changed("
+contactGroup.getGroupName()+"). Ignoring");
return;
}
//we do have a new name. store a copy of it for our next deteciton
//and fire the corresponding event.
logger.trace("Dispatching group change event.");
contactGroup.initNameCopy();
fireGroupEvent(contactGroup,
ServerStoredGroupEvent.GROUP_RENAMED_EVENT);
}
}
private class JoustSimBuddyListener implements BuddyListener
{
/**
* screennameChanged
*
* @param buddy Buddy
* @param oldScreenname Screenname
* @param newScreenname Screenname
*/
public void screennameChanged(Buddy buddy, Screenname oldScreenname,
Screenname newScreenname)
{
/** @todo implement screennameChanged() */
logger.debug("/** @todo implement screennameChanged() */=");
logger.debug("buddy="+buddy);
System.out.println("oldScreenname=" + oldScreenname);
System.out.println("newScreenname=" + newScreenname);
}
/**
* alertActionChanged
*
* @param buddy Buddy
* @param oldAlertAction int
* @param newAlertAction int
*/
public void alertActionChanged(Buddy buddy, int oldAlertAction,
int newAlertAction)
{
/** @todo implement alertActionChanged() */
logger.debug("/** @todo implement alertActionChanged() */=");
System.out.println("buddy=" + buddy);
System.out.println("oldAlertAction=" + oldAlertAction);
System.out.println("newAlertAction=" + newAlertAction);
}
/**
* alertSoundChanged
*
* @param buddy Buddy
* @param oldAlertSound String
* @param newAlertSound String
*/
public void alertSoundChanged(Buddy buddy, String oldAlertSound,
String newAlertSound)
{
/** @todo implement alertSoundChanged() */
logger.debug("/** @todo implement alertSoundChanged() */");
System.out.println("buddy=" + buddy);
System.out.println("oldAlertSound=" + oldAlertSound);
System.out.println("newAlertSound=" + newAlertSound);
}
/**
* alertTimeChanged
*
* @param buddy Buddy
* @param oldAlertEvent int
* @param newAlertEvent int
*/
public void alertTimeChanged(Buddy buddy, int oldAlertEvent,
int newAlertEvent)
{
/** @todo implement alertTimeChanged() */
logger.debug("/** @todo implement alertTimeChanged() */");
System.out.println("buddy=" + buddy);
System.out.println("oldAlertEvent=" + oldAlertEvent);
System.out.println("newAlertEvent=" + newAlertEvent);
}
/**
* aliasChanged
*
* @param buddy Buddy
* @param oldAlias String
* @param newAlias String
*/
public void aliasChanged(Buddy buddy, String oldAlias, String newAlias)
{
/** @todo implement aliasChanged() */
logger.debug("/** @todo implement aliasChanged() */");
System.out.println("buddy=" + buddy);
System.out.println("oldAlias=" + oldAlias);
System.out.println("newAlias=" + newAlias);
}
/**
* buddyCommentChanged
*
* @param buddy Buddy
* @param oldComment String
* @param newComment String
*/
public void buddyCommentChanged(Buddy buddy, String oldComment,
String newComment)
{
/** @todo implement buddyCommentChanged() */
logger.debug("/** @todo implement buddyCommentChanged() */");
System.out.println("buddy=" + buddy);
System.out.println("oldComment=" + oldComment);
System.out.println("newComment=" + newComment);
}
}
/**
* A dummy implementation of the JoustSIM SsiItemChangeListener.
*
* @author Emil Ivov
*/
private class JoustSimItemChangeListener implements SsiItemChangeListener
{
public void handleItemCreated(SsiItem item)
{
/** @todo implement handleItemCreated() */
logger.debug("!!! TODO: implement handleItemCreated() !!!" + item
+ " DATA=" + item.getData().toString());
}
public void handleItemDeleted(SsiItem item)
{
/** @todo implement handleItemDeleted() */
logger.debug("!!! TODO: implement handleItemDeleted()!!!" + item);
}
public void handleItemModified(SsiItem item)
{
/** @todo implement handleItemModified() */
logger.debug("!!! TODO: implement handleItemModified() !!!" + item
+ " DATA=" + item.getData().toString());
}
}
}

@ -0,0 +1,12 @@
Bundle-Activator: net.java.sip.communicator.impl.protocol.icq.Activator
Bundle-Name: ICQ Protocol Provider Implementation
Bundle-Description: An ICQ/AIM implementation of the Protocol Provider Service.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration.event,
Export-Package: net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event
Loading…
Cancel
Save