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                            if (i < (len - 1)
109                                && (subject.charAt(i) == ']' || subject.charAt(i) == ')')
110                                && subject.charAt(i + 1) == ':') {
111                                start = i + 2;
112                                done = false;
113                            }
114                        }
115                    }
116    
117                    if ("(no subject)".equals(simplifiedSubject))
118                        simplifiedSubject = "";
119    
120                    int end = len;
121    
122                    while (end > start && subject.charAt(end - 1) < ' ')
123                        end--;
124    
125                    if (start == 0 && end == len)
126                        simplifiedSubject = subject;
127                    else
128                        simplifiedSubject = subject.substring(start, end);
129                }
130            }
131    
132        /**
133         * Recursive method that traverses a pre-threaded graph (or tree)
134         * of connected Article objects and prints them out.
135         * @param article the root of the article 'tree'
136         * @param depth the current tree depth
137         */
138        public static void printThread(Article article, int depth) {
139                for (int i = 0; i < depth; ++i)
140                    System.out.print("==>");
141                System.out.println(article.getSubject() + "\t" + article.getFrom());
142                if (article.kid != null)
143                    printThread(article.kid, depth + 1);
144                if (article.next != null)
145                    printThread(article.next, depth);
146        }
147    
148        public String getArticleId() {
149            return articleId;
150        }
151    
152        public long getArticleNumberLong() {
153            return articleNumber;
154        }
155    
156        public String getDate() {
157            return date;
158        }
159    
160        public String getFrom() {
161            return from;
162        }
163    
164        public String getSubject() {
165            return subject;
166        }
167    
168        public void setArticleId(String string) {
169            articleId = string;
170        }
171    
172        public void setArticleNumber(long l) {
173            articleNumber = l;
174        }
175    
176        public void setDate(String string) {
177            date = string;
178        }
179    
180        public void setFrom(String string) {
181            from = string;
182        }
183    
184        public void setSubject(String string) {
185            subject = string;
186        }
187    
188    
189        public boolean isDummy() {
190            return (articleNumber == -1);
191        }
192    
193        public String messageThreadId() {
194            return articleId;
195        }
196    
197        public String[] messageThreadReferences() {
198            return getReferences();
199        }
200    
201        public String simplifiedSubject() {
202            if(simplifiedSubject == null)
203                simplifySubject();
204            return simplifiedSubject;
205        }
206    
207    
208        public boolean subjectIsReply() {
209            return isReply;
210        }
211    
212    
213        public void setChild(Threadable child) {
214            this.kid = (Article) child;
215            flushSubjectCache();
216        }
217    
218        private void flushSubjectCache() {
219            simplifiedSubject = null;
220        }
221    
222    
223        public void setNext(Threadable next) {
224            this.next = (Article)next;
225            flushSubjectCache();
226        }
227    
228    
229        public Threadable makeDummy() {
230            return new Article();
231        }
232    
233        @Override
234        public String toString(){ // Useful for Eclipse debugging
235            return articleNumber + " " +articleId + " " + subject;
236        }
237    
238        // DEPRECATED METHODS - for API compatibility only - DO NOT USE
239    
240        @Deprecated
241        public int getArticleNumber() {
242            return (int) articleNumber;
243        }
244    
245        @Deprecated
246        public void setArticleNumber(int a) {
247            articleNumber = a;
248        }
249        @Deprecated
250    
251        public void addHeaderField(String name, String val) {
252        }
253    
254    }