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
018 package org.apache.commons.net.nntp;
019
020 import java.util.ArrayList;
021
022 /**
023 * This is a class that contains the basic state needed for message retrieval and threading.
024 * With thanks to Jamie Zawinski <jwz@jwz.org>
025 * @author rwinston <rwinston@apache.org>
026 */
027 public 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
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 * @param depth the current tree depth
142 */
143 public static void printThread(Article article, int depth) {
144 for (int i = 0; i < depth; ++i) {
145 System.out.print("==>");
146 }
147 System.out.println(article.getSubject() + "\t" + article.getFrom()+"\t"+article.getArticleId());
148 if (article.kid != null) {
149 printThread(article.kid, depth + 1);
150 }
151 if (article.next != null) {
152 printThread(article.next, depth);
153 }
154 }
155
156 public String getArticleId() {
157 return articleId;
158 }
159
160 public long getArticleNumberLong() {
161 return articleNumber;
162 }
163
164 public String getDate() {
165 return date;
166 }
167
168 public String getFrom() {
169 return from;
170 }
171
172 public String getSubject() {
173 return subject;
174 }
175
176 public void setArticleId(String string) {
177 articleId = string;
178 }
179
180 public void setArticleNumber(long l) {
181 articleNumber = l;
182 }
183
184 public void setDate(String string) {
185 date = string;
186 }
187
188 public void setFrom(String string) {
189 from = string;
190 }
191
192 public void setSubject(String string) {
193 subject = string;
194 }
195
196
197 public boolean isDummy() {
198 return (articleNumber == -1);
199 }
200
201 public String messageThreadId() {
202 return articleId;
203 }
204
205 public String[] messageThreadReferences() {
206 return getReferences();
207 }
208
209 public String simplifiedSubject() {
210 if(simplifiedSubject == null) {
211 simplifySubject();
212 }
213 return simplifiedSubject;
214 }
215
216
217 public boolean subjectIsReply() {
218 return isReply;
219 }
220
221
222 public void setChild(Threadable child) {
223 this.kid = (Article) child;
224 flushSubjectCache();
225 }
226
227 private void flushSubjectCache() {
228 simplifiedSubject = null;
229 }
230
231
232 public void setNext(Threadable next) {
233 this.next = (Article)next;
234 flushSubjectCache();
235 }
236
237
238 public Threadable makeDummy() {
239 return new Article();
240 }
241
242 @Override
243 public String toString(){ // Useful for Eclipse debugging
244 return articleNumber + " " +articleId + " " + subject;
245 }
246
247 // DEPRECATED METHODS - for API compatibility only - DO NOT USE
248
249 @Deprecated
250 public int getArticleNumber() {
251 return (int) articleNumber;
252 }
253
254 @Deprecated
255 public void setArticleNumber(int a) {
256 articleNumber = a;
257 }
258 @Deprecated
259
260 public void addHeaderField(String name, String val) {
261 }
262
263 }