KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > mail > SortByThread


1 /*
2  * SortByThread.java
3  *
4  * Copyright (C) 2002-2003 Peter Graves
5  * $Id: SortByThread.java,v 1.2 2003/05/30 14:59:57 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j.mail;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Comparator JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import javax.swing.tree.DefaultMutableTreeNode JavaDoc;
32 import org.armedbear.j.Debug;
33 import org.armedbear.j.Log;
34
35 public final class SortByThread
36 {
37     private final List JavaDoc entries;
38
39     private final Node JavaDoc root = new Node JavaDoc();
40     private final Map JavaDoc idMap = new HashMap JavaDoc();
41
42     public SortByThread(List JavaDoc entries)
43     {
44         this.entries = Collections.unmodifiableList(entries);
45     }
46
47     public void run()
48     {
49         final int count = entries.size();
50         // Create all the nodes and populate the ID map.
51
ArrayList JavaDoc nodes = new ArrayList JavaDoc(count);
52         for (int i = 0; i < count; i++) {
53             final MailboxEntry entry = (MailboxEntry) entries.get(i);
54             Node JavaDoc node = new Node JavaDoc(entry);
55             nodes.add(node);
56             String JavaDoc messageId = entry.getMessageId();
57             if (messageId != null && messageId.length() > 0)
58                 idMap.put(messageId, node);
59             else
60                 Debug.bug();
61         }
62
63         for (int i = 0; i < count; i++) {
64             final MailboxEntry entry = (MailboxEntry) entries.get(i);
65             // References.
66
String JavaDoc[] references = entry.getReferences();
67             if (references != null) {
68                 for (int j = 0; j < references.length; j++) {
69                     String JavaDoc reference = references[j];
70                     if (reference == null || reference.length() == 0)
71                         continue;
72                     Node JavaDoc node = (Node JavaDoc) idMap.get(reference);
73                     if (node == null) {
74                         node = new Node JavaDoc(reference, entry.getBaseSubject());
75                         idMap.put(reference, node);
76                         nodes.add(node);
77                     }
78                 }
79             }
80             // In-Reply-To.
81
String JavaDoc inReplyTo = entry.getInReplyTo();
82             if (inReplyTo != null && inReplyTo.length() > 0) {
83                 Node JavaDoc node = (Node JavaDoc) idMap.get(inReplyTo);
84                 if (node == null) {
85                     node = new Node JavaDoc(inReplyTo, entry.getBaseSubject());
86                     idMap.put(inReplyTo, node);
87                     nodes.add(node);
88                 }
89             }
90         }
91
92         Iterator JavaDoc iter = nodes.iterator();
93         while (iter.hasNext()) {
94             Node JavaDoc node = (Node JavaDoc) iter.next();
95             if (node.getParent() == null) {
96                 Node JavaDoc parent = findParentForNode(node);
97                 if (parent != null)
98                     parent.add(node);
99                 else
100                     root.add(node);
101             }
102         }
103
104         removeEmptyContainers(root);
105
106         groupMessagesBySubject();
107
108         // Walk the tree and sort the entries by date (or whatever).
109
sort(root);
110     }
111
112     private void removeEmptyContainers(final Node JavaDoc parent)
113     {
114         for (int i = 0; i < parent.getChildCount(); i++) {
115             Node JavaDoc node = (Node JavaDoc) parent.getChildAt(i);
116             if (node.getMailboxEntry() == null && node.getChildCount() == 0) {
117                 // An empty container with no children.
118
parent.remove(i);
119                 --i;
120                 continue;
121             }
122             if (node.getMailboxEntry() == null && node.getChildCount() > 0) {
123                 Debug.assertTrue(node.getParent() == parent);
124                 if (parent != root || node.getChildCount() == 1) {
125                     for (int j = node.getChildCount(); j-- > 0;) {
126                         Node JavaDoc child = (Node JavaDoc) node.getChildAt(j);
127                         Debug.assertTrue(child.getParent() == node);
128                         node.remove(j);
129                         Debug.assertTrue(child.getParent() == null);
130                         parent.add(child);
131                         Debug.assertTrue(child.getParent() == parent);
132                     }
133                     Debug.assertTrue(node.getChildCount() == 0);
134                     parent.remove(i);
135                     --i;
136                     continue;
137                 }
138             }
139             if (node.getMailboxEntry() != null) {
140                 removeEmptyContainers(node);
141             }
142         }
143     }
144
145     private void groupMessagesBySubject()
146     {
147         HashMap JavaDoc subjectMap = createSubjectMap();
148
149         // Iterate through top-level nodes.
150
for (int i = root.getChildCount(); i-- > 0; ) {
151             final Node JavaDoc node = (Node JavaDoc) root.getChildAt(i);
152             MailboxEntry entry = node.getMailboxEntry();
153             if (entry == null) {
154                 if (node.getChildCount() > 0)
155                     entry = ((Node JavaDoc)node.getChildAt(0)).getMailboxEntry();
156                 if (entry == null)
157                     continue;
158             }
159             final String JavaDoc baseSubject = entry.getBaseSubject();
160             if (baseSubject == null || baseSubject.length() == 0)
161                 continue;
162             Node JavaDoc oldNode = (Node JavaDoc) subjectMap.get(baseSubject);
163             if (oldNode == node)
164                 continue;
165
166             if (oldNode.isDummy() && node.isDummy()) {
167                 // Add node's children to old node.
168
for (int j = node.getChildCount(); j-- > 0;) {
169                     Node JavaDoc child = (Node JavaDoc) node.getChildAt(j);
170                     node.remove(j);
171                     oldNode.add(child);
172                 }
173                 // Remove node.
174
root.remove(i);
175                 continue;
176             }
177
178             if (oldNode.isDummy()) {
179                 Debug.assertTrue(!node.isDummy());
180                 // Make this node be a child of the other.
181
root.remove(i);
182                 oldNode.add(node);
183                 continue;
184             }
185
186             Debug.assertTrue(!oldNode.isDummy());
187             final MailboxEntry oldEntry = oldNode.getMailboxEntry();
188             Debug.assertTrue(oldEntry != null);
189             if (!node.isDummy()) {
190                 if (entry.subjectIsReply() && !oldEntry.subjectIsReply()) {
191                     // Make this node be a child of the other.
192
root.remove(i);
193                     oldNode.add(node);
194                     continue;
195                 }
196             }
197
198             // If neither entry is a reply, leave them both at top level.
199
if (!entry.subjectIsReply() && !oldEntry.subjectIsReply())
200                 continue;
201
202             // Make old and new nodes be children of a new dummy node.
203
// Create a new container for the old message.
204
Node JavaDoc c = new Node JavaDoc(oldEntry);
205             for (int j = oldNode.getChildCount(); j-- > 0;) {
206                 Node JavaDoc child = (Node JavaDoc) oldNode.getChildAt(j);
207                 oldNode.remove(j);
208                 c.add(child);
209             }
210             // Make old container into a dummy by emptying it.
211
oldNode.setUserObject(null);
212             oldNode.setBaseSubject(baseSubject);
213             // Add old and new messages to new dummy node.
214
oldNode.add(c);
215             oldNode.add(node);
216         }
217
218         subjectMap.clear();
219         subjectMap = null;
220     }
221
222     private HashMap JavaDoc createSubjectMap()
223     {
224         HashMap JavaDoc subjectMap = new HashMap JavaDoc();
225         for (int i = 0, limit = root.getChildCount(); i < limit; i++) {
226             final Node JavaDoc node = (Node JavaDoc) root.getChildAt(i);
227             MailboxEntry entry = node.getMailboxEntry();
228             if (entry == null) {
229                 // Dummy node.
230
if (node.getChildCount() > 0)
231                     entry = ((Node JavaDoc)node.getChildAt(0)).getMailboxEntry();
232                 else {
233                     Log.debug("dummy node child count is zero");
234                     continue;
235                 }
236             }
237             if (entry != null) {
238                 String JavaDoc baseSubject = entry.getBaseSubject();
239                 if (baseSubject != null && baseSubject.length() > 0) {
240                     Node JavaDoc oldNode = (Node JavaDoc) subjectMap.get(baseSubject);
241                     if (oldNode == null) {
242                         subjectMap.put(baseSubject, node);
243                     } else {
244                         MailboxEntry oldEntry = oldNode.getMailboxEntry();
245                         if (oldEntry != null && oldEntry.subjectIsReply() &&
246                             !entry.subjectIsReply()) {
247                             subjectMap.put(baseSubject, node);
248                         } else if (node.getMailboxEntry() == null && oldEntry != null) {
249                             subjectMap.put(baseSubject, node);
250                         }
251                     }
252                 }
253             }
254         }
255         return subjectMap;
256     }
257
258     private void sort(Node JavaDoc node)
259     {
260         // Depth first!
261
for (int i = 0, limit = node.getChildCount(); i < limit; i++)
262             sort((Node JavaDoc)node.getChildAt(i));
263         if (node.getChildCount() > 1)
264             node.sortChildren();
265     }
266
267     private Node JavaDoc findParentForNode(Node JavaDoc node)
268     {
269         final MailboxEntry entry = node.getMailboxEntry();
270         if (entry == null)
271             return null;
272         final String JavaDoc inReplyTo = entry.getInReplyTo();
273         if (inReplyTo != null) {
274             Node JavaDoc parent = (Node JavaDoc) idMap.get(inReplyTo);
275             if (parent != null)
276                 if (!parent.isNodeAncestor(node))
277                     return parent;
278         }
279         final String JavaDoc[] references = entry.getReferences();
280         if (references != null) {
281             for (int i = references.length; i-- > 0;) {
282                 Node JavaDoc parent = (Node JavaDoc) idMap.get(references[i]);
283                 if (parent != null)
284                     if (!parent.isNodeAncestor(node))
285                         return parent;
286             }
287         }
288         return null;
289     }
290
291     private int sequenceNumber;
292
293     public void addEntries(Mailbox mb, MailboxFilter filter)
294     {
295         sequenceNumber = 1;
296         addEntriesForNode(root, mb, filter, 0);
297     }
298
299     private void addEntriesForNode(Node JavaDoc node, Mailbox mb, MailboxFilter filter,
300         int depth)
301     {
302         if (node != root) {
303             MailboxEntry entry = node.getMailboxEntry();
304             if (entry != null) {
305                 entry.setSequenceNumber(sequenceNumber++);
306                 if (filter == null || filter.accept(entry))
307                     mb.appendLine(entry, depth);
308             } else {
309                 if (node.getChildCount() > 0) {
310                     Node JavaDoc child = (Node JavaDoc) node.getChildAt(0);
311                     MailboxEntry childEntry = child.getMailboxEntry();
312                     if (childEntry != null)
313                         childEntry.setOrphan(true);
314                 }
315             }
316         }
317         for (int i = 0, limit = node.getChildCount(); i < limit; i++)
318             addEntriesForNode((Node JavaDoc)node.getChildAt(i), mb, filter, depth + 1);
319     }
320 }
321
322 class Node extends DefaultMutableTreeNode JavaDoc
323 {
324     private String JavaDoc messageId;
325     private String JavaDoc baseSubject;
326
327     Node()
328     {
329         super();
330     }
331
332     Node(MailboxEntry entry)
333     {
334         super(entry);
335     }
336
337     Node(String JavaDoc messageId, String JavaDoc baseSubject)
338     {
339         this.messageId = messageId;
340         this.baseSubject = baseSubject;
341     }
342
343     MailboxEntry getMailboxEntry()
344     {
345         return (MailboxEntry) getUserObject();
346     }
347
348     String JavaDoc getBaseSubject()
349     {
350         return baseSubject;
351     }
352
353     void setBaseSubject(String JavaDoc s)
354     {
355         baseSubject = s;
356     }
357
358     String JavaDoc getMessageId()
359     {
360         return messageId;
361     }
362
363     RFC822Date getDate()
364     {
365         MailboxEntry entry = getMailboxEntry();
366         if (entry != null)
367             return entry.getDate();
368         if (getChildCount() > 0) {
369             Node JavaDoc child = (Node JavaDoc) getChildAt(0);
370             entry = child.getMailboxEntry();
371             if (entry != null)
372                 return entry.getDate();
373         }
374         Log.debug("getDate no date");
375         return null;
376     }
377
378     boolean isDummy()
379     {
380         return getUserObject() == null;
381     }
382
383     void sortChildren()
384     {
385         if (children != null)
386             sortEntriesByDate(children);
387     }
388
389     private static final Comparator JavaDoc comparator = new Comparator JavaDoc() {
390         public int compare(Object JavaDoc o1, Object JavaDoc o2)
391         {
392             return RFC822Date.compare(((Node JavaDoc)o1).getDate(),
393                 ((Node JavaDoc)o2).getDate());
394         }
395     };
396
397     private static final void sortEntriesByDate(List JavaDoc list)
398     {
399         Collections.sort(list, comparator);
400     }
401 }
402
Popular Tags