Article.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.net.nntp;

  18. import java.io.PrintStream;
  19. import java.util.ArrayList;
  20. import java.util.Collections;

  21. import org.apache.commons.net.util.NetConstants;

  22. /**
  23.  * This is a class that contains the basic state needed for message retrieval and threading. With thanks to Jamie Zawinski (jwz@jwz.org)
  24.  */
  25. public class Article implements Threadable {
  26.     /**
  27.      * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out.
  28.      *
  29.      * @param article the root of the article 'tree'
  30.      * @since 3.4
  31.      */
  32.     public static void printThread(final Article article) {
  33.         printThread(article, 0, System.out);
  34.     }

  35.     /**
  36.      * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out.
  37.      *
  38.      * @param article the root of the article 'tree'
  39.      * @param depth   the current tree depth
  40.      */
  41.     public static void printThread(final Article article, final int depth) {
  42.         printThread(article, depth, System.out);
  43.     }

  44.     /**
  45.      * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out.
  46.      *
  47.      * @param article the root of the article 'tree'
  48.      * @param depth   the current tree depth
  49.      * @param ps      the PrintStream to use
  50.      * @since 3.4
  51.      */
  52.     public static void printThread(final Article article, final int depth, final PrintStream ps) {
  53.         for (int i = 0; i < depth; ++i) {
  54.             ps.print("==>");
  55.         }
  56.         ps.println(article.getSubject() + "\t" + article.getFrom() + "\t" + article.getArticleId());
  57.         if (article.kid != null) {
  58.             printThread(article.kid, depth + 1);
  59.         }
  60.         if (article.next != null) {
  61.             printThread(article.next, depth);
  62.         }
  63.     }

  64.     /**
  65.      * Recursive method that traverses a pre-threaded graph (or tree) of connected Article objects and prints them out.
  66.      *
  67.      * @param article the root of the article 'tree'
  68.      * @param ps      the PrintStream to use
  69.      * @since 3.4
  70.      */
  71.     public static void printThread(final Article article, final PrintStream ps) {
  72.         printThread(article, 0, ps);
  73.     }

  74.     private long articleNumber;
  75.     private String subject;
  76.     private String date;
  77.     private String articleId;

  78.     private String simplifiedSubject;

  79.     private String from;
  80.     private ArrayList<String> references;

  81.     private boolean isReply;

  82.     public Article kid, next;

  83.     public Article() {
  84.         articleNumber = -1; // isDummy
  85.     }

  86.     @Deprecated

  87.     public void addHeaderField(final String name, final String val) {
  88.     }

  89.     /**
  90.      * Adds a message-id to the list of messages that this message references (i.e. replies to)
  91.      *
  92.      * @param msgId the message id to add
  93.      */
  94.     public void addReference(final String msgId) {
  95.         if (msgId == null || msgId.isEmpty()) {
  96.             return;
  97.         }
  98.         if (references == null) {
  99.             references = new ArrayList<>();
  100.         }
  101.         isReply = true;
  102.         Collections.addAll(references, msgId.split(" "));
  103.     }

  104.     private void flushSubjectCache() {
  105.         simplifiedSubject = null;
  106.     }

  107.     public String getArticleId() {
  108.         return articleId;
  109.     }

  110.     @Deprecated
  111.     public int getArticleNumber() {
  112.         return (int) articleNumber;
  113.     }

  114.     public long getArticleNumberLong() {
  115.         return articleNumber;
  116.     }

  117.     public String getDate() {
  118.         return date;
  119.     }

  120.     public String getFrom() {
  121.         return from;
  122.     }

  123.     /**
  124.      * Returns the MessageId references as an array of Strings
  125.      *
  126.      * @return an array of message-ids
  127.      */
  128.     public String[] getReferences() {
  129.         if (references == null) {
  130.             return NetConstants.EMPTY_STRING_ARRAY;
  131.         }
  132.         return references.toArray(NetConstants.EMPTY_STRING_ARRAY);
  133.     }

  134.     public String getSubject() {
  135.         return subject;
  136.     }

  137.     @Override
  138.     public boolean isDummy() {
  139.         return articleNumber == -1;
  140.     }

  141.     @Override
  142.     public Threadable makeDummy() {
  143.         return new Article();
  144.     }

  145.     @Override
  146.     public String messageThreadId() {
  147.         return articleId;
  148.     }

  149.     @Override
  150.     public String[] messageThreadReferences() {
  151.         return getReferences();
  152.     }

  153.     public void setArticleId(final String string) {
  154.         articleId = string;
  155.     }

  156.     @Deprecated
  157.     public void setArticleNumber(final int a) {
  158.         articleNumber = a;
  159.     }

  160.     public void setArticleNumber(final long l) {
  161.         articleNumber = l;
  162.     }

  163.     @Override
  164.     public void setChild(final Threadable child) {
  165.         this.kid = (Article) child;
  166.         flushSubjectCache();
  167.     }

  168.     public void setDate(final String string) {
  169.         date = string;
  170.     }

  171.     public void setFrom(final String string) {
  172.         from = string;
  173.     }

  174.     @Override
  175.     public void setNext(final Threadable next) {
  176.         this.next = (Article) next;
  177.         flushSubjectCache();
  178.     }

  179.     public void setSubject(final String string) {
  180.         subject = string;
  181.     }

  182.     @Override
  183.     public String simplifiedSubject() {
  184.         if (simplifiedSubject == null) {
  185.             simplifySubject();
  186.         }
  187.         return simplifiedSubject;
  188.     }

  189.     // DEPRECATED METHODS - for API compatibility only - DO NOT USE

  190.     /**
  191.      * Attempts to parse the subject line for some typical reply signatures, and strip them out
  192.      */
  193.     private void simplifySubject() {
  194.         int start = 0;
  195.         final String subject = getSubject();
  196.         final int len = subject.length();

  197.         boolean done = false;

  198.         while (!done) {
  199.             done = true;

  200.             // skip whitespace
  201.             // "Re: " breaks this
  202.             while (start < len && subject.charAt(start) == ' ') {
  203.                 start++;
  204.             }

  205.             if (start < len - 2 && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
  206.                     && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {

  207.                 if (subject.charAt(start + 2) == ':') {
  208.                     start += 3; // Skip "Re:"
  209.                     done = false;
  210.                 } else if (start < len - 2 && (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {

  211.                     int i = start + 3;

  212.                     while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9') {
  213.                         i++;
  214.                     }

  215.                     if (i < len - 1 && (subject.charAt(i) == ']' || subject.charAt(i) == ')') && subject.charAt(i + 1) == ':') {
  216.                         start = i + 2;
  217.                         done = false;
  218.                     }
  219.                 }
  220.             }

  221.             if ("(no subject)".equals(simplifiedSubject)) {
  222.                 simplifiedSubject = "";
  223.             }

  224.             int end = len;

  225.             while (end > start && subject.charAt(end - 1) < ' ') {
  226.                 end--;
  227.             }

  228.             if (start == 0 && end == len) {
  229.                 simplifiedSubject = subject;
  230.             } else {
  231.                 simplifiedSubject = subject.substring(start, end);
  232.             }
  233.         }
  234.     }

  235.     @Override
  236.     public boolean subjectIsReply() {
  237.         return isReply;
  238.     }

  239.     @Override
  240.     public String toString() { // Useful for Eclipse debugging
  241.         return articleNumber + " " + articleId + " " + subject;
  242.     }

  243. }