    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());
    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    }