trunk/com/so/news/storage/Article.java
author chris <chris@marvin>
Tue Jan 20 10:21:03 2009 +0100 (2009-01-20)
changeset 0 f907866f0e4b
permissions -rw-r--r--
Initial import.
     1 /*
     2  *   StarOffice News Server
     3  *   see AUTHORS for the list of contributors
     4  *
     5  *   This program is free software: you can redistribute it and/or modify
     6  *   it under the terms of the GNU General Public License as published by
     7  *   the Free Software Foundation, either version 3 of the License, or
     8  *   (at your option) any later version.
     9  *
    10  *   This program is distributed in the hope that it will be useful,
    11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  *   GNU General Public License for more details.
    14  *
    15  *   You should have received a copy of the GNU General Public License
    16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  */
    18 
    19 package com.so.news.storage;
    20 
    21 import java.sql.ResultSet;
    22 import java.sql.SQLException;
    23 import java.util.Date;
    24 import java.util.HashMap;
    25 import java.util.Map;
    26 import java.util.Map.Entry;
    27 import java.util.UUID;
    28 
    29 import com.so.news.Config;
    30 import com.so.news.Debug;
    31 
    32 /**
    33  * Represents a newsgroup article.
    34  * @author Christian Lins
    35  * @author Denis Schwerdel
    36  */
    37 public class Article
    38 {
    39   /**
    40    * Loads the Article identified by the given ID from the Database.
    41    * @param messageID
    42    * @return null if Article is not found or if an error occurred.
    43    */
    44   public static Article getByMessageID(String messageID)
    45   {
    46     try
    47     {
    48       return Database.getInstance().getArticle(messageID);
    49     }
    50     catch(SQLException ex)
    51     {
    52       ex.printStackTrace(Debug.getInstance().getStream());
    53       return null;
    54     }
    55   }
    56   
    57   public static Article getByNumberInGroup(Group group, int number)
    58     throws SQLException
    59   {
    60     long gid = group.getID(); 
    61     return Database.getInstance().getArticle(gid, number); // Is number her correct?
    62   }
    63   
    64   private String              body      = "";
    65   private long                groupID   = -1;
    66   private Map<String, String> header    = new HashMap<String, String>();
    67   private int                 numberInGroup = -1;
    68   private String              msgID     = null;
    69   
    70   /**
    71    * Default constructor.
    72    */
    73   public Article()
    74   {
    75   }
    76   
    77   /**
    78    * Creates a new Article object using the date from the given
    79    * ResultSet. It is expected that ResultSet.next() was already
    80    * called by the Database class.
    81    * This construction has only package visibility.
    82    * @param rs
    83    */
    84   Article(ResultSet rs)
    85     throws SQLException
    86   {
    87     this.body  = rs.getString("body");
    88     this.msgID = rs.getString("message_id");
    89     
    90     // Parse the header
    91     parseHeader(rs.getString("header"));
    92   }
    93   
    94   /**
    95    * Parses the header fields and puts them into a map for faster access.
    96    * TODO: There could be fields that go over more than one line, some
    97    * bad clients do create them.
    98    * @param hsrc
    99    */
   100   private void parseHeader(String hsrc)
   101   {
   102     String[] lines = hsrc.split("\n");
   103     
   104     for(String line : lines)
   105     {
   106       String[] kv = line.split(":");
   107       if(kv.length < 2)
   108       {
   109         Debug.getInstance().log("Invalid header field: " + line);
   110         continue;
   111       }
   112       else
   113       {
   114         // Set value in the header hash map
   115         String value = kv[1];
   116         for(int n = 2; n < kv.length; n++)
   117           value += ":" + kv[n];
   118         this.header.put(kv[0], value);
   119       }
   120     }
   121   }
   122   
   123   /**
   124    * Returnes the next Article in the group of this Article.
   125    * @return
   126    */
   127   public Article nextArticleInGroup()
   128   {
   129     return null;
   130   }
   131 
   132   /**
   133    * Returns the previous Article in the group of this Article.
   134    * @return
   135    */
   136   public Article prevArticleInGroup()
   137   {
   138     return null;
   139   }
   140 
   141   /**
   142    * Generates a message id for this article and sets it into
   143    * the header HashMap.
   144    */
   145   private String generateMessageID()
   146   {
   147     this.msgID = "<" + UUID.randomUUID() + "@"
   148         + Config.getInstance().get("n3tpd.hostname", "localhost") + ">";
   149     
   150     this.header.put("Message-ID", msgID);
   151     
   152     return msgID;
   153   }
   154 
   155   /**
   156    * Tries to delete this article.
   157    * @return false if the article could not be deleted, otherwise true
   158    */
   159   public boolean delete()
   160   {
   161     return false;
   162   }
   163   
   164   /**
   165    * Checks if all necessary header fields are within this header.
   166    */
   167   private void validateHeader()
   168   {    
   169     // Forces a MessageID creation if not existing
   170     getMessageID();
   171     
   172     // Check if the references are correct...
   173     String rep = header.get("In-Reply-To");
   174     if(rep == null) // Some clients use only references instead of In-Reply-To
   175       return; //rep = header.get("References");
   176     
   177     String ref = getMessageID();
   178     
   179     if(rep != null && !rep.equals(""))
   180     {
   181       Article art = null; //TODO // getByMessageID(rep, articleDir);
   182       if(art != null)
   183       {
   184         ref = art.header.get("References") + " " + rep;
   185       }
   186     }
   187     header.put("References", ref);
   188   }
   189 
   190   /**
   191    * Returns the body string.
   192    */
   193   public String getBody()
   194   {
   195     return body;
   196   }
   197   
   198   /**
   199    * @return Numerical ID of the associated Group.
   200    */
   201   long getGroupID()
   202   {
   203     if(groupID == -1) // If the GroupID was not determined yet
   204     {
   205       // Determining GroupID
   206       String   newsgroups = this.header.get("Newsgroups");
   207       if(newsgroups != null)
   208       {
   209         String[] newsgroup  = newsgroups.split(",");
   210         // Crossposting is not supported
   211         try
   212         {
   213           Group group;
   214           if(newsgroup.length > 0)
   215             group = Database.getInstance().getGroup(newsgroup[0].trim());
   216           else
   217             group = Database.getInstance().getGroup(newsgroups.trim());
   218           // TODO: What to do if Group does not exist?
   219           this.groupID = group.getID();
   220         }
   221         catch(SQLException ex)
   222         {
   223           ex.printStackTrace(Debug.getInstance().getStream());
   224           System.err.println(ex.getLocalizedMessage());
   225         }
   226       }
   227       else
   228         System.err.println("Should never happen: Article::getGroupID");
   229     }
   230     return this.groupID;
   231   }
   232 
   233   public void setBody(String body)
   234   {
   235     this.body = body;
   236   }
   237 
   238   public int getNumberInGroup()
   239   {
   240     return this.numberInGroup;
   241   }
   242   
   243   public void setHeader(HashMap<String, String> header)
   244   {
   245     this.header = header;
   246   }
   247 
   248   public void setNumberInGroup(int id)
   249   {
   250     this.numberInGroup = id;
   251   }
   252 
   253   public String getMessageID()
   254   {
   255     if(msgID == null)
   256       msgID = generateMessageID();
   257     return msgID;
   258   }
   259 
   260   /**
   261    * @return Header source code of this Article.
   262    */
   263   public String getHeaderSource()
   264   {
   265     StringBuffer buf = new StringBuffer();
   266     
   267     for(Entry<String, String> entry : this.header.entrySet())
   268     {
   269       buf.append(entry.getKey());
   270       buf.append(":");
   271       buf.append(entry.getValue());
   272       buf.append("\n");
   273     }
   274     
   275     return buf.toString();
   276   }
   277   
   278   public Map<String, String> getHeader()
   279   {
   280     return this.header;
   281   }
   282   
   283   public Date getDate()
   284   {
   285     try
   286     {
   287       String date = this.header.get("Date");
   288       return new Date(Date.parse(date));
   289     }
   290     catch(Exception e)
   291     {
   292       e.printStackTrace(Debug.getInstance().getStream());
   293       return null;
   294     }
   295   }
   296 
   297   public void setDate(Date date)
   298   {
   299     this.header.put("Date", date.toString());
   300   }
   301   
   302   @Override
   303   public String toString()
   304   {
   305     return getMessageID();
   306   }
   307 }