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