📄 threadedemails.java
字号:
package mujmail.threading;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;
import mujmail.util.Functions;
import mujmail.IStorage;
import mujmail.MessageHeader;
import mujmail.Settings;
import mujmail.ordering.Comparator;
/**
* This class represents structure for threaded e-mail.
* Structure contains vector of root messages and for each root message
* it contains {@link Vector} of child messages. Child messages vectors
* are stored in hash map, in which root message ID is the key.
*
* {@link ThreadedEmails} structure is used also for other boxes too
* (f.e. Outbox, Draft, ...). In these boxes we do not need threads, so
* in these cases only root messages are used and, for memory saving,
* there are not empty vectors, but nulls.
*
* @author Betlista
*/
public class ThreadedEmails implements IStorage {
/** Flag signals if we want to print debug prints */
private static final boolean DEBUG = false;
/* *
* While Hashtable does not support adding nulls, we use this
* constant to represent that there is nothing in hashtable stored under
* some key.
*/
// private static final Object NULL = new Object(); // why not Vector? Because Object is the simplest class.
/** Root messages, threads (with or without children). */
private Vector/*<MessageHeader>*/ rootMessages;
/**
* Child vectors mapped in map, in which parent message ID is key under
* which child vector is stored.
*/
private Hashtable/*< String (parentID - threadedMessageID), Vector<MessageHeader> >*/ children;
/**
* Mapping for getting parent for child ID.
*/
private Hashtable/*< String (childID - threadedMessageID), MessageHeader>*/ parents;
/**
* Number of e-mail headers stored in structure.
*/
int size;
/**
* Creates empty structure.
*/
public ThreadedEmails() {
children = new Hashtable();
parents = new Hashtable();
rootMessages = new Vector();
size = 0;
lastReturnedHeader = null;
lastReturnedIndex = 0;
}
/**
* <p>Returns the index at which the message is stored in vector.</p>
* <p>
* Invariant of this method is, that this condition have to be true:<br>
* <code>
* // index is the index returned by this method<br>
* // messages and threadingMessageID are parameters<br>
* threadingMessageID.equals( messages.elementAt(index).getThreadingMessageID() )
* </code>
* </p>
* @param messages vector to be searched in
* @param threadingMessageID message ID we are looking message for
* @return index of the message in the vectors
*/
private int indexOf(final Vector/*<MessageHeaders>*/ messages, final String threadingMessageID) {
final int size = messages.size();
MessageHeader header;
for (int i = 0; i < size; ++i) {
header = (MessageHeader)messages.elementAt( i );
if ( threadingMessageID.equals( header.getThreadingMessageID() ) ) {
return i;
}
}
return -1;
}
/**
* Adds root (new thread) to vector of root messages.
*
* @param messageHeader e-mail header that represents thread parent
* message
*/
public void addRoot(final MessageHeader messageHeader) {
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addRoot(MessageHeader) - message header: " + messageHeader );
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addRoot(MessageHeader) - size (start): " + size);
// if the message is the empty root message and showing of these
// messages is turned off, such message is skipped
// if ( !Settings.rootsVisible && messageHeader.isEmpty() ) {
// return;
// }
final int index = indexOf(rootMessages, messageHeader.getThreadingMessageID() );
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addMessage(MessageHeader, MessageHeader) - index: " + index);
// if there is not this message in root messages
if ( index == -1 ) {
rootMessages.addElement( messageHeader );
//String messageID = messageHeader.getThreadingMessageID();
//children.put(messageID, NULL); // have no children yet
//parents.put(messageID, NULL); // have no parent too (it's root message)
++size;
} else {
MessageHeader header = (MessageHeader)rootMessages.elementAt( index );
if ( header.isEmpty() ) {
rootMessages.setElementAt( messageHeader, index);
} else {
// this is here because we support multiple messages with same
// threading message ID
rootMessages.addElement( messageHeader );
++size;
}
}
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addRoot(MessageHeader) - size (end): " + size);
}
/**
* Adds message to thread. rootMessageID could be different from
* messageHeaders parent ID.
*
* @param parentMessage e-mail header message ID of the parent message
* @param messageHeader e-mail header to be added to the thread
*/
public void addMessage( final MessageHeader parentMessage, final MessageHeader messageHeader ) {
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addMessage(MessageHeader, MessageHeader) - rootMessage: " + parentMessage + ", messageHeader: " + messageHeader);
if (DEBUG) System.out.println("DEBUG ThreadedEmails.addMessage(MessageHeader, MessageHeader) - size (start): " + size);
// when threading is not enabled - all messages are added to the root messages
if ( ! Settings.threading ) {
addRoot( messageHeader );
return;
}
// roots are added yet
// add to parents
parents.put( messageHeader.getThreadingMessageID(), parentMessage );
// add to children
Object childVector = children.get( parentMessage.getThreadingMessageID() );
if ( childVector instanceof Vector ) {
((Vector)childVector).addElement( messageHeader );
++size;
} else {
Vector v = new Vector();
v.addElement( messageHeader );
children.put( parentMessage.getThreadingMessageID(), v );
++size;
}
}
/* ************************
* Interface methods *
**************************/
/*
* (non-Javadoc)
* @see mujmail.IStorage#getEnumeration()
*/
public Enumeration getEnumeration() {
return new Enumerator();
}
/**
* Simply returns number of e-mail headers in structure.
* Number of messages is increased when
* {@link #addMessage(MessageHeader, MessageHeader)} is called.
*
* @return number of e-mail headers in structure.
*/
public int getSize() {
return size;
}
/**
* Returns whether structure is empty.
*
* @return true if structure is empty (size == 0), false when size > 0
*/
public boolean isEmpty() {
return size == 0;
}
private int lastReturnedIndex = 0;
private MessageHeader lastReturnedHeader = null;
//#ifdef MUJMAIL_TEST_GET_MESSAGE_AT
//# public static int simpleNext = 0;
//# public static int simplePrevious = 0;
//# public static int nextFromZero = 0;
//#endif
/*
* (non-Javadoc)
* @see mujmail.IStorage#getMessageAt(int)
*/
public MessageHeader getMessageAt(final int index) {
if (DEBUG) { System.out.println("DEBUG ThreadedEmails.getMessageAt(" + index + ")" ); }
//System.out.println(" lastReturnedIndex = " + lastReturnedIndex);
//System.out.println(" lastReturnedHeader= " + lastReturnedHeader);
/* TODO (Betlista): this implementation is really stupid, but
* this method should not be used, I want to remove usage of this
* method from the code
*/
/* Betlista:
* Previous comment is correct, but we can assume that when user uses
* this method he wants with quite good possibility the next message
* he wanted before - on this is based next solution:
*
* I remember previously returned message and it's index and when user
* request message with index i than:
* when i > lastReturnedIndex
* than it's called nextElement() until lastReturned == i
* when i < lastReturnedIndex && i > (lastReturnedIndex - i)
* than it's called previousElement() until lastReturned == i
* when i < lastReturnedIndex && i < (lastReturnedIndex - i)
* than it's called nextElement() for i times (from zero)
*
* Note: lastReturnedIndex is shifted by one again current position
* lastReturnedIndex=5 means that we returned 4th position (position are conted from 0)
*/
final Enumerator messages = new Enumerator();
MessageHeader messageHeader = null;
// Note: This guard has to be here because other 3 possibilities (noted above)
// moves index immediately
// We want to know same position as in last call
if (index == lastReturnedIndex - 1) { // -1 is shift between lastReturedIndex and real position
return lastReturnedHeader;
}
if (index >= lastReturnedIndex) {
//#ifdef MUJMAIL_TEST_GET_MESSAGE_AT
//# ++simpleNext;
//#endif
messages.actual = lastReturnedHeader;
messages.index = lastReturnedIndex;
while ( messages.hasNextElement() ) {
messageHeader = (MessageHeader)messages.nextElement();
//if (DEBUG) { System.out.println(" (" + (messages.index) + ") " + messageHeader); }
if ( messages.index - 1 == index ) {
lastReturnedHeader = messages.actual;
lastReturnedIndex = messages.index;
break;
}
}
} else if ( index < lastReturnedIndex ) {
if ( index < lastReturnedIndex - index ) {
//#ifdef MUJMAIL_TEST_GET_MESSAGE_AT
//# ++nextFromZero;
//#endif
while ( messages.hasNextElement() ) {
messageHeader = (MessageHeader)messages.nextElement();
//if (DEBUG) { System.out.println(" (" + (messages.index) + ") " + messageHeader); }
if ( messages.index - 1 == index ) {
lastReturnedHeader = messages.actual;
lastReturnedIndex = messages.index;
break;
}
}
} else {
//#ifdef MUJMAIL_TEST_GET_MESSAGE_AT
//# ++simplePrevious;
//#endif
messages.actual = lastReturnedHeader;
messages.index = lastReturnedIndex;
while ( messages.hasPreviousElement() ) {
messageHeader = (MessageHeader)messages.previousElement();
//if (DEBUG) { System.out.println(" (" + (messages.index) + ") " + messageHeader); }
if ( messages.index - 1 == index ) {
lastReturnedHeader = messages.actual;
lastReturnedIndex = messages.index;
break;
}
}
}
}
//System.out.println("Result (" + index + ")=" + messageHeader);
//System.out.println(" lastReturnedIndex = " + lastReturnedIndex);
//System.out.println(" lastReturnedHeader= " + lastReturnedHeader);
//System.out.println();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -