View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.nntp;
19  
20  import java.io.PrintStream;
21  import java.util.ArrayList;
22  
23  /**
24   * This is a class that contains the basic state needed for message retrieval and threading.
25   * With thanks to Jamie  Zawinski (jwz@jwz.org)
26   */
27  public class Article implements Threadable {
28      private long articleNumber;
29      private String subject;
30      private String date;
31      private String articleId;
32      private String simplifiedSubject;
33      private String from;
34      private ArrayList<String> references;
35      private boolean isReply = false;
36  
37      public Article kid, next;
38  
39      public Article() {
40          articleNumber = -1; // isDummy
41      }
42  
43      /**
44       * Adds a message-id to the list of messages that this message references (i.e. replies to)
45       * @param msgId the message id to add
46       */
47      public void addReference(String msgId) {
48          if (msgId == null || msgId.length() == 0) {
49              return;
50          }
51          if (references == null) {
52              references = new ArrayList<String>();
53          }
54          isReply = true;
55          for(String s : msgId.split(" ")) {
56              references.add(s);
57          }
58      }
59  
60      /**
61       * Returns the MessageId references as an array of Strings
62       * @return an array of message-ids
63       */
64      public String[] getReferences() {
65          if (references == null) {
66              return new String[0];
67          }
68          return references.toArray(new String[references.size()]);
69      }
70  
71      /**
72       * Attempts to parse the subject line for some typical reply signatures, and strip them out
73       *
74       */
75      private void simplifySubject() {
76              int start = 0;
77              String subject = getSubject();
78              int len = subject.length();
79  
80              boolean done = false;
81  
82              while (!done) {
83                  done = true;
84  
85                  // skip whitespace
86                  // "Re: " breaks this
87                  while (start < len && subject.charAt(start) == ' ') {
88                      start++;
89                  }
90  
91                  if (start < (len - 2)
92                      && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
93                      && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {
94  
95                      if (subject.charAt(start + 2) == ':') {
96                          start += 3; // Skip "Re:"
97                          done = false;
98                      } else if (
99                          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 }