| 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.FolderEvent; |
|---|
| 34 | import org.logicprobe.LogicMail.mail.FolderExpungedEvent; |
|---|
| 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.MailStoreEvent; |
|---|
| 39 | import org.logicprobe.LogicMail.mail.MailStoreListener; |
|---|
| 40 | import org.logicprobe.LogicMail.mail.MessageEvent; |
|---|
| 41 | import org.logicprobe.LogicMail.mail.MessageListener; |
|---|
| 42 | import org.logicprobe.LogicMail.mail.MessageToken; |
|---|
| 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 the root <code>MailboxNode</code> instance and delegates |
|---|
| 52 | * events from the underlying mail store to the appropriate |
|---|
| 53 | * <code>MailboxNode</code> or <code>MessageNode</code>. |
|---|
| 54 | */ |
|---|
| 55 | public abstract class AccountNode implements Node { |
|---|
| 56 | public final static int STATUS_LOCAL = 0; |
|---|
| 57 | public final static int STATUS_OFFLINE = 1; |
|---|
| 58 | public final static int STATUS_ONLINE = 2; |
|---|
| 59 | |
|---|
| 60 | protected final MailStoreServices mailStoreServices; |
|---|
| 61 | private MailRootNode parent; |
|---|
| 62 | private MailboxNode rootMailbox; |
|---|
| 63 | private final Hashtable pathMailboxMap; |
|---|
| 64 | private final Object rootMailboxLock = new Object(); |
|---|
| 65 | private final EventListenerList listenerList = new EventListenerList(); |
|---|
| 66 | |
|---|
| 67 | protected int status; |
|---|
| 68 | |
|---|
| 69 | /** |
|---|
| 70 | * Construct a new node for a network account. |
|---|
| 71 | * |
|---|
| 72 | * @param mailStoreServices Services for interacting with the mail store. |
|---|
| 73 | */ |
|---|
| 74 | protected AccountNode(MailStoreServices mailStoreServices) { |
|---|
| 75 | this.rootMailbox = null; |
|---|
| 76 | this.pathMailboxMap = new Hashtable(); |
|---|
| 77 | |
|---|
| 78 | this.mailStoreServices = mailStoreServices; |
|---|
| 79 | |
|---|
| 80 | addMailStoreListeners(); |
|---|
| 81 | } |
|---|
| 82 | |
|---|
| 83 | private void addMailStoreListeners() { |
|---|
| 84 | this.mailStoreServices.addMailStoreListener(new MailStoreListener() { |
|---|
| 85 | public void folderTreeUpdated(FolderEvent e) { |
|---|
| 86 | mailStoreFolderTreeUpdated(e); |
|---|
| 87 | } |
|---|
| 88 | public void refreshRequired(MailStoreEvent e) { |
|---|
| 89 | mailStoreRefreshRequired(e); |
|---|
| 90 | } |
|---|
| 91 | }); |
|---|
| 92 | |
|---|
| 93 | this.mailStoreServices.addFolderListener(new FolderListener() { |
|---|
| 94 | public void folderStatusChanged(FolderEvent e) { |
|---|
| 95 | MailboxNode mailboxNode = getMailboxNodeForEvent(e); |
|---|
| 96 | mailboxNode.mailStoreFolderStatusChanged(e); |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | public void folderMessagesAvailable(FolderMessagesEvent e) { |
|---|
| 100 | MailboxNode mailboxNode = getMailboxNodeForEvent(e); |
|---|
| 101 | mailboxNode.mailStoreFolderMessagesAvailable(e); |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | public void folderExpunged(FolderExpungedEvent e) { |
|---|
| 105 | MailboxNode mailboxNode = getMailboxNodeForEvent(e); |
|---|
| 106 | mailboxNode.mailStoreFolderExpunged(e); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | public void folderRefreshRequired(FolderEvent e) { } |
|---|
| 110 | }); |
|---|
| 111 | |
|---|
| 112 | this.mailStoreServices.addMessageListener(new MessageListener() { |
|---|
| 113 | public void messageAvailable(MessageEvent e) { |
|---|
| 114 | MailboxNode mailboxNode = getMailboxNodeForEvent(e); |
|---|
| 115 | if(mailboxNode != null) { |
|---|
| 116 | mailboxNode.mailStoreMessageAvailable(e); |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | public void messageFlagsChanged(MessageEvent e) { |
|---|
| 121 | MailboxNode mailboxNode = getMailboxNodeForEvent(e); |
|---|
| 122 | if(mailboxNode != null) { |
|---|
| 123 | mailboxNode.mailStoreMessageFlagsChanged(e); |
|---|
| 124 | } |
|---|
| 125 | } |
|---|
| 126 | }); |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | protected MailboxNode getMailboxNodeForFolder(FolderTreeItem folder) { |
|---|
| 130 | MailboxNode mailboxNode = (MailboxNode) pathMailboxMap.get(folder.getPath()); |
|---|
| 131 | return mailboxNode; |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | private MailboxNode getMailboxNodeForEvent(FolderEvent e) { |
|---|
| 135 | MailboxNode mailboxNode = getMailboxNodeForFolder(e.getFolder()); |
|---|
| 136 | return mailboxNode; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | private MailboxNode getMailboxNodeForEvent(MessageEvent e) { |
|---|
| 140 | MessageToken messageToken = e.getMessageToken(); |
|---|
| 141 | if(messageToken == null) { return null; } |
|---|
| 142 | Enumeration en = pathMailboxMap.elements(); |
|---|
| 143 | while(en.hasMoreElements()) { |
|---|
| 144 | MailboxNode currentMailbox = (MailboxNode)en.nextElement(); |
|---|
| 145 | if(messageToken.containedWithin(currentMailbox.getFolderTreeItem())) { |
|---|
| 146 | return currentMailbox; |
|---|
| 147 | } |
|---|
| 148 | } |
|---|
| 149 | return null; |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | protected MailboxNode[] getAllMailboxNodes() { |
|---|
| 153 | Vector resultVector = new Vector(); |
|---|
| 154 | Enumeration e = pathMailboxMap.elements(); |
|---|
| 155 | while(e.hasMoreElements()) { |
|---|
| 156 | MailboxNode mailbox = (MailboxNode)e.nextElement(); |
|---|
| 157 | if(mailbox.getType() == MailboxNode.TYPE_INBOX) { |
|---|
| 158 | resultVector.insertElementAt(mailbox, 0); |
|---|
| 159 | } |
|---|
| 160 | else { |
|---|
| 161 | resultVector.addElement(mailbox); |
|---|
| 162 | } |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | MailboxNode[] result = new MailboxNode[resultVector.size()]; |
|---|
| 166 | resultVector.copyInto(result); |
|---|
| 167 | return result; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | /** |
|---|
| 171 | * The name of the protocol behind this account. |
|---|
| 172 | */ |
|---|
| 173 | public String getProtocolName() { |
|---|
| 174 | return ""; |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | public void accept(NodeVisitor visitor) { |
|---|
| 178 | visitor.visit(this); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | /** |
|---|
| 182 | * Sets the root node which is the parent of this account. |
|---|
| 183 | * |
|---|
| 184 | * @param parent The root node. |
|---|
| 185 | */ |
|---|
| 186 | void setParent(MailRootNode parent) { |
|---|
| 187 | this.parent = parent; |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | /** |
|---|
| 191 | * Gets the root node which is the parent of this account. |
|---|
| 192 | * |
|---|
| 193 | * @return The root node. |
|---|
| 194 | */ |
|---|
| 195 | public MailRootNode getParent() { |
|---|
| 196 | return this.parent; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | /** |
|---|
| 200 | * Get the top-level mailbox contained within this account. |
|---|
| 201 | * This mailbox typically exists only for the purpose of |
|---|
| 202 | * containing other mailboxes, and is not normally shown |
|---|
| 203 | * to the user. |
|---|
| 204 | * |
|---|
| 205 | * @return Root mailbox node. |
|---|
| 206 | */ |
|---|
| 207 | public MailboxNode getRootMailbox() { |
|---|
| 208 | synchronized (rootMailboxLock) { |
|---|
| 209 | return this.rootMailbox; |
|---|
| 210 | } |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | /** |
|---|
| 214 | * Gets the mail store services associated with this account. |
|---|
| 215 | * |
|---|
| 216 | * @return The mail store services. |
|---|
| 217 | */ |
|---|
| 218 | MailStoreServices getMailStoreServices() { |
|---|
| 219 | return this.mailStoreServices; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | /** |
|---|
| 223 | * Sets the status of this account. |
|---|
| 224 | * |
|---|
| 225 | * @param status The status. |
|---|
| 226 | */ |
|---|
| 227 | void setStatus(int status) { |
|---|
| 228 | if (this.status != status) { |
|---|
| 229 | this.status = status; |
|---|
| 230 | mailStoreServices.setConnected(status == AccountNode.STATUS_ONLINE); |
|---|
| 231 | fireAccountStatusChanged(AccountNodeEvent.TYPE_CONNECTION); |
|---|
| 232 | } |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | /** |
|---|
| 236 | * Gets the status of this account. |
|---|
| 237 | * |
|---|
| 238 | * @return The status. |
|---|
| 239 | */ |
|---|
| 240 | public int getStatus() { |
|---|
| 241 | return this.status; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | /** |
|---|
| 245 | * Gets whether this account supports folders. |
|---|
| 246 | * If folders are not supported, then this account will automatically |
|---|
| 247 | * present a single "INBOX" folder. However, no other folder-related |
|---|
| 248 | * operations will have any relevance. |
|---|
| 249 | * |
|---|
| 250 | * @return True if supported, false otherwise. |
|---|
| 251 | */ |
|---|
| 252 | public boolean hasFolders() { |
|---|
| 253 | return this.mailStoreServices.hasFolders(); |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | /** |
|---|
| 257 | * Gets whether this account supports retrieval of individual message parts. |
|---|
| 258 | * |
|---|
| 259 | * @return True if supported, false otherwise. |
|---|
| 260 | */ |
|---|
| 261 | public boolean hasMessageParts() { |
|---|
| 262 | return this.mailStoreServices.hasMessageParts(); |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | /** |
|---|
| 266 | * Gets whether this account supports undelete. |
|---|
| 267 | * |
|---|
| 268 | * @return True if supported, false otherwise. |
|---|
| 269 | */ |
|---|
| 270 | public boolean hasUndelete() { |
|---|
| 271 | return this.mailStoreServices.hasUndelete(); |
|---|
| 272 | } |
|---|
| 273 | |
|---|
| 274 | /** |
|---|
| 275 | * Gets whether this account supports expunging deleted messages. |
|---|
| 276 | * |
|---|
| 277 | * @return True if supported, false otherwise. |
|---|
| 278 | */ |
|---|
| 279 | public boolean hasExpunge() { |
|---|
| 280 | return this.mailStoreServices.hasExpunge(); |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | /** |
|---|
| 284 | * Called to trigger a refresh of the mailboxes under |
|---|
| 285 | * this account. Completion is signaled by an |
|---|
| 286 | * AccountStatusChanged event. |
|---|
| 287 | */ |
|---|
| 288 | public void refreshMailboxes() { |
|---|
| 289 | if (mailStoreServices.hasFolders()) { |
|---|
| 290 | mailStoreServices.requestFolderTree(); |
|---|
| 291 | } |
|---|
| 292 | } |
|---|
| 293 | |
|---|
| 294 | /** |
|---|
| 295 | * Called to trigger a refresh of message count status |
|---|
| 296 | * for mailboxes under this account. Completion is |
|---|
| 297 | * signaled by MailboxStatusChanged events on the |
|---|
| 298 | * updated mailboxes. |
|---|
| 299 | */ |
|---|
| 300 | public void refreshMailboxStatus() { |
|---|
| 301 | FolderTreeItem[] folders = getFolderTreeItems(); |
|---|
| 302 | |
|---|
| 303 | mailStoreServices.requestFolderStatus(folders); |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | /** |
|---|
| 307 | * Gets a flag list of the folder tree items for all the mailboxes |
|---|
| 308 | * contained within this account. |
|---|
| 309 | * |
|---|
| 310 | * @return the folder tree item array |
|---|
| 311 | */ |
|---|
| 312 | protected FolderTreeItem[] getFolderTreeItems() { |
|---|
| 313 | int size = pathMailboxMap.size(); |
|---|
| 314 | FolderTreeItem[] folders = new FolderTreeItem[size]; |
|---|
| 315 | Enumeration e = pathMailboxMap.keys(); |
|---|
| 316 | |
|---|
| 317 | for (int i = 0; i < size; i++) { |
|---|
| 318 | folders[i] = ((MailboxNode) pathMailboxMap.get(e.nextElement())).getFolderTreeItem(); |
|---|
| 319 | } |
|---|
| 320 | return folders; |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | /** |
|---|
| 324 | * Handles folder tree updates. |
|---|
| 325 | * |
|---|
| 326 | * @param e Event data. |
|---|
| 327 | */ |
|---|
| 328 | private void mailStoreFolderTreeUpdated(FolderEvent e) { |
|---|
| 329 | FolderTreeItem rootFolder = e.getFolder(); |
|---|
| 330 | |
|---|
| 331 | synchronized (rootMailboxLock) { |
|---|
| 332 | Hashtable remainingMailboxMap = new Hashtable(); |
|---|
| 333 | |
|---|
| 334 | if (rootMailbox != null) { |
|---|
| 335 | // Disassemble the model tree into a flat collection of nodes |
|---|
| 336 | Vector flatMailboxes = new Vector(); |
|---|
| 337 | populateFlatMailboxes(flatMailboxes, rootMailbox); |
|---|
| 338 | rootMailbox = null; |
|---|
| 339 | |
|---|
| 340 | // Prune the collection to only include nodes that are still valid, |
|---|
| 341 | // and make them reference the new FolderTreeItem objects. |
|---|
| 342 | Hashtable folderPathMap = new Hashtable(); |
|---|
| 343 | populateFolderPathMap(folderPathMap, rootFolder); |
|---|
| 344 | |
|---|
| 345 | Vector removedFolders = null; |
|---|
| 346 | int size = flatMailboxes.size(); |
|---|
| 347 | for (int i = 0; i < size; i++) { |
|---|
| 348 | MailboxNode mailboxNode = (MailboxNode) flatMailboxes.elementAt(i); |
|---|
| 349 | FolderTreeItem oldFolderItem = mailboxNode.getFolderTreeItem(); |
|---|
| 350 | String path = oldFolderItem.getPath(); |
|---|
| 351 | |
|---|
| 352 | if (folderPathMap.containsKey(path)) { |
|---|
| 353 | // We found a duplicate item in the new folder tree |
|---|
| 354 | FolderTreeItem newFolderItem = (FolderTreeItem) folderPathMap.get(path); |
|---|
| 355 | mailboxNode.setFolderTreeItem(newFolderItem); |
|---|
| 356 | |
|---|
| 357 | // Need to copy over the UID to preserve cache validity. |
|---|
| 358 | // This needs to be done after setting it on the |
|---|
| 359 | // mailbox, since the setter copies the item before |
|---|
| 360 | // adding it. |
|---|
| 361 | mailboxNode.getFolderTreeItem().setUniqueId( |
|---|
| 362 | oldFolderItem.getUniqueId()); |
|---|
| 363 | |
|---|
| 364 | remainingMailboxMap.put(path, mailboxNode); |
|---|
| 365 | } |
|---|
| 366 | else { |
|---|
| 367 | // This mailbox was removed |
|---|
| 368 | if(removedFolders == null) { |
|---|
| 369 | removedFolders = new Vector(); |
|---|
| 370 | } |
|---|
| 371 | removedFolders.addElement(oldFolderItem); |
|---|
| 372 | } |
|---|
| 373 | } |
|---|
| 374 | |
|---|
| 375 | // Clear the saved data for any orphaned folders |
|---|
| 376 | if(removedFolders != null) { |
|---|
| 377 | FolderTreeItem[] orphanedItems = new FolderTreeItem[removedFolders.size()]; |
|---|
| 378 | removedFolders.copyInto(orphanedItems); |
|---|
| 379 | mailStoreServices.removeSavedData(orphanedItems); |
|---|
| 380 | } |
|---|
| 381 | } |
|---|
| 382 | |
|---|
| 383 | // Build a new tree from the FolderTreeItem, using the collected |
|---|
| 384 | // nodes where possible, and new nodes when necessary. |
|---|
| 385 | this.pathMailboxMap.clear(); |
|---|
| 386 | this.rootMailbox = new MailboxNode(rootFolder, false, -1); |
|---|
| 387 | populateMailboxNodes(rootFolder, rootMailbox, remainingMailboxMap); |
|---|
| 388 | } |
|---|
| 389 | |
|---|
| 390 | save(); |
|---|
| 391 | fireAccountStatusChanged(AccountNodeEvent.TYPE_MAILBOX_TREE); |
|---|
| 392 | } |
|---|
| 393 | |
|---|
| 394 | protected void mailStoreRefreshRequired(MailStoreEvent e) { |
|---|
| 395 | // Default empty implementation |
|---|
| 396 | } |
|---|
| 397 | |
|---|
| 398 | private void populateFlatMailboxes(Vector flatMailboxes, |
|---|
| 399 | MailboxNode currentMailbox) { |
|---|
| 400 | flatMailboxes.addElement(currentMailbox); |
|---|
| 401 | |
|---|
| 402 | MailboxNode[] childNodes = currentMailbox.getMailboxes(); |
|---|
| 403 | |
|---|
| 404 | for (int i = 0; i < childNodes.length; i++) { |
|---|
| 405 | populateFlatMailboxes(flatMailboxes, childNodes[i]); |
|---|
| 406 | } |
|---|
| 407 | |
|---|
| 408 | currentMailbox.clearMailboxes(); |
|---|
| 409 | } |
|---|
| 410 | |
|---|
| 411 | private void populateFolderPathMap(Hashtable folderPathMap, |
|---|
| 412 | FolderTreeItem folderTreeItem) { |
|---|
| 413 | if (folderTreeItem != null) { |
|---|
| 414 | folderPathMap.put(folderTreeItem.getPath(), folderTreeItem); |
|---|
| 415 | |
|---|
| 416 | if (folderTreeItem.hasChildren()) { |
|---|
| 417 | FolderTreeItem[] children = folderTreeItem.children(); |
|---|
| 418 | |
|---|
| 419 | for (int i = 0; i < children.length; i++) { |
|---|
| 420 | populateFolderPathMap(folderPathMap, children[i]); |
|---|
| 421 | } |
|---|
| 422 | } |
|---|
| 423 | } |
|---|
| 424 | } |
|---|
| 425 | |
|---|
| 426 | private void populateMailboxNodes(FolderTreeItem folderTreeItem, |
|---|
| 427 | MailboxNode currentMailbox, Hashtable remainingMailboxMap) { |
|---|
| 428 | pathMailboxMap.put(folderTreeItem.getPath(), currentMailbox); |
|---|
| 429 | |
|---|
| 430 | if (folderTreeItem.hasChildren()) { |
|---|
| 431 | FolderTreeItem[] folderTreeItemChildren = folderTreeItem.children(); |
|---|
| 432 | |
|---|
| 433 | for (int i = 0; i < folderTreeItemChildren.length; i++) { |
|---|
| 434 | MailboxNode childMailbox; |
|---|
| 435 | |
|---|
| 436 | if (remainingMailboxMap.containsKey( |
|---|
| 437 | folderTreeItemChildren[i].getPath())) { |
|---|
| 438 | childMailbox = (MailboxNode) remainingMailboxMap.get(folderTreeItemChildren[i].getPath()); |
|---|
| 439 | } else { |
|---|
| 440 | int mailboxType = getMailboxType(folderTreeItemChildren[i]); |
|---|
| 441 | if(mailboxType == MailboxNode.TYPE_OUTBOX) { |
|---|
| 442 | childMailbox = new OutboxMailboxNode(folderTreeItemChildren[i]); |
|---|
| 443 | } |
|---|
| 444 | else { |
|---|
| 445 | childMailbox = new MailboxNode(folderTreeItemChildren[i], |
|---|
| 446 | folderTreeItemChildren[i].isAppendable(), |
|---|
| 447 | mailboxType); |
|---|
| 448 | } |
|---|
| 449 | childMailbox.setParentAccount(this); |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | populateMailboxNodes(folderTreeItemChildren[i], childMailbox, |
|---|
| 453 | remainingMailboxMap); |
|---|
| 454 | currentMailbox.addMailbox(childMailbox); |
|---|
| 455 | } |
|---|
| 456 | } |
|---|
| 457 | } |
|---|
| 458 | |
|---|
| 459 | /** |
|---|
| 460 | * Attempts to determine the folder type based on its name, |
|---|
| 461 | * and any configuration options. |
|---|
| 462 | * <p> |
|---|
| 463 | * This approach is only necessary to for local folders and general |
|---|
| 464 | * defaults. Explicitly configured special folders have their type set |
|---|
| 465 | * later in the account loading process. |
|---|
| 466 | * </p> |
|---|
| 467 | * @param folderTreeItem Source folder tree item. |
|---|
| 468 | * @return Mailbox type |
|---|
| 469 | */ |
|---|
| 470 | protected int getMailboxType(FolderTreeItem folderTreeItem) { |
|---|
| 471 | int mailboxType; |
|---|
| 472 | if (folderTreeItem.getPath().equalsIgnoreCase("INBOX")) { |
|---|
| 473 | mailboxType = MailboxNode.TYPE_INBOX; |
|---|
| 474 | } |
|---|
| 475 | else { |
|---|
| 476 | mailboxType = MailboxNode.TYPE_NORMAL; |
|---|
| 477 | } |
|---|
| 478 | return mailboxType; |
|---|
| 479 | } |
|---|
| 480 | |
|---|
| 481 | /** |
|---|
| 482 | * Adds a <tt>AccountNodeListener</tt> to the account node. |
|---|
| 483 | * |
|---|
| 484 | * @param l The <tt>AccountNodeListener</tt> to be added. |
|---|
| 485 | */ |
|---|
| 486 | public void addAccountNodeListener(AccountNodeListener l) { |
|---|
| 487 | listenerList.add(AccountNodeListener.class, l); |
|---|
| 488 | } |
|---|
| 489 | |
|---|
| 490 | /** |
|---|
| 491 | * Removes a <tt>AccountNodeListener</tt> from the account node. |
|---|
| 492 | * |
|---|
| 493 | * @param l The <tt>AccountNodeListener</tt> to be removed. |
|---|
| 494 | */ |
|---|
| 495 | public void removeAccountNodeListener(AccountNodeListener l) { |
|---|
| 496 | listenerList.remove(AccountNodeListener.class, l); |
|---|
| 497 | } |
|---|
| 498 | |
|---|
| 499 | /** |
|---|
| 500 | * Returns an array of all <tt>AccountNodeListener</tt>s |
|---|
| 501 | * that have been added to this account node. |
|---|
| 502 | * |
|---|
| 503 | * @return All the <tt>AccountNodeListener</tt>s that have been added, |
|---|
| 504 | * or an empty array if no listeners have been added. |
|---|
| 505 | */ |
|---|
| 506 | public AccountNodeListener[] getAccountNodeListeners() { |
|---|
| 507 | return (AccountNodeListener[]) listenerList.getListeners(AccountNodeListener.class); |
|---|
| 508 | } |
|---|
| 509 | |
|---|
| 510 | /** |
|---|
| 511 | * Notifies all registered <tt>AccountNodeListener</tt>s that |
|---|
| 512 | * the account status has changed. |
|---|
| 513 | * |
|---|
| 514 | * @param type Event type. |
|---|
| 515 | */ |
|---|
| 516 | protected void fireAccountStatusChanged(int type) { |
|---|
| 517 | Object[] listeners = listenerList.getListeners(AccountNodeListener.class); |
|---|
| 518 | AccountNodeEvent e = null; |
|---|
| 519 | |
|---|
| 520 | for (int i = 0; i < listeners.length; i++) { |
|---|
| 521 | if (e == null) { |
|---|
| 522 | e = new AccountNodeEvent(this, type); |
|---|
| 523 | } |
|---|
| 524 | |
|---|
| 525 | ((AccountNodeListener) listeners[i]).accountStatusChanged(e); |
|---|
| 526 | } |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | /** |
|---|
| 530 | * Saves the mailbox tree to persistent storage. |
|---|
| 531 | */ |
|---|
| 532 | abstract void save(); |
|---|
| 533 | |
|---|
| 534 | /** |
|---|
| 535 | * Loads the mailbox tree from persistent storage. |
|---|
| 536 | */ |
|---|
| 537 | abstract void load(); |
|---|
| 538 | |
|---|
| 539 | /** |
|---|
| 540 | * Clear any persistent data associated with this account node. |
|---|
| 541 | * |
|---|
| 542 | * <p> |
|---|
| 543 | * When this account node removed from the model tree because the |
|---|
| 544 | * underlying account has been deleted, this method needs to be called to |
|---|
| 545 | * ensure that persistent data does not linger on the device. |
|---|
| 546 | * </p> |
|---|
| 547 | */ |
|---|
| 548 | protected void removeSavedData() { |
|---|
| 549 | FolderTreeItem[] folderTreeItems = this.getFolderTreeItems(); |
|---|
| 550 | mailStoreServices.removeSavedData(folderTreeItems); |
|---|
| 551 | } |
|---|
| 552 | |
|---|
| 553 | /** |
|---|
| 554 | * Sets the top-level mailbox contained within this account. |
|---|
| 555 | * This method should only be called by subclasses when loading saved |
|---|
| 556 | * account data. |
|---|
| 557 | */ |
|---|
| 558 | protected void setRootMailbox(MailboxNode mailboxNode) { |
|---|
| 559 | synchronized (rootMailboxLock) { |
|---|
| 560 | this.rootMailbox = mailboxNode; |
|---|
| 561 | prepareDeserializedMailboxNode(rootMailbox); |
|---|
| 562 | } |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | /** |
|---|
| 566 | * Traverses the deserialized mailbox nodes, populates any necessary |
|---|
| 567 | * data structures in the account node, and sets the mailbox parent |
|---|
| 568 | * account references. |
|---|
| 569 | * |
|---|
| 570 | * @param mailboxNode The mailbox node. |
|---|
| 571 | */ |
|---|
| 572 | private void prepareDeserializedMailboxNode(MailboxNode mailboxNode) { |
|---|
| 573 | mailboxNode.setParentAccount(this); |
|---|
| 574 | |
|---|
| 575 | FolderTreeItem item = mailboxNode.getFolderTreeItem(); |
|---|
| 576 | |
|---|
| 577 | if ((item != null) && (item.getPath().length() > 0)) { |
|---|
| 578 | this.pathMailboxMap.put(item.getPath(), mailboxNode); |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | MailboxNode[] children = mailboxNode.getMailboxes(); |
|---|
| 582 | |
|---|
| 583 | for (int i = 0; i < children.length; i++) { |
|---|
| 584 | prepareDeserializedMailboxNode(children[i]); |
|---|
| 585 | } |
|---|
| 586 | } |
|---|
| 587 | } |
|---|