| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.net.nntp; |
| |
| import java.io.PrintStream; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| |
| import org.apache.commons.net.util.NetConstants; |
| |
| /** |
| * This is a class that contains the basic state needed for message retrieval and threading. |
| * With thanks to Jamie Zawinski (jwz@jwz.org) |
| */ |
| public class Article implements Threadable { |
| /** |
| * Recursive method that traverses a pre-threaded graph (or tree) |
| * of connected Article objects and prints them out. |
| * @param article the root of the article 'tree' |
| * @since 3.4 |
| */ |
| public static void printThread(final Article article) { |
| printThread(article, 0, System.out); |
| } |
| /** |
| * Recursive method that traverses a pre-threaded graph (or tree) |
| * of connected Article objects and prints them out. |
| * @param article the root of the article 'tree' |
| * @param depth the current tree depth |
| */ |
| public static void printThread(final Article article, final int depth) { |
| printThread(article, depth, System.out); |
| } |
| /** |
| * Recursive method that traverses a pre-threaded graph (or tree) |
| * of connected Article objects and prints them out. |
| * @param article the root of the article 'tree' |
| * @param depth the current tree depth |
| * @param ps the PrintStream to use |
| * @since 3.4 |
| */ |
| public static void printThread(final Article article, final int depth, final PrintStream ps) { |
| for (int i = 0; i < depth; ++i) { |
| ps.print("==>"); |
| } |
| ps.println(article.getSubject() + "\t" + article.getFrom()+"\t"+article.getArticleId()); |
| if (article.kid != null) { |
| printThread(article.kid, depth + 1); |
| } |
| if (article.next != null) { |
| printThread(article.next, depth); |
| } |
| } |
| /** |
| * Recursive method that traverses a pre-threaded graph (or tree) |
| * of connected Article objects and prints them out. |
| * @param article the root of the article 'tree' |
| * @param ps the PrintStream to use |
| * @since 3.4 |
| */ |
| public static void printThread(final Article article, final PrintStream ps) { |
| printThread(article, 0, ps); |
| } |
| private long articleNumber; |
| private String subject; |
| private String date; |
| private String articleId; |
| |
| private String simplifiedSubject; |
| |
| private String from; |
| private ArrayList<String> references; |
| |
| private boolean isReply; |
| |
| public Article kid, next; |
| |
| public Article() { |
| articleNumber = -1; // isDummy |
| } |
| |
| @Deprecated |
| |
| public void addHeaderField(final String name, final String val) { |
| } |
| |
| /** |
| * Adds a message-id to the list of messages that this message references (i.e. replies to) |
| * @param msgId the message id to add |
| */ |
| public void addReference(final String msgId) { |
| if (msgId == null || msgId.isEmpty()) { |
| return; |
| } |
| if (references == null) { |
| references = new ArrayList<>(); |
| } |
| isReply = true; |
| Collections.addAll(references, msgId.split(" ")); |
| } |
| |
| private void flushSubjectCache() { |
| simplifiedSubject = null; |
| } |
| |
| public String getArticleId() { |
| return articleId; |
| } |
| |
| @Deprecated |
| public int getArticleNumber() { |
| return (int) articleNumber; |
| } |
| |
| public long getArticleNumberLong() { |
| return articleNumber; |
| } |
| |
| public String getDate() { |
| return date; |
| } |
| |
| public String getFrom() { |
| return from; |
| } |
| |
| /** |
| * Returns the MessageId references as an array of Strings |
| * @return an array of message-ids |
| */ |
| public String[] getReferences() { |
| if (references == null) { |
| return NetConstants.EMPTY_STRING_ARRAY; |
| } |
| return references.toArray(NetConstants.EMPTY_STRING_ARRAY); |
| } |
| |
| public String getSubject() { |
| return subject; |
| } |
| |
| @Override |
| public boolean isDummy() { |
| return (articleNumber == -1); |
| } |
| |
| @Override |
| public Threadable makeDummy() { |
| return new Article(); |
| } |
| |
| @Override |
| public String messageThreadId() { |
| return articleId; |
| } |
| |
| |
| @Override |
| public String[] messageThreadReferences() { |
| return getReferences(); |
| } |
| |
| public void setArticleId(final String string) { |
| articleId = string; |
| } |
| |
| @Deprecated |
| public void setArticleNumber(final int a) { |
| articleNumber = a; |
| } |
| |
| public void setArticleNumber(final long l) { |
| articleNumber = l; |
| } |
| |
| |
| @Override |
| public void setChild(final Threadable child) { |
| this.kid = (Article) child; |
| flushSubjectCache(); |
| } |
| |
| |
| public void setDate(final String string) { |
| date = string; |
| } |
| |
| public void setFrom(final String string) { |
| from = string; |
| } |
| |
| |
| @Override |
| public void setNext(final Threadable next) { |
| this.next = (Article)next; |
| flushSubjectCache(); |
| } |
| |
| |
| public void setSubject(final String string) { |
| subject = string; |
| } |
| |
| @Override |
| public String simplifiedSubject() { |
| if(simplifiedSubject == null) { |
| simplifySubject(); |
| } |
| return simplifiedSubject; |
| } |
| |
| // DEPRECATED METHODS - for API compatibility only - DO NOT USE |
| |
| /** |
| * Attempts to parse the subject line for some typical reply signatures, and strip them out |
| * |
| */ |
| private void simplifySubject() { |
| int start = 0; |
| final String subject = getSubject(); |
| final int len = subject.length(); |
| |
| boolean done = false; |
| |
| while (!done) { |
| done = true; |
| |
| // skip whitespace |
| // "Re: " breaks this |
| while (start < len && subject.charAt(start) == ' ') { |
| start++; |
| } |
| |
| if (start < (len - 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 "Re:" |
| done = false; |
| } else if ( |
| start < (len - 2) |
| && |
| (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) { |
| |
| int i = start + 3; |
| |
| while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9') { |
| i++; |
| } |
| |
| if (i < (len - 1) |
| && (subject.charAt(i) == ']' || subject.charAt(i) == ')') |
| && subject.charAt(i + 1) == ':') |
| { |
| start = i + 2; |
| done = false; |
| } |
| } |
| } |
| |
| if ("(no subject)".equals(simplifiedSubject)) { |
| simplifiedSubject = ""; |
| } |
| |
| int end = len; |
| |
| while (end > start && subject.charAt(end - 1) < ' ') { |
| end--; |
| } |
| |
| if (start == 0 && end == len) { |
| simplifiedSubject = subject; |
| } else { |
| simplifiedSubject = subject.substring(start, end); |
| } |
| } |
| } |
| |
| @Override |
| public boolean subjectIsReply() { |
| return isReply; |
| } |
| @Override |
| public String toString(){ // Useful for Eclipse debugging |
| return articleNumber + " " +articleId + " " + subject; |
| } |
| |
| } |