private void gatherSubjects()

in src/main/java/org/apache/commons/net/nntp/Threader.java [159:290]


    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, c, 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;

    }