    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    }