1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 package org.apache.commons.net.nntp;
56
57 import java.util.ArrayList;
58 import java.util.StringTokenizer;
59
60 /**
61 * This is a class that contains the basic state needed for message retrieval and threading.
62 * With thanks to Jamie Zawinski <jwz@jwz.org>
63 * @author rwinston <rwinston@checkfree.com>
64 *
65 */
66 public class Article implements Threadable {
67 private int articleNumber;
68 private String subject;
69 private String date;
70 private String articleId;
71 private String simplifiedSubject;
72 private String from;
73 private StringBuffer header;
74 private StringBuffer references;
75 private boolean isReply = false;
76
77 public Article kid, next;
78
79 public Article() {
80 header = new StringBuffer();
81 }
82
83 /**
84 * Adds an arbitrary header key and value to this message's header.
85 * @param name the header name
86 * @param val the header value
87 */
88 public void addHeaderField(String name, String val) {
89 header.append(name);
90 header.append(": ");
91 header.append(val);
92 header.append('\n');
93 }
94
95 /**
96 * Adds a message-id to the list of messages that this message references (i.e. replies to)
97 * @param msgId
98 */
99 public void addReference(String msgId) {
100 if (references == null) {
101 references = new StringBuffer();
102 references.append("References: ");
103 }
104 references.append(msgId);
105 references.append("\t");
106 }
107
108 /**
109 * Returns the MessageId references as an array of Strings
110 * @return an array of message-ids
111 */
112 public String[] getReferences() {
113 if (references == null)
114 return new String[0];
115 ArrayList list = new ArrayList();
116 int terminator = references.toString().indexOf(':');
117 StringTokenizer st =
118 new StringTokenizer(references.substring(terminator), "\t");
119 while (st.hasMoreTokens()) {
120 list.add(st.nextToken());
121 }
122 return (String[]) list.toArray();
123 }
124
125 /**
126 * Attempts to parse the subject line for some typical reply signatures, and strip them out
127 *
128 */
129 private void simplifySubject() {
130 int start = 0;
131 String subject = getSubject();
132 int len = subject.length();
133
134 boolean done = false;
135
136 while (!done) {
137 done = true;
138
139
140
141 while (start < len && subject.charAt(start) == ' ') {
142 start++;
143 }
144
145 if (start < (len - 2)
146 && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
147 && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {
148
149 if (subject.charAt(start + 2) == ':') {
150 start += 3;
151 isReply = true;
152 done = false;
153 } else if (
154 start < (len - 2)
155 &&
156 (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {
157
158 int i = start + 3;
159
160 while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
161 i++;
162
163 if (i < (len - 1)
164 && (subject.charAt(i) == ']' || subject.charAt(i) == ')')
165 && subject.charAt(i + 1) == ':') {
166 start = i + 2;
167 isReply = true;
168 done = false;
169 }
170 }
171 }
172
173 if (simplifiedSubject == "(no subject)")
174 simplifiedSubject = "";
175
176 int end = len;
177
178 while (end > start && subject.charAt(end - 1) < ' ')
179 end--;
180
181 if (start == 0 && end == len)
182 simplifiedSubject = subject;
183 else
184 simplifiedSubject = subject.substring(start, end);
185 }
186 }
187
188 /**
189 * Recursive method that traverses a pre-threaded graph (or tree)
190 * of connected Article objects and prints them out.
191 * @param article the root of the article 'tree'
192 * @param depth the current tree depth
193 */
194 public static void printThread(Article article, int depth) {
195 for (int i = 0; i < depth; ++i)
196 System.out.print("==>");
197 System.out.println(article.getSubject() + "\t" + article.getFrom());
198 if (article.kid != null)
199 printThread(article.kid, depth + 1);
200 if (article.next != null)
201 printThread(article.next, depth);
202 }
203
204 public String getArticleId() {
205 return articleId;
206 }
207
208 public int getArticleNumber() {
209 return articleNumber;
210 }
211
212 public String getDate() {
213 return date;
214 }
215
216 public String getFrom() {
217 return from;
218 }
219
220 public String getSubject() {
221 return subject;
222 }
223
224 public void setArticleId(String string) {
225 articleId = string;
226 }
227
228 public void setArticleNumber(int i) {
229 articleNumber = i;
230 }
231
232 public void setDate(String string) {
233 date = string;
234 }
235
236 public void setFrom(String string) {
237 from = string;
238 }
239
240 public void setSubject(String string) {
241 subject = string;
242 }
243
244
245 public boolean isDummy() {
246 return (getSubject() == null);
247 }
248
249 public String messageThreadId() {
250 return articleId;
251 }
252
253 public String[] messageThreadReferences() {
254 return getReferences();
255 }
256
257 public String simplifiedSubject() {
258 if(simplifiedSubject == null)
259 simplifySubject();
260 return simplifiedSubject;
261 }
262
263
264 public boolean subjectIsReply() {
265 if(simplifiedSubject == null)
266 simplifySubject();
267 return isReply;
268 }
269
270
271 public void setChild(Threadable child) {
272 this.kid = (Article) child;
273 flushSubjectCache();
274 }
275
276 private void flushSubjectCache() {
277 simplifiedSubject = null;
278 }
279
280
281 public void setNext(Threadable next) {
282 this.next = (Article)next;
283 flushSubjectCache();
284 }
285
286
287 public Threadable makeDummy() {
288 return (Threadable)new Article();
289 }
290 }