001: /* ====================================================================
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2001-2005 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "Apache" and "Apache Software Foundation" and
027: * "Apache Commons" must not be used to endorse or promote products
028: * derived from this software without prior written permission. For
029: * written permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache",
032: * nor may "Apache" appear in their name, without
033: * prior written permission of the Apache Software Foundation.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: */
054:
055: package org.apache.commons.net.nntp;
056:
057: import java.util.ArrayList;
058: import java.util.StringTokenizer;
059:
060: /**
061: * This is a class that contains the basic state needed for message retrieval and threading.
062: * With thanks to Jamie Zawinski <jwz@jwz.org>
063: * @author rwinston <rwinston@checkfree.com>
064: *
065: */
066: public class Article implements Threadable {
067: private int articleNumber;
068: private String subject;
069: private String date;
070: private String articleId;
071: private String simplifiedSubject;
072: private String from;
073: private StringBuffer header;
074: private StringBuffer references;
075: private boolean isReply = false;
076:
077: public Article kid, next;
078:
079: public Article() {
080: header = new StringBuffer();
081: }
082:
083: /**
084: * Adds an arbitrary header key and value to this message's header.
085: * @param name the header name
086: * @param val the header value
087: */
088: public void addHeaderField(String name, String val) {
089: header.append(name);
090: header.append(": ");
091: header.append(val);
092: header.append('\n');
093: }
094:
095: /**
096: * Adds a message-id to the list of messages that this message references (i.e. replies to)
097: * @param msgId
098: */
099: 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 = new StringTokenizer(references
118: .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
147: .charAt(start) == 'R')
148: && (subject.charAt(start + 1) == 'e' || subject
149: .charAt(start + 1) == 'E')) {
150:
151: if (subject.charAt(start + 2) == ':') {
152: start += 3; // Skip "Re:"
153: isReply = true;
154: done = false;
155: } else if (start < (len - 2)
156: && (subject.charAt(start + 2) == '[' || subject
157: .charAt(start + 2) == '(')) {
158:
159: int i = start + 3;
160:
161: while (i < len && subject.charAt(i) >= '0'
162: && subject.charAt(i) <= '9')
163: i++;
164:
165: if (i < (len - 1)
166: && (subject.charAt(i) == ']' || subject
167: .charAt(i) == ')')
168: && subject.charAt(i + 1) == ':') {
169: start = i + 2;
170: isReply = true;
171: done = false;
172: }
173: }
174: }
175:
176: if (simplifiedSubject == "(no subject)")
177: simplifiedSubject = "";
178:
179: int end = len;
180:
181: while (end > start && subject.charAt(end - 1) < ' ')
182: end--;
183:
184: if (start == 0 && end == len)
185: simplifiedSubject = subject;
186: else
187: simplifiedSubject = subject.substring(start, end);
188: }
189: }
190:
191: /**
192: * Recursive method that traverses a pre-threaded graph (or tree)
193: * of connected Article objects and prints them out.
194: * @param article the root of the article 'tree'
195: * @param depth the current tree depth
196: */
197: public static void printThread(Article article, int depth) {
198: for (int i = 0; i < depth; ++i)
199: System.out.print("==>");
200: System.out.println(article.getSubject() + "\t"
201: + article.getFrom());
202: if (article.kid != null)
203: printThread(article.kid, depth + 1);
204: if (article.next != null)
205: printThread(article.next, depth);
206: }
207:
208: public String getArticleId() {
209: return articleId;
210: }
211:
212: public int getArticleNumber() {
213: return articleNumber;
214: }
215:
216: public String getDate() {
217: return date;
218: }
219:
220: public String getFrom() {
221: return from;
222: }
223:
224: public String getSubject() {
225: return subject;
226: }
227:
228: public void setArticleId(String string) {
229: articleId = string;
230: }
231:
232: public void setArticleNumber(int i) {
233: articleNumber = i;
234: }
235:
236: public void setDate(String string) {
237: date = string;
238: }
239:
240: public void setFrom(String string) {
241: from = string;
242: }
243:
244: public void setSubject(String string) {
245: subject = string;
246: }
247:
248: public boolean isDummy() {
249: return (getSubject() == null);
250: }
251:
252: public String messageThreadId() {
253: return articleId;
254: }
255:
256: public String[] messageThreadReferences() {
257: return getReferences();
258: }
259:
260: public String simplifiedSubject() {
261: if (simplifiedSubject == null)
262: simplifySubject();
263: return simplifiedSubject;
264: }
265:
266: public boolean subjectIsReply() {
267: if (simplifiedSubject == null)
268: simplifySubject();
269: return isReply;
270: }
271:
272: public void setChild(Threadable child) {
273: this .kid = (Article) child;
274: flushSubjectCache();
275: }
276:
277: private void flushSubjectCache() {
278: simplifiedSubject = null;
279: }
280:
281: public void setNext(Threadable next) {
282: this .next = (Article) next;
283: flushSubjectCache();
284: }
285:
286: public Threadable makeDummy() {
287: return (Threadable) new Article();
288: }
289: }
|