| 1 | /*- |
|---|
| 2 | * Copyright (c) 2008, Derek Konigsberg |
|---|
| 3 | * All rights reserved. |
|---|
| 4 | * |
|---|
| 5 | * Redistribution and use in source and binary forms, with or without |
|---|
| 6 | * modification, are permitted provided that the following conditions |
|---|
| 7 | * are met: |
|---|
| 8 | * |
|---|
| 9 | * 1. Redistributions of source code must retain the above copyright |
|---|
| 10 | * notice, this list of conditions and the following disclaimer. |
|---|
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 12 | * notice, this list of conditions and the following disclaimer in the |
|---|
| 13 | * documentation and/or other materials provided with the distribution. |
|---|
| 14 | * 3. Neither the name of the project nor the names of its |
|---|
| 15 | * contributors may be used to endorse or promote products derived |
|---|
| 16 | * from this software without specific prior written permission. |
|---|
| 17 | * |
|---|
| 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|---|
| 21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|---|
| 22 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|---|
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|---|
| 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|---|
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|---|
| 27 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|---|
| 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
|---|
| 29 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 30 | */ |
|---|
| 31 | package org.logicprobe.LogicMail.model; |
|---|
| 32 | |
|---|
| 33 | import org.logicprobe.LogicMail.mail.AbstractMailStore; |
|---|
| 34 | import org.logicprobe.LogicMail.mail.FolderEvent; |
|---|
| 35 | import org.logicprobe.LogicMail.mail.FolderListener; |
|---|
| 36 | import org.logicprobe.LogicMail.mail.FolderMessagesEvent; |
|---|
| 37 | import org.logicprobe.LogicMail.mail.FolderTreeItem; |
|---|
| 38 | import org.logicprobe.LogicMail.mail.MailStoreListener; |
|---|
| 39 | import org.logicprobe.LogicMail.mail.MessageEvent; |
|---|
| 40 | import org.logicprobe.LogicMail.mail.MessageListener; |
|---|
| 41 | import org.logicprobe.LogicMail.mail.MessageToken; |
|---|
| 42 | import org.logicprobe.LogicMail.message.FolderMessage; |
|---|
| 43 | import org.logicprobe.LogicMail.util.EventListenerList; |
|---|
| 44 | |
|---|
| 45 | import java.util.Enumeration; |
|---|
| 46 | import java.util.Hashtable; |
|---|
| 47 | import java.util.Vector; |
|---|
| 48 | |
|---|
| 49 | /** |
|---|
| 50 | * Account node for the mail data model. |
|---|
| 51 | * This node contains only the root <tt>MailboxNode</tt> instance. |
|---|
| 52 | * Currently the type of mail store backing this node is |
|---|
| 53 | * determined by the constructor that is called. |
|---|
| 54 | * Eventually a more elegant approach will need to |
|---|
| 55 | * be implemented. |
|---|
| 56 | */ |
|---|
| 57 | public abstract class AccountNode implements Node { |
|---|
| 58 | public final static int STATUS_LOCAL = 0; |
|---|
| 59 | public final static int STATUS_OFFLINE = 1; |
|---|
| 60 | public final static int STATUS_ONLINE = 2; |
|---|
| 61 | private AbstractMailStore mailStore; |
|---|
| 62 | private MailRootNode parent; |
|---|
| 63 | private MailboxNode rootMailbox; |
|---|
| 64 | private Hashtable pathMailboxMap; |
|---|
| 65 | private Object rootMailboxLock = new Object(); |
|---|
| 66 | private EventListenerList listenerList = new EventListenerList(); |
|---|
| 67 | |
|---|
| 68 | protected int status; |
|---|
| 69 | |
|---|
| 70 | /** Map of folders to messages to fetch for them. */ |
|---|
| 71 | private Hashtable folderMessagesToFetch; |
|---|
| 72 | |
|---|
| 73 | /** |
|---|
| 74 | * Construct a new node for a network account. |
|---|
| 75 | * |
|---|
| 76 | * @param accountConfig Account configuration. |
|---|
| 77 | */ |
|---|
| 78 | protected AccountNode(AbstractMailStore mailStore) { |
|---|
| 79 | this.rootMailbox = null; |
|---|
| 80 | this.pathMailboxMap = new Hashtable(); |
|---|
| 81 | this.folderMessagesToFetch = new Hashtable(); |
|---|
| 82 | |
|---|
| 83 | this.mailStore = mailStore; |
|---|
| 84 | |
|---|
| 85 | addMailStoreListeners(); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | private void addMailStoreListeners() { |
|---|
| 89 | this.mailStore.addMailStoreListener(new MailStoreListener() { |
|---|
| 90 | public void folderTreeUpdated(FolderEvent e) { |
|---|
| 91 | mailStore_FolderTreeUpdated(e); |
|---|
| 92 | } |
|---|
| 93 | }); |
|---|
| 94 | |
|---|
| 95 | this.mailStore.addFolderListener(new FolderListener() { |
|---|
| 96 | public void folderStatusChanged(FolderEvent e) { |
|---|
| 97 | mailStore_FolderStatusChanged(e); |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | public void folderMessagesAvailable(FolderMessagesEvent e) { |
|---|
| 101 | mailStore_FolderMessagesAvailable(e); |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | public void folderExpunged(FolderEvent e) { |
|---|
| 105 | mailStore_FolderExpunged(e); |
|---|
| 106 | } |
|---|
| 107 | }); |
|---|
| 108 | |
|---|
| 109 | this.mailStore.addMessageListener(new MessageListener() { |
|---|
| 110 | public void messageAvailable(MessageEvent e) { |
|---|
| 111 | mailStore_messageAvailable(e); |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | public void messageFlagsChanged(MessageEvent e) { |
|---|
| 115 | mailStore_messageFlagsChanged(e); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | public void messageDeleted(MessageEvent e) { |
|---|
| 119 | mailStore_messageDeleted(e); |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | public void messageUndeleted(MessageEvent e) { |
|---|
| 123 | mailStore_messageUndeleted(e); |
|---|
| 124 | } |
|---|
| 125 | }); |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | public void accept(NodeVisitor visitor) { |
|---|
| 129 | visitor.visit(this); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | /** |
|---|
| 133 | * Sets the root node which is the parent of this account. |
|---|
| 134 | * |
|---|
| 135 | * @param parent The root node. |
|---|
| 136 | */ |
|---|
| 137 | void setParent(MailRootNode parent) { |
|---|
| 138 | this.parent = parent; |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | /** |
|---|
| 142 | * Gets the root node which is the parent of this account. |
|---|
| 143 | * |
|---|
| 144 | * @return The root node. |
|---|
| 145 | */ |
|---|
| 146 | public MailRootNode getParent() { |
|---|
| 147 | return this.parent; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | /** |
|---|
| 151 | * Get the top-level mailbox contained within this account. |
|---|
| 152 | * This mailbox typically exists only for the purpose of |
|---|
| 153 | * containing other mailboxes, and is not normally shown |
|---|
| 154 | * to the user. |
|---|
| 155 | * |
|---|
| 156 | * @return Root mailbox node. |
|---|
| 157 | */ |
|---|
| 158 | public MailboxNode getRootMailbox() { |
|---|
| 159 | synchronized (rootMailboxLock) { |
|---|
| 160 | return this.rootMailbox; |
|---|
| 161 | } |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | /** |
|---|
| 165 | * Gets the mail store associated with this account. |
|---|
| 166 | * |
|---|
| 167 | * @return The mail store. |
|---|
| 168 | */ |
|---|
| 169 | AbstractMailStore getMailStore() { |
|---|
| 170 | return this.mailStore; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | /** |
|---|
| 174 | * Sets the status of this account. |
|---|
| 175 | * |
|---|
| 176 | * @param status The status. |
|---|
| 177 | */ |
|---|
| 178 | void setStatus(int status) { |
|---|
| 179 | if (this.status != status) { |
|---|
| 180 | this.status = status; |
|---|
| 181 | |
|---|
| 182 | fireAccountStatusChanged(AccountNodeEvent.TYPE_CONNECTION); |
|---|
| 183 | } |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | /** |
|---|
| 187 | * Gets the status of this account. |
|---|
| 188 | * |
|---|
| 189 | * @return The status. |
|---|
| 190 | */ |
|---|
| 191 | public int getStatus() { |
|---|
| 192 | return this.status; |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | /** |
|---|
| 196 | * Gets whether this account supports folders. |
|---|
| 197 | * If folders are not supported, then this account will automatically |
|---|
| 198 | * present a single "INBOX" folder. However, no other folder-related |
|---|
| 199 | * operations will have any relevance. |
|---|
| 200 | * |
|---|
| 201 | * @return True if supported, false otherwise. |
|---|
| 202 | */ |
|---|
| 203 | public boolean hasFolders() { |
|---|
| 204 | return this.mailStore.hasFolders(); |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | /** |
|---|
| 208 | * Gets whether this account supports undelete. |
|---|
| 209 | * |
|---|
| 210 | * @return True if supported, false otherwise. |
|---|
| 211 | */ |
|---|
| 212 | public boolean hasUndelete() { |
|---|
| 213 | return this.mailStore.hasUndelete(); |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | /** |
|---|
| 217 | * Gets whether this account supports expunging deleted messages. |
|---|
| 218 | * |
|---|
| 219 | * @return True if supported, false otherwise. |
|---|
| 220 | */ |
|---|
| 221 | public boolean hasExpunge() { |
|---|
| 222 | return this.mailStore.hasExpunge(); |
|---|
| 223 | } |
|---|
| 224 | |
|---|
| 225 | /** |
|---|
| 226 | * Called to trigger a refresh of the mailboxes under |
|---|
| 227 | * this account. Completion is signaled by an |
|---|
| 228 | * AccountStatusChanged event. |
|---|
| 229 | */ |
|---|
| 230 | public void refreshMailboxes() { |
|---|
| 231 | if (mailStore.hasFolders()) { |
|---|
| 232 | mailStore.requestFolderTree(); |
|---|
| 233 | } |
|---|
| 234 | } |
|---|
| 235 | |
|---|
| 236 | /** |
|---|
| 237 | * Called to trigger a refresh of message count status |
|---|
| 238 | * for mailboxes under this account. Completion is |
|---|
| 239 | * signaled by MailboxStatusChanged events on the |
|---|
| 240 | * updated mailboxes. |
|---|
| 241 | */ |
|---|
| 242 | public void refreshMailboxStatus() { |
|---|
| 243 | int size = pathMailboxMap.size(); |
|---|
| 244 | FolderTreeItem[] folders = new FolderTreeItem[size]; |
|---|
| 245 | Enumeration e = pathMailboxMap.keys(); |
|---|
| 246 | |
|---|
| 247 | for (int i = 0; i < size; i++) { |
|---|
| 248 | folders[i] = ((MailboxNode) pathMailboxMap.get(e.nextElement())).getFolderTreeItem(); |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | mailStore.requestFolderStatus(folders); |
|---|
| 252 | } |
|---|
| 253 | |
|---|
| 254 | /** |
|---|
| 255 | * Handles folder tree updates. |
|---|
| 256 | * |
|---|
| 257 | * @param e Event data. |
|---|
| 258 | */ |
|---|
| 259 | private void mailStore_FolderTreeUpdated(FolderEvent e) { |
|---|
| 260 | FolderTreeItem rootFolder = e.getFolder(); |
|---|
| 261 | |
|---|
| 262 | synchronized (rootMailboxLock) { |
|---|
| 263 | Hashtable remainingMailboxMap = new Hashtable(); |
|---|
| 264 | |
|---|
| 265 | if (rootMailbox != null) { |
|---|
| 266 | // Disassemble the model tree into a flat collection of nodes |
|---|
| 267 | Vector flatMailboxes = new Vector(); |
|---|
| 268 | populateFlatMailboxes(flatMailboxes, rootMailbox); |
|---|
| 269 | rootMailbox = null; |
|---|
| 270 | |
|---|
| 271 | // Prune the collection to only include nodes that are still valid, |
|---|
| 272 | // and make them reference the new FolderTreeItem objects. |
|---|
| 273 | Hashtable folderPathMap = new Hashtable(); |
|---|
| 274 | populateFolderPathMap(folderPathMap, rootFolder); |
|---|
| 275 | |
|---|
| 276 | int size = flatMailboxes.size(); |
|---|
| 277 | |
|---|
| 278 | for (int i = 0; i < size; i++) { |
|---|
| 279 | MailboxNode mailboxNode = (MailboxNode) flatMailboxes.elementAt(i); |
|---|
| 280 | String path = mailboxNode.getFolderTreeItem().getPath(); |
|---|
| 281 | |
|---|
| 282 | if (folderPathMap.containsKey(path)) { |
|---|
| 283 | mailboxNode.setFolderTreeItem((FolderTreeItem) folderPathMap.get(path)); |
|---|
| 284 | remainingMailboxMap.put(path, mailboxNode); |
|---|
| 285 | } |
|---|
| 286 | } |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | // Build a new tree from the FolderTreeItem, using the collected |
|---|
| 290 | // nodes where possible, and new nodes when necessary. |
|---|
| 291 | this.pathMailboxMap.clear(); |
|---|
| 292 | this.rootMailbox = new MailboxNode(rootFolder, false, -1); |
|---|
| 293 | populateMailboxNodes(rootFolder, rootMailbox, remainingMailboxMap); |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | save(); |
|---|
| 297 | fireAccountStatusChanged(AccountNodeEvent.TYPE_MAILBOX_TREE); |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | private void populateFlatMailboxes(Vector flatMailboxes, |
|---|
| 301 | MailboxNode currentMailbox) { |
|---|
| 302 | flatMailboxes.addElement(currentMailbox); |
|---|
| 303 | |
|---|
| 304 | MailboxNode[] childNodes = currentMailbox.getMailboxes(); |
|---|
| 305 | |
|---|
| 306 | for (int i = 0; i < childNodes.length; i++) { |
|---|
| 307 | populateFlatMailboxes(flatMailboxes, childNodes[i]); |
|---|
| 308 | } |
|---|
| 309 | |
|---|
| 310 | currentMailbox.clearMailboxes(); |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | private void populateFolderPathMap(Hashtable folderPathMap, |
|---|
| 314 | FolderTreeItem folderTreeItem) { |
|---|
| 315 | if (folderTreeItem != null) { |
|---|
| 316 | folderPathMap.put(folderTreeItem.getPath(), folderTreeItem); |
|---|
| 317 | |
|---|
| 318 | if (folderTreeItem.hasChildren()) { |
|---|
| 319 | FolderTreeItem[] children = folderTreeItem.children(); |
|---|
| 320 | |
|---|
| 321 | for (int i = 0; i < children.length; i++) { |
|---|
| 322 | populateFolderPathMap(folderPathMap, children[i]); |
|---|
| 323 | } |
|---|
| 324 | } |
|---|
| 325 | } |
|---|
| 326 | } |
|---|
| 327 | |
|---|
| 328 | private void populateMailboxNodes(FolderTreeItem folderTreeItem, |
|---|
| 329 | MailboxNode currentMailbox, Hashtable remainingMailboxMap) { |
|---|
| 330 | pathMailboxMap.put(folderTreeItem.getPath(), currentMailbox); |
|---|
| 331 | |
|---|
| 332 | if (folderTreeItem.hasChildren()) { |
|---|
| 333 | FolderTreeItem[] folderTreeItemChildren = folderTreeItem.children(); |
|---|
| 334 | |
|---|
| 335 | for (int i = 0; i < folderTreeItemChildren.length; i++) { |
|---|
| 336 | MailboxNode childMailbox; |
|---|
| 337 | |
|---|
| 338 | if (remainingMailboxMap.containsKey( |
|---|
| 339 | folderTreeItemChildren[i].getPath())) { |
|---|
| 340 | childMailbox = (MailboxNode) remainingMailboxMap.get(folderTreeItemChildren[i].getPath()); |
|---|
| 341 | } else { |
|---|
| 342 | int mailboxType = getMailboxType(folderTreeItemChildren[i]); |
|---|
| 343 | if(mailboxType == MailboxNode.TYPE_OUTBOX) { |
|---|
| 344 | childMailbox = new OutboxMailboxNode(folderTreeItemChildren[i]); |
|---|
| 345 | } |
|---|
| 346 | else { |
|---|
| 347 | childMailbox = new MailboxNode(folderTreeItemChildren[i], |
|---|
| 348 | folderTreeItemChildren[i].isAppendable(), |
|---|
| 349 | mailboxType); |
|---|
| 350 | } |
|---|
| 351 | childMailbox.setParentAccount(this); |
|---|
| 352 | } |
|---|
| 353 | |
|---|
| 354 | populateMailboxNodes(folderTreeItemChildren[i], childMailbox, |
|---|
| 355 | remainingMailboxMap); |
|---|
| 356 | currentMailbox.addMailbox(childMailbox); |
|---|
| 357 | } |
|---|
| 358 | } |
|---|
| 359 | } |
|---|
| 360 | |
|---|
| 361 | /** |
|---|
| 362 | * Handles folder status changes. |
|---|
| 363 | * |
|---|
| 364 | * @param e Event data. |
|---|
| 365 | */ |
|---|
| 366 | private void mailStore_FolderStatusChanged(FolderEvent e) { |
|---|
| 367 | updateMailboxStatus(e.getFolder()); |
|---|
| 368 | } |
|---|
| 369 | |
|---|
| 370 | /** |
|---|
| 371 | * Recursively update mailbox status. |
|---|
| 372 | * |
|---|
| 373 | * @param currentFolder Folder item to start from. |
|---|
| 374 | */ |
|---|
| 375 | private void updateMailboxStatus(FolderTreeItem currentFolder) { |
|---|
| 376 | MailboxNode mailboxNode = (MailboxNode) pathMailboxMap.get(currentFolder.getPath()); |
|---|
| 377 | |
|---|
| 378 | if (mailboxNode != null) { |
|---|
| 379 | FolderTreeItem mailboxFolder = mailboxNode.getFolderTreeItem(); |
|---|
| 380 | mailboxFolder.setMsgCount(currentFolder.getMsgCount()); |
|---|
| 381 | mailboxFolder.setUnseenCount(currentFolder.getUnseenCount()); |
|---|
| 382 | mailboxNode.updateUnseenFolderTreeItem(); |
|---|
| 383 | mailboxNode.fireMailboxStatusChanged(MailboxNodeEvent.TYPE_STATUS, null); |
|---|
| 384 | } |
|---|
| 385 | |
|---|
| 386 | if (currentFolder.hasChildren()) { |
|---|
| 387 | FolderTreeItem[] children = currentFolder.children(); |
|---|
| 388 | |
|---|
| 389 | for (int i = 0; i < children.length; i++) { |
|---|
| 390 | updateMailboxStatus(children[i]); |
|---|
| 391 | } |
|---|
| 392 | } |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | /** |
|---|
| 396 | * Handles folder messages becoming available. |
|---|
| 397 | * |
|---|
| 398 | * @param e Event data. |
|---|
| 399 | */ |
|---|
| 400 | private void mailStore_FolderMessagesAvailable(FolderMessagesEvent e) { |
|---|
| 401 | if (e.getMessages() != null) { |
|---|
| 402 | // Find the MailboxNode that this event applies to. |
|---|
| 403 | // If none apply, then shortcut out of here. |
|---|
| 404 | if (!pathMailboxMap.containsKey(e.getFolder().getPath())) { |
|---|
| 405 | return; |
|---|
| 406 | } |
|---|
| 407 | |
|---|
| 408 | MailboxNode mailboxNode = (MailboxNode) pathMailboxMap.get(e.getFolder().getPath()); |
|---|
| 409 | FolderMessage[] folderMessages = e.getMessages(); |
|---|
| 410 | |
|---|
| 411 | if(e.isFlagsOnly()) { |
|---|
| 412 | // Only flags have been retrieved, so the existing messages need to |
|---|
| 413 | // be checked and additional actions requested accordingly. |
|---|
| 414 | for (int i = 0; i < folderMessages.length; i++) { |
|---|
| 415 | if(!mailboxNode.updateMessageFlags(folderMessages[i].getMessageToken(), folderMessages[i].getFlags())) { |
|---|
| 416 | // Message does not currently exist in the mailbox, and |
|---|
| 417 | // is not in the process of being loaded from cache. |
|---|
| 418 | synchronized(folderMessagesToFetch) { |
|---|
| 419 | Vector messagesToFetch = (Vector)folderMessagesToFetch.get(e.getFolder()); |
|---|
| 420 | if(messagesToFetch == null) { |
|---|
| 421 | messagesToFetch = new Vector(); |
|---|
| 422 | folderMessagesToFetch.put(e.getFolder(), messagesToFetch); |
|---|
| 423 | } |
|---|
| 424 | messagesToFetch.addElement(folderMessages[i].getMessageToken()); |
|---|
| 425 | } |
|---|
| 426 | } |
|---|
| 427 | } |
|---|
| 428 | } |
|---|
| 429 | else { |
|---|
| 430 | // Determine what MessageNodes need to be created, and add them. |
|---|
| 431 | Vector addedMessages = new Vector(); |
|---|
| 432 | |
|---|
| 433 | for (int i = 0; i < folderMessages.length; i++) { |
|---|
| 434 | MessageNode messageNode = new MessageNode(folderMessages[i]); |
|---|
| 435 | messageNode.setExistsOnServer(true); |
|---|
| 436 | addedMessages.addElement(messageNode); |
|---|
| 437 | } |
|---|
| 438 | |
|---|
| 439 | MessageNode[] addedMessagesArray = new MessageNode[addedMessages.size()]; |
|---|
| 440 | addedMessages.copyInto(addedMessagesArray); |
|---|
| 441 | mailboxNode.addMessages(addedMessagesArray); |
|---|
| 442 | } |
|---|
| 443 | } |
|---|
| 444 | else { |
|---|
| 445 | synchronized(folderMessagesToFetch) { |
|---|
| 446 | FolderTreeItem fetchFolder = e.getFolder(); |
|---|
| 447 | |
|---|
| 448 | // Clean out all messages that couldn't be verified against the server |
|---|
| 449 | MailboxNode mailboxNode = (MailboxNode) pathMailboxMap.get(fetchFolder.getPath()); |
|---|
| 450 | mailboxNode.removeMessagesNotOnServer(); |
|---|
| 451 | |
|---|
| 452 | // Queue a fetch for messages that do exist |
|---|
| 453 | Vector messagesToFetch = (Vector)folderMessagesToFetch.remove(fetchFolder); |
|---|
| 454 | if(messagesToFetch != null) { |
|---|
| 455 | MessageToken[] fetchArray = new MessageToken[messagesToFetch.size()]; |
|---|
| 456 | messagesToFetch.copyInto(fetchArray); |
|---|
| 457 | mailStore.requestFolderMessagesSet(e.getFolder(), fetchArray); |
|---|
| 458 | } |
|---|
| 459 | } |
|---|
| 460 | } |
|---|
| 461 | } |
|---|
| 462 | |
|---|
| 463 | /** |
|---|
| 464 | * Handles a folder being expunged. |
|---|
| 465 | * |
|---|
| 466 | * @param e Event data. |
|---|
| 467 | */ |
|---|
| 468 | private void mailStore_FolderExpunged(FolderEvent e) { |
|---|
| 469 | FolderTreeItem fetchFolder = e.getFolder(); |
|---|
| 470 | MailboxNode mailboxNode = (MailboxNode) pathMailboxMap.get(fetchFolder.getPath()); |
|---|
| 471 | mailboxNode.handleExpungeNotification(); |
|---|
| 472 | } |
|---|
| 473 | |
|---|
| 474 | /** |
|---|
| 475 | * Handles a message being loaded. |
|---|
| 476 | * |
|---|
| 477 | * @param e Event data. |
|---|
| 478 | */ |
|---|
| 479 | private void mailStore_messageAvailable(MessageEvent e) { |
|---|
| 480 | MessageNode messageNode = findMessageForToken(e.getMessageToken()); |
|---|
| 481 | |
|---|
| 482 | if (messageNode != null) { |
|---|
| 483 | // Set the SEEN bit and unset the RECENT bit |
|---|
| 484 | int flags = messageNode.getFlags(); |
|---|
| 485 | flags |= MessageNode.Flag.SEEN; |
|---|
| 486 | flags &= ~MessageNode.Flag.RECENT; |
|---|
| 487 | |
|---|
| 488 | messageNode.setFlags(flags); |
|---|
| 489 | |
|---|
| 490 | switch(e.getType()) { |
|---|
| 491 | case MessageEvent.TYPE_FULLY_LOADED: |
|---|
| 492 | messageNode.setMessageStructure(e.getMessageStructure()); |
|---|
| 493 | messageNode.setMessageSource(e.getMessageSource()); |
|---|
| 494 | messageNode.putMessageContent(e.getMessageContent()); |
|---|
| 495 | messageNode.commitMessage(); |
|---|
| 496 | break; |
|---|
| 497 | case MessageEvent.TYPE_CONTENT_LOADED: |
|---|
| 498 | messageNode.putMessageContent(e.getMessageContent()); |
|---|
| 499 | messageNode.commitMessage(); |
|---|
| 500 | break; |
|---|
| 501 | } |
|---|
| 502 | } |
|---|
| 503 | } |
|---|
| 504 | |
|---|
| 505 | /** |
|---|
| 506 | * Handles a message flags changing. |
|---|
| 507 | * |
|---|
| 508 | * @param e Event data. |
|---|
| 509 | */ |
|---|
| 510 | private void mailStore_messageFlagsChanged(MessageEvent e) { |
|---|
| 511 | MessageNode messageNode = findMessageForToken(e.getMessageToken()); |
|---|
| 512 | |
|---|
| 513 | if (messageNode != null) { |
|---|
| 514 | messageNode.setFlags(MessageNode.convertMessageFlags(e.getMessageFlags())); |
|---|
| 515 | messageNode.fireMessageStatusChanged(MessageNodeEvent.TYPE_FLAGS); |
|---|
| 516 | } |
|---|
| 517 | } |
|---|
| 518 | |
|---|
| 519 | /** |
|---|
| 520 | * Handles a message being deleted. |
|---|
| 521 | * |
|---|
| 522 | * @param e Event data. |
|---|
| 523 | */ |
|---|
| 524 | private void mailStore_messageDeleted(MessageEvent e) { |
|---|
| 525 | MessageNode messageNode = findMessageForToken(e.getMessageToken()); |
|---|
| 526 | |
|---|
| 527 | if (messageNode != null) { |
|---|
| 528 | messageNode.setFlags(MessageNode.convertMessageFlags(e.getMessageFlags())); |
|---|
| 529 | messageNode.fireMessageStatusChanged(MessageNodeEvent.TYPE_FLAGS); |
|---|
| 530 | } |
|---|
| 531 | } |
|---|
| 532 | |
|---|
| 533 | /** |
|---|
| 534 | * Handles a message being undeleted. |
|---|
| 535 | * |
|---|
| 536 | * @param e Event data. |
|---|
| 537 | */ |
|---|
| 538 | private void mailStore_messageUndeleted(MessageEvent e) { |
|---|
| 539 | MessageNode messageNode = findMessageForToken(e.getMessageToken()); |
|---|
| 540 | |
|---|
| 541 | if (messageNode != null) { |
|---|
| 542 | messageNode.setFlags(MessageNode.convertMessageFlags(e.getMessageFlags())); |
|---|
| 543 | messageNode.fireMessageStatusChanged(MessageNodeEvent.TYPE_FLAGS); |
|---|
| 544 | } |
|---|
| 545 | } |
|---|
| 546 | |
|---|
| 547 | /** |
|---|
| 548 | * Finds the message node matching a particular token. |
|---|
| 549 | * |
|---|
| 550 | * @param messageToken |
|---|
| 551 | * @return Message node, or null if none was found. |
|---|
| 552 | */ |
|---|
| 553 | private MessageNode findMessageForToken(MessageToken messageToken) { |
|---|
| 554 | MailboxNode mailboxNode = null; |
|---|
| 555 | Enumeration e = pathMailboxMap.elements(); |
|---|
| 556 | while(e.hasMoreElements()) { |
|---|
| 557 | MailboxNode currentMailbox = (MailboxNode)e.nextElement(); |
|---|
| 558 | if(messageToken.containedWithin(currentMailbox.getFolderTreeItem())) { |
|---|
| 559 | mailboxNode = currentMailbox; |
|---|
| 560 | break; |
|---|
| 561 | } |
|---|
| 562 | } |
|---|
| 563 | |
|---|
| 564 | if (mailboxNode != null) { |
|---|
| 565 | // Change this to use the a real tag object once implemented |
|---|
| 566 | return mailboxNode.getMessageByToken(messageToken); |
|---|
| 567 | } else { |
|---|
| 568 | return null; |
|---|
| 569 | } |
|---|
| 570 | } |
|---|
| 571 | |
|---|
| 572 | /** |
|---|
| 573 | * Attempts to determine the folder type based on its name, |
|---|
| 574 | * and any configuration options. |
|---|
| 575 | * <p> |
|---|
| 576 | * This approach is only necessary to for local folders and general |
|---|
| 577 | * defaults. Explicitly configured special folders have their type set |
|---|
| 578 | * later in the account loading process. |
|---|
| 579 | * </p> |
|---|
| 580 | * @param folderTreeItem Source folder tree item. |
|---|
| 581 | * @return Mailbox type |
|---|
| 582 | */ |
|---|
| 583 | protected int getMailboxType(FolderTreeItem folderTreeItem) { |
|---|
| 584 | int mailboxType; |
|---|
| 585 | if (folderTreeItem.getPath().equalsIgnoreCase("INBOX")) { |
|---|
| 586 | mailboxType = MailboxNode.TYPE_INBOX; |
|---|
| 587 | } |
|---|
| 588 | else { |
|---|
| 589 | mailboxType = MailboxNode.TYPE_NORMAL; |
|---|
| 590 | } |
|---|
| 591 | return mailboxType; |
|---|
| 592 | } |
|---|
| 593 | |
|---|
| 594 | /** |
|---|
| 595 | * Adds a <tt>AccountNodeListener</tt> to the account node. |
|---|
| 596 | * |
|---|
| 597 | * @param l The <tt>AccountNodeListener</tt> to be added. |
|---|
| 598 | */ |
|---|
| 599 | public void addAccountNodeListener(AccountNodeListener l) { |
|---|
| 600 | listenerList.add(AccountNodeListener.class, l); |
|---|
| 601 | } |
|---|
| 602 | |
|---|
| 603 | /** |
|---|
| 604 | * Removes a <tt>AccountNodeListener</tt> from the account node. |
|---|
| 605 | * |
|---|
| 606 | * @param l The <tt>AccountNodeListener</tt> to be removed. |
|---|
| 607 | */ |
|---|
| 608 | public void removeAccountNodeListener(AccountNodeListener l) { |
|---|
| 609 | listenerList.remove(AccountNodeListener.class, l); |
|---|
| 610 | } |
|---|
| 611 | |
|---|
| 612 | /** |
|---|
| 613 | * Returns an array of all <tt>AccountNodeListener</tt>s |
|---|
| 614 | * that have been added to this account node. |
|---|
| 615 | * |
|---|
| 616 | * @return All the <tt>AccountNodeListener</tt>s that have been added, |
|---|
| 617 | * or an empty array if no listeners have been added. |
|---|
| 618 | */ |
|---|
| 619 | public AccountNodeListener[] getAccountNodeListeners() { |
|---|
| 620 | return (AccountNodeListener[]) listenerList.getListeners(AccountNodeListener.class); |
|---|
| 621 | } |
|---|
| 622 | |
|---|
| 623 | /** |
|---|
| 624 | * Notifies all registered <tt>AccountNodeListener</tt>s that |
|---|
| 625 | * the account status has changed. |
|---|
| 626 | * |
|---|
| 627 | * @param type Event type. |
|---|
| 628 | */ |
|---|
| 629 | protected void fireAccountStatusChanged(int type) { |
|---|
| 630 | Object[] listeners = listenerList.getListeners(AccountNodeListener.class); |
|---|
| 631 | AccountNodeEvent e = null; |
|---|
| 632 | |
|---|
| 633 | for (int i = 0; i < listeners.length; i++) { |
|---|
| 634 | if (e == null) { |
|---|
| 635 | e = new AccountNodeEvent(this, type); |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | ((AccountNodeListener) listeners[i]).accountStatusChanged(e); |
|---|
| 639 | } |
|---|
| 640 | } |
|---|
| 641 | |
|---|
| 642 | /** |
|---|
| 643 | * Saves the mailbox tree to persistent storage. |
|---|
| 644 | */ |
|---|
| 645 | abstract void save(); |
|---|
| 646 | |
|---|
| 647 | /** |
|---|
| 648 | * Loads the mailbox tree from persistent storage. |
|---|
| 649 | */ |
|---|
| 650 | abstract void load(); |
|---|
| 651 | |
|---|
| 652 | /** |
|---|
| 653 | * Clear any persistent data associated with this account node. |
|---|
| 654 | * |
|---|
| 655 | * <p> |
|---|
| 656 | * When this account node removed from the model tree because the |
|---|
| 657 | * underlying account has been deleted, this method needs to be called to |
|---|
| 658 | * ensure that persistent data does not linger on the device. |
|---|
| 659 | * </p> |
|---|
| 660 | */ |
|---|
| 661 | protected void removeSavedData() { |
|---|
| 662 | // Default empty implementation |
|---|
| 663 | } |
|---|
| 664 | |
|---|
| 665 | /** |
|---|
| 666 | * Sets the top-level mailbox contained within this account. |
|---|
| 667 | * This method should only be called by subclasses when loading saved |
|---|
| 668 | * account data. |
|---|
| 669 | */ |
|---|
| 670 | protected void setRootMailbox(MailboxNode mailboxNode) { |
|---|
| 671 | synchronized (rootMailboxLock) { |
|---|
| 672 | this.rootMailbox = mailboxNode; |
|---|
| 673 | prepareDeserializedMailboxNode(rootMailbox); |
|---|
| 674 | } |
|---|
| 675 | } |
|---|
| 676 | |
|---|
| 677 | /** |
|---|
| 678 | * Traverses the deserialized mailbox nodes, populates any necessary |
|---|
| 679 | * data structures in the account node, and sets the mailbox parent |
|---|
| 680 | * account references. |
|---|
| 681 | * |
|---|
| 682 | * @param mailboxNode The mailbox node. |
|---|
| 683 | */ |
|---|
| 684 | private void prepareDeserializedMailboxNode(MailboxNode mailboxNode) { |
|---|
| 685 | mailboxNode.setParentAccount(this); |
|---|
| 686 | |
|---|
| 687 | FolderTreeItem item = mailboxNode.getFolderTreeItem(); |
|---|
| 688 | |
|---|
| 689 | if ((item != null) && (item.getPath().length() > 0)) { |
|---|
| 690 | this.pathMailboxMap.put(item.getPath(), mailboxNode); |
|---|
| 691 | } |
|---|
| 692 | |
|---|
| 693 | MailboxNode[] children = mailboxNode.getMailboxes(); |
|---|
| 694 | |
|---|
| 695 | for (int i = 0; i < children.length; i++) { |
|---|
| 696 | prepareDeserializedMailboxNode(children[i]); |
|---|
| 697 | } |
|---|
| 698 | } |
|---|
| 699 | } |
|---|