View Javadoc

1   /* ====================================================================
2    * The Apache Software License, Version 1.1
3    *
4    * Copyright (c) 2001-2005 The Apache Software Foundation.  All rights
5    * reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer.
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution,
20   *    if any, must include the following acknowledgment:
21   *       "This product includes software developed by the
22   *        Apache Software Foundation (http://www.apache.org/)."
23   *    Alternately, this acknowledgment may appear in the software itself,
24   *    if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The names "Apache" and "Apache Software Foundation" and
27   *    "Apache Commons" must not be used to endorse or promote products
28   *    derived from this software without prior written permission. For
29   *    written permission, please contact apache@apache.org.
30   *
31   * 5. Products derived from this software may not be called "Apache",
32   *    nor may "Apache" appear in their name, without
33   *    prior written permission of the Apache Software Foundation.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46   * SUCH DAMAGE.
47   * ====================================================================
48   *
49   * This software consists of voluntary contributions made by many
50   * individuals on behalf of the Apache Software Foundation.  For more
51   * information on the Apache Software Foundation, please see
52   * <http://www.apache.org/>.
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 				// skip whitespace
140 				// "Re: " breaks this
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; // Skip "Re:"
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 }