in src/main/java/org/apache/commons/net/nntp/Threader.java [167:300]
private void gatherSubjects(final NntpThreadContainer root) {
int count = 0;
for (NntpThreadContainer c = root.child; c != null; c = c.next) {
count++;
}
// TODO verify this will avoid rehashing
HashMap<String, NntpThreadContainer> subjectTable = new HashMap<>((int) (count * 1.2), (float) 0.9);
count = 0;
for (NntpThreadContainer c = root.child; c != null; c = c.next) {
Threadable threadable = c.threadable;
// No threadable? If so, it is a dummy node in the root set.
// Only root set members may be dummies, and they always have at least 2 kids
// Take the first kid as representative of the subject
if (threadable == null) {
threadable = c.child.threadable;
}
final String subj = threadable.simplifiedSubject();
if (subj == null || subj.isEmpty()) {
continue;
}
final NntpThreadContainer old = subjectTable.get(subj);
// Add this container to the table iff:
// - There exists no container with this subject
// - or this is a dummy container and the old one is not - the dummy one is
// more interesting as a root, so put it in the table instead
// - The container in the table has a "Re:" version of this subject, and
// this container has a non-"Re:" version of this subject. The non-"Re:" version
// is the more interesting of the two.
if (old == null || c.threadable == null && old.threadable != null
|| old.threadable != null && old.threadable.subjectIsReply() && c.threadable != null && !c.threadable.subjectIsReply()) {
subjectTable.put(subj, c);
count++;
}
}
// If the table is empty, we're done
if (count == 0) {
return;
}
// subjectTable is now populated with one entry for each subject which occurs in the
// root set. Iterate over the root set, and gather together the difference.
NntpThreadContainer prev;
NntpThreadContainer c;
NntpThreadContainer rest;
for (prev = null, c = root.child, rest = c.next; c != null; prev = c, c = rest, rest = rest == null ? null : rest.next) {
Threadable threadable = c.threadable;
// is it a dummy node?
if (threadable == null) {
threadable = c.child.threadable;
}
final String subj = threadable.simplifiedSubject();
// Don't thread together all subjectless messages
if (subj == null || subj.isEmpty()) {
continue;
}
final NntpThreadContainer old = subjectTable.get(subj);
if (old == c) { // That's us
continue;
}
// We have now found another container in the root set with the same subject
// Remove the "second" message from the root set
if (prev == null) {
root.child = c.next;
} else {
prev.next = c.next;
}
c.next = null;
if (old.threadable == null && c.threadable == null) {
// both dummies - merge them
NntpThreadContainer tail;
for (tail = old.child; tail != null && tail.next != null; tail = tail.next) {
// do nothing
}
if (tail != null) { // protect against possible NPE
tail.next = c.child;
}
for (tail = c.child; tail != null; tail = tail.next) {
tail.parent = old;
}
c.child = null;
} else if (old.threadable == null || c.threadable != null && c.threadable.subjectIsReply() && !old.threadable.subjectIsReply()) {
// Else if old is empty, or c has "Re:" and old does not ==> make this message a child of old
c.parent = old;
c.next = old.child;
old.child = c;
} else {
// else make the old and new messages be children of a new dummy container.
// We create a new container object for old.msg and empty the old container
final NntpThreadContainer newc = new NntpThreadContainer();
newc.threadable = old.threadable;
newc.child = old.child;
for (NntpThreadContainer tail = newc.child; tail != null; tail = tail.next) {
tail.parent = newc;
}
old.threadable = null;
old.child = null;
c.parent = old;
newc.parent = old;
// Old is now a dummy- give it 2 kids , c and newc
old.child = c;
c.next = newc;
}
// We've done a merge, so keep the same prev
c = prev;
}
subjectTable.clear();
subjectTable = null;
}