001    package edu.harvard.deas.hyperenc.gui;
002    
003    import java.util.ArrayList;
004    import java.util.Date;
005    import java.util.List;
006    
007    import javax.swing.table.AbstractTableModel;
008    
009    import edu.harvard.deas.hyperenc.Contact;
010    import edu.harvard.deas.hyperenc.HyperMessageType;
011    
012    
013    /**
014     * A TableModel backed by a MessageStorage object. Displays a list of messages
015     * stored in a MessageStorage, one message per row, with selected fields of the
016     * message (for example: subject, sender, date) displayed in each of several
017     * columns.
018     * <p>
019     * TODO document how to use this -- when are update events fired, etc.
020     * TODO store all messages and use a filter instead of the ridiculous acrobatics we do now
021     * TODO this whole class sucks -- replace with a simpler version (use RowFilter)
022     */
023    public class MessageTableModel extends AbstractTableModel {
024      private static final long serialVersionUID = 1L;
025      
026      private MessageStorage msgStorage;
027      private Contact selectedSender;
028      private List<GuiMessage> senderMessages = new ArrayList<GuiMessage>(); // XXX not backed by the MessageStorage...
029      
030      private List<String> columnNames;
031      
032      /**
033       * TODO document constructor
034       * @param msgStorage
035       */
036      public MessageTableModel(MessageStorage msgStorage) {
037        columnNames = new ArrayList<String>();
038        // XXX hard-coded columns...
039        columnNames.add("Subject");
040        columnNames.add("Date");
041        columnNames.add("Decrypted?");
042        columnNames.add("Unread?");
043        
044        this.msgStorage = msgStorage;
045      }
046      
047      @Override
048      public String getColumnName(int column) {
049        return columnNames.get(column);
050      }
051      
052      // XXX hard-coded, this sucks
053      @Override
054      public Class<?> getColumnClass(int column) {
055        switch (column) {
056        case 0:
057          return String.class;
058        case 1:
059          return Date.class;
060        case 2:
061          return Boolean.class;
062        case 3:
063          return Boolean.class;
064        default:
065          throw new IllegalArgumentException("Illegal column number!");
066        }
067      }
068      
069      public int getColumnCount() {
070        return columnNames.size();
071      }
072      
073      public int getRowCount() {
074        if (senderMessages == null)
075          return 0;
076        return senderMessages.size();
077      }
078      
079      public Object getValueAt(int rowIndex, int columnIndex) {
080        GuiMessage gm;
081        try {
082          gm = senderMessages.get(rowIndex);
083        } catch (IndexOutOfBoundsException e) {
084          throw new IllegalArgumentException("illegal row index");
085        }
086        
087        switch (columnIndex) {
088        case 0:
089          return gm.getSubject();
090        case 1:
091          return gm.getDate();
092        case 2:
093          return gm.getHyperMessage().getType() == HyperMessageType.UNENCRYPTED;
094        case 3:
095          return gm.isRead() == ReadStatus.UNREAD;
096        default:
097          throw new IllegalArgumentException("illegal column index");
098        }
099      }
100      
101      /**
102       * Always returns <code>false</code>; no cells are editable.
103       * @return <code>false</code>
104       */
105      @Override
106      public boolean isCellEditable(int row, int col) {
107        return false;
108      }
109    
110      /**
111       * Sets this model to show only those messages in the HyperStorage from the
112       * given sender. Fires an event to indicate that the table data has changed.
113       * 
114       * @param contact
115       *        e-mail contact of the desired sender
116       */
117      public void selectSender(Contact contact) {
118        senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(contact));
119        
120        fireTableDataChanged();
121      }
122      
123      /**
124       * Returns the sender for which this model is set to show messages.
125       * @see #selectSender(Contact) 
126       */
127      public Contact getSelectedSender() {
128        return selectedSender;
129      }
130      
131      /**
132       * Adds a new GuiMessage to the underlying MessageStorage. Fires an event to
133       * indicate that the table has been updated.
134       * 
135       * @param gm
136       *        the message to add
137       */
138      // TODO use a row-inserted event instead
139      public void addMessage(GuiMessage gm) {
140        msgStorage.addMessage(gm);
141        senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(selectedSender));
142        fireTableDataChanged();
143      }
144      
145      /**
146       * Returns the GuiMessage represented by the specified row.
147       * @param rowNum a row number
148       * @return the GuiMessage in the given row
149       */
150      public GuiMessage getMessageAtRow(int rowNum) {
151        return senderMessages.get(rowNum);
152      }
153    
154      /**
155       * Replaces the GuiMessage in row <code>rowNum</code> with <code>gm</code>.
156       * 
157       * @param rowNum
158       *        the row to place the message in
159       * @param gm
160       *        the new message to insert
161       * @return the message that was replaced (the message that was originally in
162       *         the specified row)
163       */
164      public GuiMessage replaceMessageAtRow(int rowNum, GuiMessage gm) {
165        GuiMessage oldgm = senderMessages.set(rowNum, gm);
166        msgStorage.removeMessage(oldgm.getID());
167        msgStorage.addMessage(gm);
168        fireTableRowsUpdated(rowNum, rowNum);
169        return oldgm;
170      }
171      
172      /**
173       * Marks the GuiMessage in the specified row as read.
174       * @param rowNum the row to mark read
175       */
176      public void markRead(int rowNum) {
177        GuiMessage gm = senderMessages.get(rowNum);
178        GuiMessage newgm = GuiMessage.markRead(gm);
179        replaceMessageAtRow(rowNum, newgm);
180      }
181      
182      /**
183       * Marks the GuiMessage in the specified row as unread.
184       * @param rowNum the row to mark unread
185       */
186      public void markUnread(int rowNum) {
187        GuiMessage gm = senderMessages.get(rowNum);
188        GuiMessage newgm = GuiMessage.markUnread(gm);
189        replaceMessageAtRow(rowNum, newgm);
190      }
191    
192      /**
193       * Removes the GuiMessage with the given ID from the underlying
194       * MessageStorage. Fires an event to indicate that the table has been updated.
195       * 
196       * @param id
197       *        the message ID to remove
198       */
199      // TODO use a row-deleted event instead
200      public void removeMessage(int id) {
201        msgStorage.removeMessage(id);
202        senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(selectedSender));
203        fireTableDataChanged();
204      }
205    }