    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
    018package org.apache.commons.net.nntp;
    019
    020import java.io.PrintStream;
    021import java.util.ArrayList;
    022
    023/**
    024 * This is a class that contains the basic state needed for message retrieval and threading.
    025 * With thanks to Jamie  Zawinski (jwz@jwz.org)
    026 */
    027public 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 the message id to add
    046     */
    047    public void addReference(final String msgId) {
    048        if (msgId == null || msgId.length() == 0) {
    049            return;
    050        }
    051        if (references == null) {
    052            references = new ArrayList<>();
    053        }
    054        isReply = true;
    055        for(final 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            final String subject = getSubject();
    078            final 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     * @since 3.4
    142     */
    143    public static void printThread(final Article article) {
    144        printThread(article, 0, System.out);
    145    }
    146
    147    /**
    148     * Recursive method that traverses a pre-threaded graph (or tree)
    149     * of connected Article objects and prints them out.
    150     * @param article the root of the article 'tree'
    151     * @param ps the PrintStream to use
    152     * @since 3.4
    153     */
    154    public static void printThread(final Article article, final PrintStream ps) {
    155        printThread(article, 0, ps);
    156    }
    157
    158    /**
    159     * Recursive method that traverses a pre-threaded graph (or tree)
    160     * of connected Article objects and prints them out.
    161     * @param article the root of the article 'tree'
    162     * @param depth the current tree depth
    163     */
    164    public static void printThread(final Article article, final int depth) {
    165        printThread(article, depth, System.out);
    166    }
    167
    168    /**
    169     * Recursive method that traverses a pre-threaded graph (or tree)
    170     * of connected Article objects and prints them out.
    171     * @param article the root of the article 'tree'
    172     * @param depth the current tree depth
    173     * @param ps the PrintStream to use
    174     * @since 3.4
    175     */
    176    public static void printThread(final Article article, final int depth, final PrintStream ps) {
    177            for (int i = 0; i < depth; ++i) {
    178                ps.print("==>");
    179            }
    180            ps.println(article.getSubject() + "\t" + article.getFrom()+"\t"+article.getArticleId());
    181            if (article.kid != null) {
    182                printThread(article.kid, depth + 1);
    183            }
    184            if (article.next != null) {
    185                printThread(article.next, depth);
    186            }
    187    }
    188
    189    public String getArticleId() {
    190        return articleId;
    191    }
    192
    193    public long getArticleNumberLong() {
    194        return articleNumber;
    195    }
    196
    197    public String getDate() {
    198        return date;
    199    }
    200
    201    public String getFrom() {
    202        return from;
    203    }
    204
    205    public String getSubject() {
    206        return subject;
    207    }
    208
    209    public void setArticleId(final String string) {
    210        articleId = string;
    211    }
    212
    213    public void setArticleNumber(final long l) {
    214        articleNumber = l;
    215    }
    216
    217    public void setDate(final String string) {
    218        date = string;
    219    }
    220
    221    public void setFrom(final String string) {
    222        from = string;
    223    }
    224
    225    public void setSubject(final String string) {
    226        subject = string;
    227    }
    228
    229
    230    @Override
    231    public boolean isDummy() {
    232        return (articleNumber == -1);
    233    }
    234
    235    @Override
    236    public String messageThreadId() {
    237        return articleId;
    238    }
    239
    240    @Override
    241    public String[] messageThreadReferences() {
    242        return getReferences();
    243    }
    244
    245    @Override
    246    public String simplifiedSubject() {
    247        if(simplifiedSubject == null) {
    248            simplifySubject();
    249        }
    250        return simplifiedSubject;
    251    }
    252
    253
    254    @Override
    255    public boolean subjectIsReply() {
    256        return isReply;
    257    }
    258
    259
    260    @Override
    261    public void setChild(final Threadable child) {
    262        this.kid = (Article) child;
    263        flushSubjectCache();
    264    }
    265
    266    private void flushSubjectCache() {
    267        simplifiedSubject = null;
    268    }
    269
    270
    271    @Override
    272    public void setNext(final Threadable next) {
    273        this.next = (Article)next;
    274        flushSubjectCache();
    275    }
    276
    277
    278    @Override
    279    public Threadable makeDummy() {
    280        return new Article();
    281    }
    282
    283    @Override
    284    public String toString(){ // Useful for Eclipse debugging
    285        return articleNumber + " " +articleId + " " + subject;
    286    }
    287
    288    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
    289
    290    @Deprecated
    291    public int getArticleNumber() {
    292        return (int) articleNumber;
    293    }
    294
    295    @Deprecated
    296    public void setArticleNumber(final int a) {
    297        articleNumber = a;
    298    }
    299    @Deprecated
    300
    301    public void addHeaderField(final String name, final String val) {
    302    }
    303
    304}