1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-11 03:09:46 +02:00

Created new chat manager which handles the creation of Chats.

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6213 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Alex Wenckus 2006-11-22 22:55:37 +00:00 committed by alex
parent 18a35e4e8f
commit ae6065d7cc
36 changed files with 3964 additions and 714 deletions

View file

@ -20,15 +20,12 @@
package org.jivesoftware.smack;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.ThreadFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.StringUtils;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* A chat is a series of messages sent between two users. Each chat has a unique
@ -37,73 +34,28 @@ import java.util.Set;
* don't send thread IDs at all. Therefore, if a message without a thread ID
* arrives it is routed to the most recently created Chat with the message
* sender.
*
* @see XMPPConnection#createChat(String)
*
* @author Matt Tucker
*/
public class Chat {
/**
* A prefix helps to make sure that ID's are unique across mutliple instances.
*/
private static String prefix = StringUtils.randomString(5);
/**
* Keeps track of the current increment, which is appended to the prefix to
* forum a unique ID.
*/
private static long id = 0;
/**
* Returns the next unique id. Each id made up of a short alphanumeric
* prefix along with a unique numeric value.
*
* @return the next id.
*/
private static synchronized String nextID() {
return prefix + Long.toString(id++);
}
private XMPPConnection connection;
private ChatManager chatManager;
private String threadID;
private String participant;
private PacketFilter messageFilter;
private PacketCollector messageCollector;
private final Set<WeakReference<PacketListener>> listeners =
new HashSet<WeakReference<PacketListener>>();
/**
* Creates a new chat with the specified user.
*
* @param connection the connection the chat will use.
* @param participant the user to chat with.
*/
public Chat(XMPPConnection connection, String participant) {
// Automatically assign the next chat ID.
this(connection, participant, nextID());
}
new CopyOnWriteArraySet<WeakReference<PacketListener>>();
/**
* Creates a new chat with the specified user and thread ID.
*
* @param connection the connection the chat will use.
* @param chatManager the chatManager the chat will use.
* @param participant the user to chat with.
* @param threadID the thread ID to use.
*/
public Chat(XMPPConnection connection, String participant, String threadID) {
this.connection = connection;
Chat(ChatManager chatManager, String participant, String threadID) {
this.chatManager = chatManager;
this.participant = participant;
this.threadID = threadID;
// Register with the map of chats so that messages with no thread ID
// set will be delivered to this Chat.
connection.chats.put(StringUtils.parseBareAddress(participant),
new WeakReference<Chat>(this));
// Filter the messages whose thread equals Chat's id
messageFilter = new ThreadFilter(threadID);
messageCollector = connection.createPacketCollector(messageFilter);
}
/**
@ -140,30 +92,15 @@ public class Chat {
* @throws XMPPException if sending the message fails.
*/
public void sendMessage(String text) throws XMPPException {
Message message = createMessage();
message.setBody(text);
connection.sendPacket(message);
}
/**
* Creates a new Message to the chat participant. The message returned
* will have its thread property set with this chat ID.
*
* @return a new message addressed to the chat participant and
* using the correct thread value.
* @see #sendMessage(Message)
*/
public Message createMessage() {
Message message = new Message(participant, Message.Type.CHAT);
Message message = new Message(participant, Message.Type.chat);
message.setThread(threadID);
return message;
message.setBody(text);
chatManager.sendMessage(this, message);
}
/**
* Sends a message to the other chat participant. The thread ID, recipient,
* and message type of the message will automatically set to those of this chat
* in case the Message was not created using the {@link #createMessage() createMessage}
* method.
* and message type of the message will automatically set to those of this chat.
*
* @param message the message to send.
* @throws XMPPException if an error occurs sending the message.
@ -172,47 +109,9 @@ public class Chat {
// Force the recipient, message type, and thread ID since the user elected
// to send the message through this chat object.
message.setTo(participant);
message.setType(Message.Type.CHAT);
message.setType(Message.Type.chat);
message.setThread(threadID);
connection.sendPacket(message);
}
/**
* Polls for and returns the next message, or <tt>null</tt> if there isn't
* a message immediately available. This method provides significantly different
* functionalty than the {@link #nextMessage()} method since it's non-blocking.
* In other words, the method call will always return immediately, whereas the
* nextMessage method will return only when a message is available (or after
* a specific timeout).
*
* @return the next message if one is immediately available and
* <tt>null</tt> otherwise.
*/
public Message pollMessage() {
return (Message)messageCollector.pollResult();
}
/**
* Returns the next available message in the chat. The method call will block
* (not return) until a message is available.
*
* @return the next message.
*/
public Message nextMessage() {
return (Message)messageCollector.nextResult();
}
/**
* Returns the next available message in the chat. The method call will block
* (not return) until a packet is available or the <tt>timeout</tt> has elapased.
* If the timeout elapses without a result, <tt>null</tt> will be returned.
*
* @param timeout the maximum amount of time to wait for the next message.
* @return the next message, or <tt>null</tt> if the timeout elapses without a
* message becoming available.
*/
public Message nextMessage(long timeout) {
return (Message)messageCollector.nextResult(timeout);
chatManager.sendMessage(this, message);
}
/**
@ -222,12 +121,21 @@ public class Chat {
* @param listener a packet listener.
*/
public void addMessageListener(PacketListener listener) {
connection.addPacketListener(listener, messageFilter);
// Keep track of the listener so that we can manually deliver extra
// messages to it later if needed.
synchronized (listeners) {
listeners.add(new WeakReference<PacketListener>(listener));
if(listener == null) {
return;
}
listeners.add(new WeakReference<PacketListener>(listener));
}
/**
* Creates a {@link org.jivesoftware.smack.PacketCollector} which will accumulate the Messages
* for this chat. Always cancel PacketCollectors when finished with them as they will accumulate
* messages indefinitely.
*
* @return the PacketCollector which returns Messages for this chat.
*/
public PacketCollector createCollector() {
return chatManager.createPacketCollector(this);
}
/**
@ -244,31 +152,16 @@ public class Chat {
// probably never had one.
message.setThread(threadID);
messageCollector.processPacket(message);
synchronized (listeners) {
for (Iterator<WeakReference<PacketListener>> i=listeners.iterator(); i.hasNext(); ) {
WeakReference<PacketListener> listenerRef = i.next();
PacketListener listener;
if ((listener = listenerRef.get()) != null) {
listener.processPacket(message);
}
// If the reference was cleared, remove it from the set.
else {
i.remove();
}
for (Iterator<WeakReference<PacketListener>> i = listeners.iterator(); i.hasNext();) {
WeakReference<PacketListener> listenerRef = i.next();
PacketListener listener;
if ((listener = listenerRef.get()) != null) {
listener.processPacket(message);
}
}
}
public void finalize() throws Throwable {
super.finalize();
try {
if (messageCollector != null) {
messageCollector.cancel();
// If the reference was cleared, remove it from the set.
else {
i.remove();
}
}
catch (Exception e) {
// Ignore.
}
}
}

View file

@ -0,0 +1,25 @@
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
* This software is the proprietary information of Jive Software. Use is subject to license terms.
*/
package org.jivesoftware.smack;
/**
* A listener for chat related events.
*
* @author Alexander Wenckus
*/
public interface ChatListener {
/**
* Event fired when a new chat is created.
*
* @param chat the chat that was created.
* @param createdLocally true if the chat was created by the local user and false if it wasn't.
*/
void chatCreated(Chat chat, boolean createdLocally);
}

View file

@ -0,0 +1,188 @@
/**
* $RCSfile: $
* $Revision: $
* $Date: $
*
* Copyright (C) 2006 Jive Software. All rights reserved.
* This software is the proprietary information of Jive Software. Use is subject to license terms.
*/
package org.jivesoftware.smack;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.collections.ReferenceMap;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.filter.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* The chat manager keeps track of references to all current chats. It will not hold any references
* in memory on its own so it is neccesary to keep a reference to the chat object itself. To be
* made aware of new chats, register a listener by calling {@link #addChatListener(ChatListener)}.
*
* @author Alexander Wenckus
*/
public class ChatManager {
/**
* Returns the next unique id. Each id made up of a short alphanumeric
* prefix along with a unique numeric value.
*
* @return the next id.
*/
private static synchronized String nextID() {
return prefix + Long.toString(id++);
}
/**
* A prefix helps to make sure that ID's are unique across mutliple instances.
*/
private static String prefix = StringUtils.randomString(5);
/**
* Keeps track of the current increment, which is appended to the prefix to
* forum a unique ID.
*/
private static long id = 0;
/**
* Maps thread ID to chat.
*/
private Map<String, Chat> threadChats = new ReferenceMap<String, Chat>(ReferenceMap.HARD,
ReferenceMap.WEAK);
/**
* Maps jids to chats
*/
private Map<String, Chat> jidChats = new ReferenceMap<String, Chat>(ReferenceMap.HARD,
ReferenceMap.WEAK);
private Set<ChatListener> chatListeners = new CopyOnWriteArraySet<ChatListener>();
private XMPPConnection connection;
ChatManager(XMPPConnection connection) {
this.connection = connection;
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class),
new PacketFilter() {
public boolean accept(Packet packet) {
if (!(packet instanceof Message)) {
return false;
}
Message.Type messageType = ((Message) packet).getType();
return messageType != Message.Type.groupchat &&
messageType != Message.Type.headline;
}
});
// Add a listener for all message packets so that we can deliver errant
// messages to the best Chat instance available.
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
Chat chat;
if (message.getThread() == null) {
chat = getUserChat(StringUtils.parseBareAddress(message.getFrom()));
}
else {
chat = getThreadChat(message.getThread());
}
if(chat == null) {
chat = createChat(message);
}
deliverMessage(chat, message);
}
}, filter);
}
/**
* Creates a new chat and returns it.
*
* @param userJID the user this chat is with.
* @param listener the listener which will listen for new messages from this chat.
* @return the created chat.
*/
public Chat createChat(String userJID, PacketListener listener) {
String threadID = nextID();
Chat chat = createChat(userJID, threadID, true);
chat.addMessageListener(listener);
return chat;
}
private Chat createChat(String userJID, String threadID, boolean createdLocally) {
Chat chat = new Chat(this, userJID, threadID);
threadChats.put(threadID, chat);
jidChats.put(userJID, chat);
for(ChatListener listener : chatListeners) {
listener.chatCreated(chat, createdLocally);
}
return chat;
}
private Chat createChat(Message message) {
String threadID = message.getThread();
String userJID = message.getFrom();
return createChat(userJID, threadID, false);
}
private Chat getUserChat(String userJID) {
return jidChats.get(userJID);
}
private Chat getThreadChat(String thread) {
return threadChats.get(thread);
}
/**
* Register a new listener with the ChatManager to recieve events related to chats.
*
* @param listener the listener.
*/
public void addChatListener(ChatListener listener) {
chatListeners.add(listener);
}
/**
* Removes a listener, it will no longer be notified of new events related to chats.
*
* @param listener the listener that is being removed
*/
public void removeChatListener(ChatListener listener) {
chatListeners.remove(listener);
}
/**
* Returns an unmodifiable collection of all chat listeners currently registered with this
* manager.
*
* @return an unmodifiable collection of all chat listeners currently registered with this
* manager.
*/
public Collection<ChatListener> getChatListeners() {
return Collections.unmodifiableCollection(chatListeners);
}
private void deliverMessage(Chat chat, Message message) {
// Here we will run any interceptors
chat.deliver(message);
}
void sendMessage(Chat chat, Message message) {
// Here we will run any interceptors
connection.sendPacket(message);
}
PacketCollector createPacketCollector(Chat chat) {
return connection.createPacketCollector(new AndFilter(new ThreadFilter(chat.getThreadID()),
new FromContainsFilter(chat.getParticipant())));
}
}

View file

@ -22,8 +22,6 @@ package org.jivesoftware.smack;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
@ -34,15 +32,12 @@ import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@ -147,14 +142,6 @@ public class XMPPConnection {
Writer writer;
Reader reader;
/**
* A map between JIDs and the most recently created Chat object with that JID.
* Reference to the Chat is stored via a WeakReference so that the map
* does not interfere with garbage collection. The map of chats must be stored
* with each connection.
*/
Map<String, WeakReference<Chat>> chats = new ConcurrentHashMap<String, WeakReference<Chat>>();
/**
* Collection of available stream compression methods offered by the server.
*/
@ -167,6 +154,7 @@ public class XMPPConnection {
* Holds the initial configuration used while creating the connection.
*/
private ConnectionConfiguration configuration;
private ChatManager chatManager;
/**
* Creates a new connection to the specified XMPP server. A DNS SRV lookup will be
@ -564,18 +552,16 @@ public class XMPPConnection {
}
/**
* Creates a new chat with the specified participant. The participant should
* be a valid XMPP user such as <tt>jdoe@jivesoftware.com</tt> or
* <tt>jdoe@jivesoftware.com/work</tt>.
* Returns a chat manager instance for this connection. The ChatManager manages all incoming and
* outgoing chats on the current connection.
*
* @param participant the person to start the conversation with.
* @return a new Chat object.
* @return a chat manager instance for this connection.
*/
public Chat createChat(String participant) {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
public synchronized ChatManager getChatManager() {
if(this.chatManager == null) {
this.chatManager = new ChatManager(this);
}
return new Chat(this, participant);
return this.chatManager;
}
/**
@ -934,34 +920,6 @@ public class XMPPConnection {
for (ConnectionCreationListener listener : connectionEstablishedListeners) {
listener.connectionCreated(this);
}
// Add a listener for all message packets so that we can deliver errant
// messages to the best Chat instance available.
addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
// Ignore any messages with a thread ID, as they will likely
// already be associated with a Chat. This will miss messages
// with new thread ID values, but we can only assume that a
// listener is registered to deal with this case.
if (message.getThread() == null &&
message.getType() != Message.Type.GROUP_CHAT &&
message.getType() != Message.Type.HEADLINE) {
WeakReference<Chat> chatRef =
chats.get(StringUtils.parseBareAddress(message.getFrom()));
if (chatRef != null) {
// Do some extra clean-up if the reference was cleared.
Chat chat = chatRef.get();
if (chat == null) {
chats.remove(message.getFrom());
}
else {
chat.deliver(message);
}
}
}
}
}, new PacketTypeFilter(Message.class));
}
else {
packetReader.notifyReconnection();

View file

@ -22,6 +22,9 @@ package org.jivesoftware.smack.filter;
import org.jivesoftware.smack.packet.Packet;
import java.util.List;
import java.util.ArrayList;
/**
* Implements the logical AND operation over two or more packet filters.
* In other words, packets pass this filter if they pass <b>all</b> of the filters.
@ -30,39 +33,34 @@ import org.jivesoftware.smack.packet.Packet;
*/
public class AndFilter implements PacketFilter {
/**
* The current number of elements in the filter.
*/
private int size;
/**
* The list of filters.
*/
private PacketFilter [] filters;
private List<PacketFilter> filters = new ArrayList<PacketFilter>();
/**
* Creates an empty AND filter. Filters should be added using the
* {@link #addFilter(PacketFilter)} method.
*/
public AndFilter() {
size = 0;
filters = new PacketFilter[3];
}
/**
* Creates an AND filter using the two specified filters.
* Creates an AND filter using the specified filters.
*
* @param filter1 the first packet filter.
* @param filter2 the second packet filter.
* @param filters the filters to add.
*/
public AndFilter(PacketFilter filter1, PacketFilter filter2) {
if (filter1 == null || filter2 == null) {
throw new IllegalArgumentException("Parameters cannot be null.");
public AndFilter(PacketFilter... filters) {
if (filters == null) {
throw new IllegalArgumentException("Parameter cannot be null.");
}
for(PacketFilter filter : filters) {
if(filter == null) {
throw new IllegalArgumentException("Parameter cannot be null.");
}
this.filters.add(filter);
}
size = 2;
filters = new PacketFilter[2];
filters[0] = filter1;
filters[1] = filter2;
}
/**
@ -75,22 +73,12 @@ public class AndFilter implements PacketFilter {
if (filter == null) {
throw new IllegalArgumentException("Parameter cannot be null.");
}
// If there is no more room left in the filters array, expand it.
if (size == filters.length) {
PacketFilter [] newFilters = new PacketFilter[filters.length+2];
for (int i=0; i<filters.length; i++) {
newFilters[i] = filters[i];
}
filters = newFilters;
}
// Add the new filter to the array.
filters[size] = filter;
size++;
filters.add(filter);
}
public boolean accept(Packet packet) {
for (int i=0; i<size; i++) {
if (!filters[i].accept(packet)) {
for (PacketFilter filter : filters) {
if (!filter.accept(packet)) {
return false;
}
}

View file

@ -45,11 +45,6 @@ public class ThreadFilter implements PacketFilter {
}
public boolean accept(Packet packet) {
if (packet instanceof Message) {
return thread.equals(((Message)packet).getThread());
}
else {
return false;
}
return packet instanceof Message && thread.equals(((Message) packet).getThread());
}
}

View file

@ -48,7 +48,7 @@ import org.jivesoftware.smack.util.StringUtils;
*/
public class Message extends Packet {
private Type type = Type.NORMAL;
private Type type = Type.normal;
private String subject = null;
private String body = null;
private String thread = null;
@ -138,7 +138,8 @@ public class Message extends Packet {
/**
* Sets the body of the message. The body is the main message contents.
* @param body
*
* @param body the body of the message.
*/
public void setBody(String body) {
this.body = body;
@ -176,7 +177,7 @@ public class Message extends Packet {
if (getFrom() != null) {
buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
}
if (type != Type.NORMAL) {
if (type != Type.normal) {
buf.append(" type=\"").append(type).append("\"");
}
buf.append(">");
@ -190,7 +191,7 @@ public class Message extends Packet {
buf.append("<thread>").append(thread).append("</thread>");
}
// Append the error subpacket if the message type is an error.
if (type == Type.ERROR) {
if (type == Type.error) {
XMPPError error = getError();
if (error != null) {
buf.append(error.toXML());
@ -205,69 +206,41 @@ public class Message extends Packet {
/**
* Represents the type of a message.
*/
public static class Type {
public enum Type {
/**
* (Default) a normal text message used in email like interface.
*/
public static final Type NORMAL = new Type("normal");
normal,
/**
* Typically short text message used in line-by-line chat interfaces.
*/
public static final Type CHAT = new Type("chat");
chat,
/**
* Chat message sent to a groupchat server for group chats.
*/
public static final Type GROUP_CHAT = new Type("groupchat");
groupchat,
/**
* Text message to be displayed in scrolling marquee displays.
*/
public static final Type HEADLINE = new Type("headline");
headline,
/**
* indicates a messaging error.
*/
public static final Type ERROR = new Type("error");
error;
/**
* Converts a String value into its Type representation.
*
* @param type the String value.
* @return the Type corresponding to the String.
*/
public static Type fromString(String type) {
if (type == null) {
return NORMAL;
public static Type fromString(String name) {
try {
return Type.valueOf(name);
}
type = type.toLowerCase();
if (CHAT.toString().equals(type)) {
return CHAT;
}
else if (GROUP_CHAT.toString().equals(type)) {
return GROUP_CHAT;
}
else if (HEADLINE.toString().equals(type)) {
return HEADLINE;
}
else if (ERROR.toString().equals(type)) {
return ERROR;
}
else {
return NORMAL;
catch (Exception e) {
return normal;
}
}
private String value;
private Type(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
}

View file

@ -0,0 +1,89 @@
// GenericsNote: Converted.
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.NoSuchElementException;
/**
* Provides an implementation of an empty iterator.
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:24 $
* @since Commons Collections 3.1
*/
abstract class AbstractEmptyIterator <E> {
/**
* Constructor.
*/
protected AbstractEmptyIterator() {
super();
}
public boolean hasNext() {
return false;
}
public E next() {
throw new NoSuchElementException("Iterator contains no elements");
}
public boolean hasPrevious() {
return false;
}
public E previous() {
throw new NoSuchElementException("Iterator contains no elements");
}
public int nextIndex() {
return 0;
}
public int previousIndex() {
return -1;
}
public void add(E obj) {
throw new UnsupportedOperationException("add() not supported for empty Iterator");
}
public void set(E obj) {
throw new IllegalStateException("Iterator contains no elements");
}
public void remove() {
throw new IllegalStateException("Iterator contains no elements");
}
public E getKey() {
throw new IllegalStateException("Iterator contains no elements");
}
public E getValue() {
throw new IllegalStateException("Iterator contains no elements");
}
public E setValue(E value) {
throw new IllegalStateException("Iterator contains no elements");
}
public void reset() {
// do nothing
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,80 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
/**
* Abstract pair class to assist with creating KeyValue and MapEntry implementations.
*
* @author James Strachan
* @author Michael A. Smith
* @author Neil O'Toole
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
* @since Commons Collections 3.0
*/
public abstract class AbstractKeyValue <K,V> implements KeyValue<K, V> {
/**
* The key
*/
protected K key;
/**
* The value
*/
protected V value;
/**
* Constructs a new pair with the specified key and given value.
*
* @param key the key for the entry, may be null
* @param value the value for the entry, may be null
*/
protected AbstractKeyValue(K key, V value) {
super();
this.key = key;
this.value = value;
}
/**
* Gets the key from the pair.
*
* @return the key
*/
public K getKey() {
return key;
}
/**
* Gets the value from the pair.
*
* @return the value
*/
public V getValue() {
return value;
}
/**
* Gets a debugging String view of the pair.
*
* @return a String view of the entry
*/
public String toString() {
return new StringBuffer().append(getKey()).append('=').append(getValue()).toString();
}
}

View file

@ -0,0 +1,89 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Map;
/**
* Abstract Pair class to assist with creating correct Map Entry implementations.
*
* @author James Strachan
* @author Michael A. Smith
* @author Neil O'Toole
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
* @since Commons Collections 3.0
*/
public abstract class AbstractMapEntry <K,V> extends AbstractKeyValue<K, V> implements Map.Entry<K, V> {
/**
* Constructs a new entry with the given key and given value.
*
* @param key the key for the entry, may be null
* @param value the value for the entry, may be null
*/
protected AbstractMapEntry(K key, V value) {
super(key, value);
}
// Map.Entry interface
//-------------------------------------------------------------------------
/**
* Sets the value stored in this Map Entry.
* <p/>
* This Map Entry is not connected to a Map, so only the local data is changed.
*
* @param value the new value
* @return the previous value
*/
public V setValue(V value) {
V answer = this.value;
this.value = value;
return answer;
}
/**
* Compares this Map Entry with another Map Entry.
* <p/>
* Implemented per API documentation of {@link java.util.Map.Entry#equals(Object)}
*
* @param obj the object to compare to
* @return true if equal key and value
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Map.Entry == false) {
return false;
}
Map.Entry other = (Map.Entry) obj;
return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));
}
/**
* Gets a hashCode compatible with the equals method.
* <p/>
* Implemented per API documentation of {@link java.util.Map.Entry#hashCode()}
*
* @return a suitable hash code
*/
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
// GenericsNote: Converted.
/*
* Copyright 2001-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Map;
/**
* A restricted implementation of {@link java.util.Map.Entry} that prevents
* the MapEntry contract from being broken.
*
* @author James Strachan
* @author Michael A. Smith
* @author Neil O'Toole
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
* @since Commons Collections 3.0
*/
public final class DefaultMapEntry <K,V> extends AbstractMapEntry<K, V> {
/**
* Constructs a new entry with the specified key and given value.
*
* @param key the key for the entry, may be null
* @param value the value for the entry, may be null
*/
public DefaultMapEntry(final K key, final V value) {
super(key, value);
}
/**
* Constructs a new entry from the specified KeyValue.
*
* @param pair the pair to copy, must not be null
* @throws NullPointerException if the entry is null
*/
public DefaultMapEntry(final KeyValue<K, V> pair) {
super(pair.getKey(), pair.getValue());
}
/**
* Constructs a new entry from the specified MapEntry.
*
* @param entry the entry to copy, must not be null
* @throws NullPointerException if the entry is null
*/
public DefaultMapEntry(final Map.Entry<K, V> entry) {
super(entry.getKey(), entry.getValue());
}
}

View file

@ -0,0 +1,58 @@
// GenericsNote: Converted.
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Iterator;
/**
* Provides an implementation of an empty iterator.
* <p/>
* This class provides an implementation of an empty iterator.
* This class provides for binary compatability between Commons Collections
* 2.1.1 and 3.1 due to issues with <code>IteratorUtils</code>.
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:24 $
* @since Commons Collections 2.1.1 and 3.1
*/
public class EmptyIterator <E> extends AbstractEmptyIterator<E> implements ResettableIterator<E> {
/**
* Singleton instance of the iterator.
*
* @since Commons Collections 3.1
*/
public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator();
/**
* Singleton instance of the iterator.
*
* @since Commons Collections 2.1.1 and 3.1
*/
public static final Iterator INSTANCE = RESETTABLE_INSTANCE;
public static <T> Iterator<T> getInstance() {
return INSTANCE;
}
/**
* Constructor.
*/
protected EmptyIterator() {
super();
}
}

View file

@ -0,0 +1,42 @@
// GenericsNote: Converted.
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
/**
* Provides an implementation of an empty map iterator.
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:24 $
* @since Commons Collections 3.1
*/
public class EmptyMapIterator extends AbstractEmptyIterator implements MapIterator, ResettableIterator {
/**
* Singleton instance of the iterator.
*
* @since Commons Collections 3.1
*/
public static final MapIterator INSTANCE = new EmptyMapIterator();
/**
* Constructor.
*/
protected EmptyMapIterator() {
super();
}
}

View file

@ -0,0 +1,61 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Map;
/**
* Defines a map that can be iterated directly without needing to create an entry set.
* <p/>
* A map iterator is an efficient way of iterating over maps.
* There is no need to access the entry set or cast to Map Entry objects.
* <pre>
* IterableMap map = new HashedMap();
* MapIterator it = map.mapIterator();
* while (it.hasNext()) {
* Object key = it.next();
* Object value = it.getValue();
* it.setValue("newValue");
* }
* </pre>
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:19 $
* @since Commons Collections 3.0
*/
public interface IterableMap <K,V> extends Map<K, V> {
/**
* Obtains a <code>MapIterator</code> over the map.
* <p/>
* A map iterator is an efficient way of iterating over maps.
* There is no need to access the entry set or cast to Map Entry objects.
* <pre>
* IterableMap map = new HashedMap();
* MapIterator it = map.mapIterator();
* while (it.hasNext()) {
* Object key = it.next();
* Object value = it.getValue();
* it.setValue("newValue");
* }
* </pre>
*
* @return a map iterator
*/
MapIterator<K, V> mapIterator();
}

View file

@ -0,0 +1,46 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
/**
* Defines a simple key value pair.
* <p/>
* A Map Entry has considerable additional semantics over and above a simple
* key-value pair. This interface defines the minimum key value, with just the
* two get methods.
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:19 $
* @since Commons Collections 3.0
*/
public interface KeyValue <K,V> {
/**
* Gets the key from the pair.
*
* @return the key
*/
K getKey();
/**
* Gets the value from the pair.
*
* @return the value
*/
V getValue();
}

View file

@ -0,0 +1,109 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Iterator;
/**
* Defines an iterator that operates over a <code>Map</code>.
* <p/>
* This iterator is a special version designed for maps. It can be more
* efficient to use this rather than an entry set iterator where the option
* is available, and it is certainly more convenient.
* <p/>
* A map that provides this interface may not hold the data internally using
* Map Entry objects, thus this interface can avoid lots of object creation.
* <p/>
* In use, this iterator iterates through the keys in the map. After each call
* to <code>next()</code>, the <code>getValue()</code> method provides direct
* access to the value. The value can also be set using <code>setValue()</code>.
* <pre>
* MapIterator it = map.mapIterator();
* while (it.hasNext()) {
* Object key = it.next();
* Object value = it.getValue();
* it.setValue(newValue);
* }
* </pre>
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:19 $
* @since Commons Collections 3.0
*/
public interface MapIterator <K,V> extends Iterator<K> {
/**
* Checks to see if there are more entries still to be iterated.
*
* @return <code>true</code> if the iterator has more elements
*/
boolean hasNext();
/**
* Gets the next <em>key</em> from the <code>Map</code>.
*
* @return the next key in the iteration
* @throws java.util.NoSuchElementException
* if the iteration is finished
*/
K next();
//-----------------------------------------------------------------------
/**
* Gets the current key, which is the key returned by the last call
* to <code>next()</code>.
*
* @return the current key
* @throws IllegalStateException if <code>next()</code> has not yet been called
*/
K getKey();
/**
* Gets the current value, which is the value associated with the last key
* returned by <code>next()</code>.
*
* @return the current value
* @throws IllegalStateException if <code>next()</code> has not yet been called
*/
V getValue();
//-----------------------------------------------------------------------
/**
* Removes the last returned key from the underlying <code>Map</code> (optional operation).
* <p/>
* This method can be called once per call to <code>next()</code>.
*
* @throws UnsupportedOperationException if remove is not supported by the map
* @throws IllegalStateException if <code>next()</code> has not yet been called
* @throws IllegalStateException if <code>remove()</code> has already been called
* since the last call to <code>next()</code>
*/
void remove();
/**
* Sets the value associated with the current key (optional operation).
*
* @param value the new value
* @return the previous value
* @throws UnsupportedOperationException if setValue is not supported by the map
* @throws IllegalStateException if <code>next()</code> has not yet been called
* @throws IllegalStateException if <code>remove()</code> has been called since the
* last call to <code>next()</code>
*/
V setValue(V value);
}

View file

@ -0,0 +1,161 @@
// GenericsNote: Converted.
/*
* Copyright 2002-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* A <code>Map</code> implementation that allows mappings to be
* removed by the garbage collector.
* <p/>
* When you construct a <code>ReferenceMap</code>, you can specify what kind
* of references are used to store the map's keys and values.
* If non-hard references are used, then the garbage collector can remove
* mappings if a key or value becomes unreachable, or if the JVM's memory is
* running low. For information on how the different reference types behave,
* see {@link java.lang.ref.Reference}.
* <p/>
* Different types of references can be specified for keys and values.
* The keys can be configured to be weak but the values hard,
* in which case this class will behave like a
* <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
* <code>WeakHashMap</code></a>. However, you can also specify hard keys and
* weak values, or any other combination. The default constructor uses
* hard keys and soft values, providing a memory-sensitive cache.
* <p/>
* This map is similar to ReferenceIdentityMap.
* It differs in that keys and values in this class are compared using <code>equals()</code>.
* <p/>
* This {@link java.util.Map} implementation does <i>not</i> allow null elements.
* Attempting to add a null key or value to the map will raise a <code>NullPointerException</code>.
* <p/>
* This implementation is not synchronized.
* You can use {@link java.util.Collections#synchronizedMap} to
* provide synchronized access to a <code>ReferenceMap</code>.
* Remember that synchronization will not stop the garbage collecter removing entries.
* <p/>
* All the available iterators can be reset back to the start by casting to
* <code>ResettableIterator</code> and calling <code>reset()</code>.
* <p/>
* NOTE: As from Commons Collections 3.1 this map extends <code>AbstractReferenceMap</code>
* (previously it extended AbstractMap). As a result, the implementation is now
* extensible and provides a <code>MapIterator</code>.
*
* @author Paul Jack
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
* @see java.lang.ref.Reference
* @since Commons Collections 3.0 (previously in main package v2.1)
*/
public class ReferenceMap <K,V> extends AbstractReferenceMap<K, V> implements Serializable {
/**
* Serialization version
*/
private static final long serialVersionUID = 1555089888138299607L;
/**
* Constructs a new <code>ReferenceMap</code> that will
* use hard references to keys and soft references to values.
*/
public ReferenceMap() {
super(HARD, SOFT, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false);
}
/**
* Constructs a new <code>ReferenceMap</code> that will
* use the specified types of references.
*
* @param keyType the type of reference to use for keys;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param valueType the type of reference to use for values;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
*/
public ReferenceMap(int keyType, int valueType) {
super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false);
}
/**
* Constructs a new <code>ReferenceMap</code> that will
* use the specified types of references.
*
* @param keyType the type of reference to use for keys;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param valueType the type of reference to use for values;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param purgeValues should the value be automatically purged when the
* key is garbage collected
*/
public ReferenceMap(int keyType, int valueType, boolean purgeValues) {
super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues);
}
/**
* Constructs a new <code>ReferenceMap</code> with the
* specified reference types, load factor and initial
* capacity.
*
* @param keyType the type of reference to use for keys;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param valueType the type of reference to use for values;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param capacity the initial capacity for the map
* @param loadFactor the load factor for the map
*/
public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor) {
super(keyType, valueType, capacity, loadFactor, false);
}
/**
* Constructs a new <code>ReferenceMap</code> with the
* specified reference types, load factor and initial
* capacity.
*
* @param keyType the type of reference to use for keys;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param valueType the type of reference to use for values;
* must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
* @param capacity the initial capacity for the map
* @param loadFactor the load factor for the map
* @param purgeValues should the value be automatically purged when the
* key is garbage collected
*/
public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor, boolean purgeValues) {
super(keyType, valueType, capacity, loadFactor, purgeValues);
}
//-----------------------------------------------------------------------
/**
* Write the map out using a custom routine.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
doWriteObject(out);
}
/**
* Read the map in using a custom routine.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
doReadObject(in);
}
}

View file

@ -0,0 +1,38 @@
// GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util.collections;
import java.util.Iterator;
/**
* Defines an iterator that can be reset back to an initial state.
* <p/>
* This interface allows an iterator to be repeatedly reused.
*
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:19 $
* @since Commons Collections 3.0
*/
public interface ResettableIterator <E> extends Iterator<E> {
/**
* Resets the iterator back to the position at which the iterator
* was created.
*/
public void reset();
}