gecko-dev/grendel/view/TestThreader.java
1998-09-09 00:52:38 +00:00

512 lines
16 KiB
Java

/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Grendel mail/news client.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1997
* Netscape Communications Corporation. All Rights Reserved.
*
* Created: Jamie Zawinski <jwz@netscape.com>, 13 Jun 1995.
* Ported from C on 14 Aug 1997.
*/
/** Test harness for Threader and Sorter.
@see Threader
@see Sorter
*/
package grendel.view;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Date;
import calypso.util.ByteBuf;
import calypso.util.ArrayEnumeration;
import calypso.util.QSort;
import calypso.util.Comparer;
import calypso.util.NetworkDate;
class TestMessageThread implements IThreadable, ISortable {
TestMessageThread next;
TestMessageThread kid;
String subject;
String author;
long date;
Object id;
Object[] refs;
int message_number;
private String subject2;
private boolean has_re;
TestMessageThread() {
subject = null; // this means "dummy".
}
public TestMessageThread (TestMessageThread next, String subject, Object id,
Object references[]) {
this.next = next;
this.subject = subject;
this.id = id;
this.refs = references;
}
public String toString() {
if (isDummy())
return "[dummy]";
String s = "[ " + id + ": " + subject + " (";
if (refs != null)
for (int i = 0; i < refs.length; i++)
s += " " + refs[i];
if (date > 0)
s += " \"" + new Date(date) + "\"";
return s + " ) ]";
}
void simplifySubject() {
int start = 0;
int L = subject.length();
boolean done = false;
while (!done) {
done = true;
// skip whitespace.
while (subject.charAt(start) <= ' ')
start++;
if (start < (L-2) &&
(subject.charAt(start) == 'r' || subject.charAt(start) == 'R') &&
(subject.charAt(start+1) == 'e' || subject.charAt(start+1) == 'e')) {
if (subject.charAt(start+2) == ':') {
start += 3; // Skip over "Re:"
has_re = true; // yes, we found it.
done = false; // keep going.
done = false;
} else if (start < (L-2) &&
(subject.charAt(start+2) == '[' ||
subject.charAt(start+2) == '(')) {
int i = start+3; // skip over "Re[" or "Re("
// Skip forward over digits after the "[" or "(".
while (i < L &&
subject.charAt(i) >= '0' &&
subject.charAt(i) <= '9')
i++;
// Now ensure that the following thing is "]:" or "):"
// Only if it is do we alter `start'.
if (i < (L-1) &&
(subject.charAt(i) == ']' ||
subject.charAt(i) == ')') &&
subject.charAt(i+1) == ':') {
start = i+2; // Skip over "]:"
has_re = true; // yes, we found it.
done = false; // keep going.
}
}
}
if (subject2 == "(no subject)") // #### i18n
subject2 = "";
}
int end = L;
// Strip trailing whitespace.
while (end > start && subject.charAt(end-1) < ' ')
end--;
if (start == 0 && end == L)
subject2 = subject;
else
subject2 = subject.substring(start, end);
}
void flushSubjectCache() {
subject2 = null;
}
// for IThreadable
public synchronized Enumeration allElements() {
return new TestMessageThreadEnumeration(this, true);
}
public Object messageThreadID() {
return id;
}
public Object[] messageThreadReferences() {
return refs;
}
public String simplifiedSubject() {
if (subject2 == null) simplifySubject();
return subject2;
}
public boolean subjectIsReply() {
if (subject2 == null) simplifySubject();
return has_re;
}
// Used by both IThreadable and ISortable
public void setNext (Object next) {
this.next = (TestMessageThread) next;
flushSubjectCache();
}
// Used by both IThreadable and ISortable
public void setChild (Object kid) {
this.kid = (TestMessageThread) kid;
flushSubjectCache();
}
public IThreadable makeDummy() {
return (IThreadable) new TestMessageThread();
}
public boolean isDummy() {
return (subject == null);
}
// For ISortable
public synchronized Enumeration children() {
return new TestMessageThreadEnumeration(this, false);
}
}
class TestMessageThreadEnumeration implements Enumeration {
TestMessageThread tail;
Enumeration kids;
boolean recursive_p;
TestMessageThreadEnumeration(TestMessageThread thread, boolean recursive_p) {
this.recursive_p = recursive_p;
if (recursive_p)
tail = thread;
else
tail = thread.kid;
}
public synchronized Object nextElement() {
if (kids != null) {
// if `kids' is non-null, then we've already returned a node,
// and we should now go to work on its children.
TestMessageThread result = (TestMessageThread) kids.nextElement();
if (!kids.hasMoreElements())
kids = null;
return result;
} else if (tail != null) {
// Return `tail', but first note its children, if any.
// We will descend into them the next time around.
TestMessageThread result = tail;
if (recursive_p && tail.kid != null)
kids = new TestMessageThreadEnumeration(tail.kid, true);
tail = tail.next;
return result;
} else {
throw new NoSuchElementException();
}
}
public synchronized boolean hasMoreElements() {
if (tail != null)
return true;
else if (kids != null && kids.hasMoreElements())
return true;
else
return false;
}
}
class TestThreader {
private static TestMessageThread first, last;
private static int count = 0;
private static final int SORT_NUMBER = 0;
private static final int SORT_DATE = 1;
private static final int SORT_SUBJECT = 2;
private static final int SORT_AUTHOR = 3;
private static void make_thread(String subject, String id, String refs) {
TestMessageThread thread = new TestMessageThread();
thread.subject = subject;
thread.id = id;
thread.message_number = ++count;
if (refs != null) {
StringTokenizer st = new StringTokenizer(refs);
Vector v = new Vector(5);
while (st.hasMoreTokens())
v.addElement(st.nextToken());
thread.refs = new Object[v.size()];
for (int i = 0; i < v.size(); i++)
thread.refs[i] = (String) v.elementAt(i);
}
if (first == null)
first = thread;
else
last.next = thread;
last = thread;
}
private static void make_thread2(String subject, String id, String date,
String refs) {
TestMessageThread thread = new TestMessageThread();
thread.subject = subject;
thread.id = id;
thread.date = NetworkDate.parseLong(new ByteBuf(date), false);
thread.message_number = ++count;
if (refs != null) {
StringTokenizer st = new StringTokenizer(refs);
Vector v = new Vector(5);
while (st.hasMoreTokens())
v.addElement(st.nextToken());
thread.refs = new Object[v.size()];
for (int i = 0; i < v.size(); i++)
thread.refs[i] = (String) v.elementAt(i);
}
if (first == null)
first = thread;
else
last.next = thread;
last = thread;
}
public static void main(String[] args) {
int sort_type = SORT_NUMBER;
make_thread("A", "1", null);
make_thread("B", "2", "1");
make_thread("C", "3", "1 2");
make_thread("D", "4", "1");
make_thread("E", "5", "3 x1 x2 x3");
make_thread("F", "6", "2");
make_thread("G", "7", "nonesuch");
make_thread("H", "8", "nonesuch");
make_thread("Loop1", "loop1", "loop2 loop3");
make_thread("Loop2", "loop2", "loop3 loop1");
make_thread("Loop3", "loop3", "loop1 loop2");
make_thread("Loop4", "loop4", "loop5");
make_thread("Loop5", "loop5", "loop4");
make_thread("Loop6", "loop6", "loop6");
make_thread("Loop7", "loop7", "loop8 loop9 loop10 loop8 loop9 loop10");
make_thread("Loop8", "loop8", "loop9 loop10 loop7 loop9 loop10 loop7");
make_thread("Loop8", "loop9", "loop10 loop7 loop8 loop10 loop7 loop8");
make_thread("Loop10", "loop10","loop7 loop8 loop9 loop7 loop8 loop9");
make_thread("Ambig1", "ambig1", null);
make_thread("Ambig2", "ambig2", "ambig1");
make_thread("Ambig3", "ambig3", "ambig1 ambig2");
make_thread("Ambig4", "ambig4", "ambig1 ambig2 ambig3");
make_thread("Ambig5a", "ambig5a", "ambig1 ambig2 ambig3 ambig4");
make_thread("Ambig5b", "ambig5b", "ambig1 ambig3 ambig2 ambig4");
make_thread("dup", "dup", null);
make_thread("dup-kid", "dup-kid", "dup");
make_thread("dup-kid", "dup-kid", "dup");
make_thread("dup-kid-2", "dup-kid-2", "dup");
make_thread("dup-kid-2", "dup-kid-2", "dup");
make_thread("dup-kid-2", "dup-kid-2", "dup");
make_thread("same subject 1", "ss1.1", null);
make_thread("same subject 1", "ss1.2", null);
make_thread("missingmessage", "missa", null);
make_thread("missingmessage", "missc", "missa missb");
make_thread2("liar 1", "<liar.1>", "", "<liar.a> <liar.c>");
make_thread2("liar 2", "<liar.2>", "", "<liar.a> <liar.b> <liar.c>");
make_thread2("liar2 1", "<liar2.1>", "", "<liar2.a> <liar2.b> <liar2.c>");
make_thread2("liar2 2", "<liar2.2>", "", "<liar2.a> <liar2.c>");
make_thread2("xx",
"<331F7D61.2781@netscape.com>",
"Thu, 06 Mar 1997 18:28:50 -0800",
null);
make_thread2("lkjhlkjh",
"<3321E51F.41C6@netscape.com>",
"Sat, 08 Mar 1997 14:15:59 -0800",
null);
make_thread2("test 2",
"<3321E5A6.41C6@netscape.com>",
"Sat, 08 Mar 1997 14:18:14 -0800",
null);
make_thread2("enc",
"<3321E5C0.167E@netscape.com>",
"Sat, 08 Mar 1997 14:18:40 -0800",
null);
make_thread2("lkjhlkjh",
"<3321E715.15FB@netscape.com>",
"Sat, 08 Mar 1997 14:24:21 -0800",
null);
make_thread2("eng",
"<3321E7A4.59E2@netscape.com>",
"Sat, 08 Mar 1997 14:26:44 -0800",
null);
make_thread2("lkjhl",
"<3321E7BB.1CFB@netscape.com>",
"Sat, 08 Mar 1997 14:27:07 -0800",
null);
make_thread2("Re: certs and signed messages",
"<332230AA.41C6@netscape.com>",
"Sat, 08 Mar 1997 19:38:18 -0800",
"<33222A5E.ED4@netscape.com>");
make_thread2("from dogbert",
"<3323546E.BEE44C78@netscape.com>",
"Sun, 09 Mar 1997 16:23:10 -0800",
null);
make_thread2("lkjhlkjhl",
"<33321E2A.1C849A20@netscape.com>",
"Thu, 20 Mar 1997 21:35:38 -0800",
null);
make_thread2("le:/u/jwz/mime/smi",
"<33323C9D.ADA4BCBA@netscape.com>",
"Thu, 20 Mar 1997 23:45:33 -0800",
null);
make_thread2("ile:/u/jwz",
"<33323F62.402C573B@netscape.com>",
"Thu, 20 Mar 1997 23:57:22 -0800",
null);
make_thread2("ljkljhlkjhl",
"<336FBAD0.864BC1F4@netscape.com>",
"Tue, 06 May 1997 16:12:16 -0700",
null);
make_thread2("lkjh",
"<336FBB46.A0028A6D@netscape.com>",
"Tue, 06 May 1997 16:14:14 -0700",
null);
make_thread2("foo",
"<337265C1.5C758C77@netscape.com>",
"Thu, 08 May 1997 16:46:09 -0700",
null);
make_thread2("Welcome to Netscape",
"<337AAB3D.C8BCE069@netscape.com>",
"Wed, 14 May 1997 23:20:45 -0700",
null);
make_thread2("Re: Welcome to Netscape",
"<337AAE46.903032E4@netscape.com>",
"Wed, 14 May 1997 23:33:45 -0700",
"<337AAB3D.C8BCE069@netscape.com>");
make_thread2("[Fwd: enc/signed test 1]",
"<338B6EE2.BB26C74C@netscape.com>",
"Tue, 27 May 1997 16:31:46 -0700",
null);
Threader t = new Threader();
last = null;
first = (TestMessageThread) t.thread (first);
System.out.print("\n------- threaded:\n\n");
printThread(first, 0);
// System.out.print("\n------- pass 2\n\n");
// t = new Threader();
// first = (TestMessageThread) t.thread (first);
// printThread(first, 0);
// System.out.print("\n------- pass 3\n\n");
// t = new Threader();
// first = (TestMessageThread) t.thread (first);
// printThread(first, 0);
Comparer comparer;
if (sort_type == SORT_DATE) {
comparer = new Comparer() {
public int compare(Object oa, Object ob) {
TestMessageThread ta = (TestMessageThread) oa;
TestMessageThread tb = (TestMessageThread) ob;
TestMessageThread a = (ta.subject == null ? ta.kid : ta);
TestMessageThread b = (tb.subject == null ? tb.kid : tb);
if (a.date == b.date) return 0;
else if (a.date < b.date) return -1;
else return 1;
}
};
} else if (sort_type == SORT_SUBJECT) {
comparer = new Comparer() {
public int compare(Object oa, Object ob) {
TestMessageThread ta = (TestMessageThread) oa;
TestMessageThread tb = (TestMessageThread) ob;
TestMessageThread a = (ta.subject == null ? ta.kid : ta);
TestMessageThread b = (tb.subject == null ? tb.kid : tb);
return a.simplifiedSubject().compareTo(b.simplifiedSubject());
}
};
} else if (sort_type == SORT_AUTHOR) {
comparer = new Comparer() {
public int compare(Object oa, Object ob) {
TestMessageThread ta = (TestMessageThread) oa;
TestMessageThread tb = (TestMessageThread) ob;
TestMessageThread a = (ta.subject == null ? ta.kid : ta);
TestMessageThread b = (tb.subject == null ? tb.kid : tb);
return a.author.compareTo(b.author);
}
};
} else { // (sort_type == SORT_NUMBER)
comparer = new Comparer() {
public int compare(Object oa, Object ob) {
TestMessageThread ta = (TestMessageThread) oa;
TestMessageThread tb = (TestMessageThread) ob;
TestMessageThread a = (ta.subject == null ? ta.kid : ta);
TestMessageThread b = (tb.subject == null ? tb.kid : tb);
if (a.message_number == b.message_number) return 0;
else if (a.message_number < b.message_number) return -1;
else return 1;
}
};
}
Sorter s = new Sorter(comparer);
TestMessageThread dummy = new TestMessageThread();
dummy.kid = first;
s.sortMessageChildren(dummy);
first = dummy.kid;
dummy = null;
System.out.print("\n------- sorted:\n\n");
printThread(first, 0);
}
private static void printThread(TestMessageThread thread, int depth) {
for (int i = 0; i < depth; i++) System.out.print(" ");
System.out.println(thread.toString());
if (thread.kid != null) printThread(thread.kid, depth+1);
if (thread.next != null) printThread(thread.next, depth);
}
}