001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.nntp; 019 020import java.io.PrintStream; 021import java.util.ArrayList; 022 023/** 024 * This is a class that contains the basic state needed for message retrieval and threading. 025 * With thanks to Jamie Zawinski (jwz@jwz.org) 026 */ 027public class Article implements Threadable { 028 private long articleNumber; 029 private String subject; 030 private String date; 031 private String articleId; 032 private String simplifiedSubject; 033 private String from; 034 private ArrayList<String> references; 035 private boolean isReply = false; 036 037 public Article kid, next; 038 039 public Article() { 040 articleNumber = -1; // isDummy 041 } 042 043 /** 044 * Adds a message-id to the list of messages that this message references (i.e. replies to) 045 * @param msgId the message id to add 046 */ 047 public void addReference(String msgId) { 048 if (msgId == null || msgId.length() == 0) { 049 return; 050 } 051 if (references == null) { 052 references = new ArrayList<String>(); 053 } 054 isReply = true; 055 for(String s : msgId.split(" ")) { 056 references.add(s); 057 } 058 } 059 060 /** 061 * Returns the MessageId references as an array of Strings 062 * @return an array of message-ids 063 */ 064 public String[] getReferences() { 065 if (references == null) { 066 return new String[0]; 067 } 068 return references.toArray(new String[references.size()]); 069 } 070 071 /** 072 * Attempts to parse the subject line for some typical reply signatures, and strip them out 073 * 074 */ 075 private void simplifySubject() { 076 int start = 0; 077 String subject = getSubject(); 078 int len = subject.length(); 079 080 boolean done = false; 081 082 while (!done) { 083 done = true; 084 085 // skip whitespace 086 // "Re: " breaks this 087 while (start < len && subject.charAt(start) == ' ') { 088 start++; 089 } 090 091 if (start < (len - 2) 092 && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R') 093 && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) { 094 095 if (subject.charAt(start + 2) == ':') { 096 start += 3; // Skip "Re:" 097 done = false; 098 } else if ( 099 start < (len - 2) 100 && 101 (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) { 102 103 int i = start + 3; 104 105 while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9') { 106 i++; 107 } 108 109 if (i < (len - 1) 110 && (subject.charAt(i) == ']' || subject.charAt(i) == ')') 111 && subject.charAt(i + 1) == ':') 112 { 113 start = i + 2; 114 done = false; 115 } 116 } 117 } 118 119 if ("(no subject)".equals(simplifiedSubject)) { 120 simplifiedSubject = ""; 121 } 122 123 int end = len; 124 125 while (end > start && subject.charAt(end - 1) < ' ') { 126 end--; 127 } 128 129 if (start == 0 && end == len) { 130 simplifiedSubject = subject; 131 } else { 132 simplifiedSubject = subject.substring(start, end); 133 } 134 } 135 } 136 137 /** 138 * Recursive method that traverses a pre-threaded graph (or tree) 139 * of connected Article objects and prints them out. 140 * @param article the root of the article 'tree' 141 * @since 3.4 142 */ 143 public static void printThread(Article article) { 144 printThread(article, 0, System.out); 145 } 146 147 /** 148 * Recursive method that traverses a pre-threaded graph (or tree) 149 * of connected Article objects and prints them out. 150 * @param article the root of the article 'tree' 151 * @param ps the PrintStream to use 152 * @since 3.4 153 */ 154 public static void printThread(Article article, PrintStream ps) { 155 printThread(article, 0, ps); 156 } 157 158 /** 159 * Recursive method that traverses a pre-threaded graph (or tree) 160 * of connected Article objects and prints them out. 161 * @param article the root of the article 'tree' 162 * @param depth the current tree depth 163 */ 164 public static void printThread(Article article, int depth) { 165 printThread(article, depth, System.out); 166 } 167 168 /** 169 * Recursive method that traverses a pre-threaded graph (or tree) 170 * of connected Article objects and prints them out. 171 * @param article the root of the article 'tree' 172 * @param depth the current tree depth 173 * @param ps the PrintStream to use 174 * @since 3.4 175 */ 176 public static void printThread(Article article, int depth, PrintStream ps) { 177 for (int i = 0; i < depth; ++i) { 178 ps.print("==>"); 179 } 180 ps.println(article.getSubject() + "\t" + article.getFrom()+"\t"+article.getArticleId()); 181 if (article.kid != null) { 182 printThread(article.kid, depth + 1); 183 } 184 if (article.next != null) { 185 printThread(article.next, depth); 186 } 187 } 188 189 public String getArticleId() { 190 return articleId; 191 } 192 193 public long getArticleNumberLong() { 194 return articleNumber; 195 } 196 197 public String getDate() { 198 return date; 199 } 200 201 public String getFrom() { 202 return from; 203 } 204 205 public String getSubject() { 206 return subject; 207 } 208 209 public void setArticleId(String string) { 210 articleId = string; 211 } 212 213 public void setArticleNumber(long l) { 214 articleNumber = l; 215 } 216 217 public void setDate(String string) { 218 date = string; 219 } 220 221 public void setFrom(String string) { 222 from = string; 223 } 224 225 public void setSubject(String string) { 226 subject = string; 227 } 228 229 230 @Override 231 public boolean isDummy() { 232 return (articleNumber == -1); 233 } 234 235 @Override 236 public String messageThreadId() { 237 return articleId; 238 } 239 240 @Override 241 public String[] messageThreadReferences() { 242 return getReferences(); 243 } 244 245 @Override 246 public String simplifiedSubject() { 247 if(simplifiedSubject == null) { 248 simplifySubject(); 249 } 250 return simplifiedSubject; 251 } 252 253 254 @Override 255 public boolean subjectIsReply() { 256 return isReply; 257 } 258 259 260 @Override 261 public void setChild(Threadable child) { 262 this.kid = (Article) child; 263 flushSubjectCache(); 264 } 265 266 private void flushSubjectCache() { 267 simplifiedSubject = null; 268 } 269 270 271 @Override 272 public void setNext(Threadable next) { 273 this.next = (Article)next; 274 flushSubjectCache(); 275 } 276 277 278 @Override 279 public Threadable makeDummy() { 280 return new Article(); 281 } 282 283 @Override 284 public String toString(){ // Useful for Eclipse debugging 285 return articleNumber + " " +articleId + " " + subject; 286 } 287 288 // DEPRECATED METHODS - for API compatibility only - DO NOT USE 289 290 @Deprecated 291 public int getArticleNumber() { 292 return (int) articleNumber; 293 } 294 295 @Deprecated 296 public void setArticleNumber(int a) { 297 articleNumber = a; 298 } 299 @Deprecated 300 301 public void addHeaderField(String name, String val) { 302 } 303 304}