View Javadoc

    1   /* ====================================================================
    2    * The Apache Software License, Version 1.1
    3    *
    4    * Copyright (c) 2001-2005 The Apache Software Foundation.  All rights
    5    * reserved.
    6    *
    7    * Redistribution and use in source and binary forms, with or without
    8    * modification, are permitted provided that the following conditions
    9    * are met:
    10   *
    11   * 1. Redistributions of source code must retain the above copyright
    12   *    notice, this list of conditions and the following disclaimer.
    13   *
    14   * 2. Redistributions in binary form must reproduce the above copyright
    15   *    notice, this list of conditions and the following disclaimer in
    16   *    the documentation and/or other materials provided with the
    17   *    distribution.
    18   *
    19   * 3. The end-user documentation included with the redistribution,
    20   *    if any, must include the following acknowledgment:
    21   *       "This product includes software developed by the
    22   *        Apache Software Foundation (http://www.apache.org/)."
    23   *    Alternately, this acknowledgment may appear in the software itself,
    24   *    if and wherever such third-party acknowledgments normally appear.
    25   *
    26   * 4. The names "Apache" and "Apache Software Foundation" and
    27   *    "Apache Commons" must not be used to endorse or promote products
    28   *    derived from this software without prior written permission. For
    29   *    written permission, please contact apache@apache.org.
    30   *
    31   * 5. Products derived from this software may not be called "Apache",
    32   *    nor may "Apache" appear in their name, without
    33   *    prior written permission of the Apache Software Foundation.
    34   *
    35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    38   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
    39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    46   * SUCH DAMAGE.
    47   * ====================================================================
    48   *
    49   * This software consists of voluntary contributions made by many
    50   * individuals on behalf of the Apache Software Foundation.  For more
    51   * information on the Apache Software Foundation, please see
    52   * <http://www.apache.org/>.
    53   */
    54
    55  package org.apache.commons.net.nntp;
    56
    57  import java.util.ArrayList;
    58  import java.util.StringTokenizer;
    59
    60  /***
    61   * This is a class that contains the basic state needed for message retrieval and threading.
    62   * With thanks to Jamie  Zawinski <jwz@jwz.org>
    63   * @author rwinston <rwinston@checkfree.com>
    64   *
    65   */
    66  public class Article implements Threadable {
    67  	private int articleNumber;
    68  	private String subject;
    69  	private String date;
    70  	private String articleId;
    71  	private String simplifiedSubject;
    72  	private String from;
    73  	private StringBuffer header;
    74  	private StringBuffer references;
    75  	private boolean isReply = false;
    76
    77  	public Article kid, next;
    78
    79  	public Article() {
    80  		header = new StringBuffer();
    81  	}
    82
    83  	/***
    84  	 * Adds an arbitrary header key and value to this message's header.
    85  	 * @param name the header name
    86  	 * @param val the header value
    87  	 */
    88  	public void addHeaderField(String name, String val) {
    89  		header.append(name);
    90  		header.append(": ");
    91  		header.append(val);
    92  		header.append('\n');
    93  	}
    94
    95  	/***
    96  	 * Adds a message-id to the list of messages that this message references (i.e. replies to)
    97  	 * @param msgId
    98  	 */
    99  	public void addReference(String msgId) {
    100 		if (references == null) {
    101 			references = new StringBuffer();
    102 			references.append("References: ");
    103 		}
    104 		references.append(msgId);
    105 		references.append("\t");
    106 	}
    107
    108 	/***
    109 	 * Returns the MessageId references as an array of Strings
    110 	 * @return an array of message-ids
    111 	 */
    112 	public String[] getReferences() {
    113 		if (references == null)
    114 			return new String[0];
    115 		ArrayList list = new ArrayList();
    116 		int terminator = references.indexOf(":");
    117 		StringTokenizer st =
    118 			new StringTokenizer(references.substring(terminator), "\t");
    119 		while (st.hasMoreTokens()) {
    120 			list.add(st.nextToken());
    121 		}
    122 		return (String[]) list.toArray();
    123 	}
    124
    125 	/***
    126 	 * Attempts to parse the subject line for some typical reply signatures, and strip them out
    127 	 *
    128 	 */
    129 	private void simplifySubject() {
    130 			int start = 0;
    131 			String subject = getSubject();
    132 			int len = subject.length();
    133
    134 			boolean done = false;
    135
    136 			while (!done) {
    137 				done = true;
    138
    139 				// skip whitespace
    140 				// "Re: " breaks this
    141 				while (start < len && subject.charAt(start) == ' ') {
    142 					start++;
    143 				}
    144
    145 				if (start < (len - 2)
    146 					&& (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
    147 					&& (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {
    148
    149 					if (subject.charAt(start + 2) == ':') {
    150 						start += 3; // Skip "Re:"
    151 						isReply = true;
    152 						done = false;
    153 					} else if (
    154 						start < (len - 2)
    155 						&&
    156 						(subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {
    157
    158 						int i = start + 3;
    159
    160 						while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
    161 							i++;
    162
    163 						if (i < (len - 1)
    164 							&& (subject.charAt(i) == ']' || subject.charAt(i) == ')')
    165 							&& subject.charAt(i + 1) == ':') {
    166 							start = i + 2;
    167 							isReply = true;
    168 							done = false;
    169 						}
    170 					}
    171 				}
    172
    173 				if (simplifiedSubject == "(no subject)")
    174 					simplifiedSubject = "";
    175
    176 				int end = len;
    177
    178 				while (end > start && subject.charAt(end - 1) < ' ')
    179 					end--;
    180
    181 				if (start == 0 && end == len)
    182 					simplifiedSubject = subject;
    183 				else
    184 					simplifiedSubject = subject.substring(start, end);
    185 			}
    186 		}
    187
    188 	/***
    189 	 * Recursive method that traverses a pre-threaded graph (or tree)
    190 	 * of connected Article objects and prints them out.
    191 	 * @param article the root of the article 'tree'
    192 	 * @param depth the current tree depth
    193 	 */
    194 	public static void printThread(Article article, int depth) {
    195 			for (int i = 0; i < depth; ++i)
    196 				System.out.print("==>");
    197 			System.out.println(article.getSubject() + "\t" + article.getFrom());
    198 			if (article.kid != null)
    199 				printThread(article.kid, depth + 1);
    200 			if (article.next != null)
    201 				printThread(article.next, depth);
    202 	}
    203
    204 	public String getArticleId() {
    205 		return articleId;
    206 	}
    207
    208 	public int getArticleNumber() {
    209 		return articleNumber;
    210 	}
    211
    212 	public String getDate() {
    213 		return date;
    214 	}
    215
    216 	public String getFrom() {
    217 		return from;
    218 	}
    219
    220 	public String getSubject() {
    221 		return subject;
    222 	}
    223
    224 	public void setArticleId(String string) {
    225 		articleId = string;
    226 	}
    227
    228 	public void setArticleNumber(int i) {
    229 		articleNumber = i;
    230 	}
    231
    232 	public void setDate(String string) {
    233 		date = string;
    234 	}
    235
    236 	public void setFrom(String string) {
    237 		from = string;
    238 	}
    239
    240 	public void setSubject(String string) {
    241 		subject = string;
    242 	}
    243
    244
    245 	public boolean isDummy() {
    246 		return (getSubject() == null);
    247 	}
    248
    249 	public String messageThreadId() {
    250 		return articleId;
    251 	}
    252
    253 	public String[] messageThreadReferences() {
    254 		return getReferences();
    255 	}
    256
    257 	public String simplifiedSubject() {
    258 		if(simplifiedSubject == null)
    259 			simplifySubject();
    260 		return simplifiedSubject;
    261 	}
    262
    263
    264 	public boolean subjectIsReply() {
    265 		if(simplifiedSubject == null)
    266 			simplifySubject();
    267 		return isReply;
    268 	}
    269
    270
    271 	public void setChild(Threadable child) {
    272 		this.kid = (Article) child;
    273 		flushSubjectCache();
    274 	}
    275
    276 	private void flushSubjectCache() {
    277 		simplifiedSubject = null;
    278 	}
    279
    280
    281 	public void setNext(Threadable next) {
    282 		this.next = (Article)next;
    283 		flushSubjectCache();
    284 	}
    285
    286
    287 	public Threadable makeDummy() {
    288 		return (Threadable)new Article();
    289 	}
    290 }