Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 713   Methods: 39
NCLOC: 204   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Folder.java 0% 12.9% 7.7% 9.2%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2004 The Apache Software Foundation
 4    *
 5    * Licensed under the Apache License, Version 2.0 (the "License");
 6    * you may not use this file except in compliance with the License.
 7    * You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    */
 17   
 18    package javax.mail;
 19   
 20    import java.util.ArrayList;
 21    import java.util.List;
 22    import javax.mail.Flags.Flag;
 23    import javax.mail.event.ConnectionEvent;
 24    import javax.mail.event.ConnectionListener;
 25    import javax.mail.event.FolderEvent;
 26    import javax.mail.event.FolderListener;
 27    import javax.mail.event.MessageChangedEvent;
 28    import javax.mail.event.MessageChangedListener;
 29    import javax.mail.event.MessageCountEvent;
 30    import javax.mail.event.MessageCountListener;
 31    import javax.mail.search.SearchTerm;
 32   
 33    /**
 34    * An abstract representation of a folder in a mail system; subclasses would
 35    * implement Folders for each supported protocol.
 36    * <p/>
 37    * Depending on protocol and implementation, folders may contain other folders, messages,
 38    * or both as indicated by the {@link Folder#HOLDS_FOLDERS} and {@link Folder#HOLDS_MESSAGES} flags.
 39    * If the immplementation supports hierarchical folders, the format of folder names is
 40    * implementation dependent; however, components of the name are separated by the
 41    * delimiter character returned by {@link Folder#getSeparator()}.
 42    * <p/>
 43    * The case-insensitive folder name "INBOX" is reserved to refer to the primary folder
 44    * for the current user on the current server; not all stores will provide an INBOX
 45    * and it may not be available at all times.
 46    *
 47    * @version $Rev: 126350 $ $Date: 2005-01-24 22:35:47 -0800 (Mon, 24 Jan 2005) $
 48    */
 49    public abstract class Folder {
 50    /**
 51    * Flag that indicates that a folder can contain messages.
 52    */
 53    public static final int HOLDS_MESSAGES = 1;
 54    /**
 55    * Flag that indicates that a folder can contain other folders.
 56    */
 57    public static final int HOLDS_FOLDERS = 2;
 58   
 59    /**
 60    * Flag indicating that this folder cannot be modified.
 61    */
 62    public static final int READ_ONLY = 1;
 63    /**
 64    * Flag indictaing that this folder can be modified.
 65    * Question: what does it mean if both are set?
 66    */
 67    public static final int READ_WRITE = 2;
 68   
 69    /**
 70    * The store that this folder is part of.
 71    */
 72    protected Store store;
 73    /**
 74    * The current mode of this folder.
 75    * When open, this can be {@link #READ_ONLY} or {@link #READ_WRITE};
 76    * otherwise is set to -1.
 77    */
 78    protected int mode = -1;
 79   
 80    private final List connectionListeners = new ArrayList(2);
 81    private final List folderListeners = new ArrayList(2);
 82    private final List messageChangedListeners = new ArrayList(2);
 83    private final List messageCountListeners = new ArrayList(2);
 84    private final EventQueue queue = new EventQueue();
 85   
 86    /**
 87    * Constructor that initializes the Store.
 88    *
 89    * @param store the store that this folder is part of
 90    */
 91  9 protected Folder(Store store) {
 92  9 this.store = store;
 93    }
 94   
 95    /**
 96    * Return the name of this folder.
 97    * This can be invoked when the folder is closed.
 98    *
 99    * @return this folder's name
 100    */
 101    public abstract String getName();
 102   
 103    /**
 104    * Return the full absolute name of this folder.
 105    * This can be invoked when the folder is closed.
 106    *
 107    * @return the full name of this folder
 108    */
 109    public abstract String getFullName();
 110   
 111    /**
 112    * Return the URLName for this folder, which includes the location of the store.
 113    *
 114    * @return the URLName for this folder
 115    * @throws MessagingException
 116    */
 117  0 public URLName getURLName() throws MessagingException {
 118    // todo shouldn't this include the full name of the folder?
 119  0 return store.getURLName();
 120    }
 121   
 122    /**
 123    * Return the store that this folder is part of.
 124    *
 125    * @return the store this folder is part of
 126    */
 127  3 public Store getStore() {
 128  3 return store;
 129    }
 130   
 131    /**
 132    * Return the parent for this folder; if the folder is at the root of a heirarchy
 133    * this returns null.
 134    * This can be invoked when the folder is closed.
 135    *
 136    * @return this folder's parent
 137    * @throws MessagingException
 138    */
 139    public abstract Folder getParent() throws MessagingException;
 140   
 141    /**
 142    * Check to see if this folder physically exists in the store.
 143    * This can be invoked when the folder is closed.
 144    *
 145    * @return true if the folder really exists
 146    * @throws MessagingException if there was a problem accessing the store
 147    */
 148    public abstract boolean exists() throws MessagingException;
 149   
 150    /**
 151    * Return a list of folders from this Folder's namespace that match the supplied pattern.
 152    * Patterns may contain the following wildcards:
 153    * <ul><li>'%' which matches any characater except hierarchy delimiters</li>
 154    * <li>'*' which matches any character including hierarchy delimiters</li>
 155    * </ul>
 156    * This can be invoked when the folder is closed.
 157    *
 158    * @param pattern the pattern to search for
 159    * @return a, possibly empty, array containing Folders that matched the pattern
 160    * @throws MessagingException if there was a problem accessing the store
 161    */
 162    public abstract Folder[] list(String pattern) throws MessagingException;
 163   
 164    /**
 165    * Return a list of folders to which the user is subscribed and which match the supplied pattern.
 166    * If the store does not support the concept of subscription then this should match against
 167    * all folders; the default implementation of this method achieves this by defaulting to the
 168    * {@link #list(String)} method.
 169    *
 170    * @param pattern the pattern to search for
 171    * @return a, possibly empty, array containing subscribed Folders that matched the pattern
 172    * @throws MessagingException if there was a problem accessing the store
 173    */
 174  0 public Folder[] listSubscribed(String pattern) throws MessagingException {
 175  0 return list(pattern);
 176    }
 177   
 178    /**
 179    * Convenience method that invokes {@link #list(String)} with the pattern "%".
 180    *
 181    * @return a, possibly empty, array of subfolders
 182    * @throws MessagingException if there was a problem accessing the store
 183    */
 184  0 public Folder[] list() throws MessagingException {
 185  0 return list("%");
 186    }
 187   
 188    /**
 189    * Convenience method that invokes {@link #listSubscribed(String)} with the pattern "%".
 190    *
 191    * @return a, possibly empty, array of subscribed subfolders
 192    * @throws MessagingException if there was a problem accessing the store
 193    */
 194  0 public Folder[] listSubscribed() throws MessagingException {
 195  0 return listSubscribed("%");
 196    }
 197   
 198    /**
 199    * Return the character used by this folder's Store to separate path components.
 200    *
 201    * @return the name separater character
 202    * @throws MessagingException if there was a problem accessing the store
 203    */
 204    public abstract char getSeparator() throws MessagingException;
 205   
 206    /**
 207    * Return the type of this folder, indicating whether it can contain subfolders,
 208    * messages, or both. The value returned is a bitmask with the appropriate bits set.
 209    *
 210    * @return the type of this folder
 211    * @throws MessagingException if there was a problem accessing the store
 212    * @see #HOLDS_FOLDERS
 213    * @see #HOLDS_MESSAGES
 214    */
 215    public abstract int getType() throws MessagingException;
 216   
 217    /**
 218    * Create a new folder capable of containing subfoldera and/or messages as
 219    * determined by the type parameter. Any hierarchy defined by the folder
 220    * name will be recursively created.
 221    * If the folder was sucessfully created, a {@link FolderEvent#CREATED CREATED FolderEvent}
 222    * is sent to all FolderListeners registered with this Folder or with the Store.
 223    *
 224    * @param type the type, indicating if this folder should contain subfolders, messages or both
 225    * @return true if the folder was sucessfully created
 226    * @throws MessagingException if there was a problem accessing the store
 227    */
 228    public abstract boolean create(int type) throws MessagingException;
 229   
 230    /**
 231    * Determine if the user is subscribed to this Folder. The default implementation in
 232    * this class always returns true.
 233    *
 234    * @return true is the user is subscribed to this Folder
 235    */
 236  0 public boolean isSubscribed() {
 237  0 return true;
 238    }
 239   
 240    /**
 241    * Set the user's subscription to this folder.
 242    * Not all Stores support subscription; the default implementation in this class
 243    * always throws a MethodNotSupportedException
 244    *
 245    * @param subscribed whether to subscribe to this Folder
 246    * @throws MessagingException if there was a problem accessing the store
 247    * @throws MethodNotSupportedException if the Store does not support subscription
 248    */
 249  0 public void setSubscribed(boolean subscribed) throws MessagingException {
 250  0 throw new MethodNotSupportedException();
 251    }
 252   
 253    /**
 254    * Check to see if this Folder conatins messages with the {@link Flag.RECENT} flag set.
 255    * This can be used when the folder is closed to perform a light-weight check for new mail;
 256    * to perform an incremental check for new mail the folder must be opened.
 257    *
 258    * @return true if the Store has recent messages
 259    * @throws MessagingException if there was a problem accessing the store
 260    */
 261    public abstract boolean hasNewMessages() throws MessagingException;
 262   
 263    /**
 264    * Get the Folder determined by the supplied name; if the name is relative
 265    * then it is interpreted relative to this folder. This does not check that
 266    * the named folder actually exists.
 267    *
 268    * @param name the name of the folder to return
 269    * @return the named folder
 270    * @throws MessagingException if there was a problem accessing the store
 271    */
 272    public abstract Folder getFolder(String name) throws MessagingException;
 273   
 274    /**
 275    * Delete this folder and possibly any subfolders. This operation can only be
 276    * performed on a closed folder.
 277    * If recurse is true, then all subfolders are deleted first, then any messages in
 278    * this folder are removed and it is finally deleted; {@link FolderEvent#DELETED}
 279    * events are sent as appropriate.
 280    * If recurse is false, then the behaviour depends on the folder type and store
 281    * implementation as followd:
 282    * <ul>
 283    * <li>If the folder can only conrain messages, then all messages are removed and
 284    * then the folder is deleted; a {@link FolderEvent#DELETED} event is sent.</li>
 285    * <li>If the folder can onlu contain subfolders, then if it is empty it will be
 286    * deleted and a {@link FolderEvent#DELETED} event is sent; if the folder is not
 287    * empty then the delete fails and this method returns false.</li>
 288    * <li>If the folder can contain both subfolders and messages, then if the folder
 289    * does not contain any subfolders, any messages are deleted, the folder itself
 290    * is deleted and a {@link FolderEvent#DELETED} event is sent; if the folder does
 291    * contain subfolders then the implementation may choose from the following three
 292    * behaviors:
 293    * <ol>
 294    * <li>it may return false indicting the operation failed</li>
 295    * <li>it may remove all messages within the folder, send a {@link FolderEvent#DELETED}
 296    * event, and then return true to indicate the delete was performed. Note this does
 297    * not delete the folder itself and the {@link #exists()} operation for this folder
 298    * will return true</li>
 299    * <li>it may remove all messages within the folder as per the previous option; in
 300    * addition it may change the type of the Folder to only HOLDS_FOLDERS indictaing
 301    * that messages may no longer be added</li>
 302    * </li>
 303    * </ul>
 304    * FolderEvents are sent to all listeners registered with this folder or
 305    * with the Store.
 306    *
 307    * @param recurse whether subfolders should be recursively deleted as well
 308    * @return true if the delete operation succeeds
 309    * @throws MessagingException if there was a problem accessing the store
 310    */
 311    public abstract boolean delete(boolean recurse) throws MessagingException;
 312   
 313    /**
 314    * Rename this folder; the folder must be closed.
 315    * If the rename is successfull, a {@link FolderEvent#RENAMED} event is sent to
 316    * all listeners registered with this folder or with the store.
 317    *
 318    * @param newName the new name for this folder
 319    * @return true if the rename succeeded
 320    * @throws MessagingException if there was a problem accessing the store
 321    */
 322    public abstract boolean renameTo(Folder newName) throws MessagingException;
 323   
 324    /**
 325    * Open this folder; the folder must be able to contain messages and
 326    * must currently be closed. If the folder is opened successfully then
 327    * a {@link ConnectionEvent#OPENED} event is sent to listeners registered
 328    * with this Folder.
 329    * <p/>
 330    * Whether the Store allows multiple connections or if it allows multiple
 331    * writers is implementation defined.
 332    *
 333    * @param mode READ_ONLY or READ_WRITE
 334    * @throws MessagingException if there was a problem accessing the store
 335    */
 336    public abstract void open(int mode) throws MessagingException;
 337   
 338    /**
 339    * Close this folder; it must already be open.
 340    * A {@link ConnectionEvent#CLOSED} event is sent to all listeners registered
 341    * with this folder.
 342    *
 343    * @param expunge whether to expunge all deleted messages
 344    * @throws MessagingException if there was a problem accessing the store; the folder is still closed
 345    */
 346    public abstract void close(boolean expunge) throws MessagingException;
 347   
 348    /**
 349    * Indicates that the folder has been opened.
 350    *
 351    * @return true if the folder is open
 352    */
 353    public abstract boolean isOpen();
 354   
 355    /**
 356    * Return the mode of this folder ass passed to {@link #open(int)}, or -1 if
 357    * the folder is closed.
 358    *
 359    * @return the mode this folder was opened with
 360    */
 361  0 public int getMode() {
 362  0 return mode;
 363    }
 364   
 365    /**
 366    * Get the flags supported by this folder.
 367    *
 368    * @return the flags supported by this folder, or null if unknown
 369    * @see Flags
 370    */
 371    public abstract Flags getPermanentFlags();
 372   
 373    /**
 374    * Return the number of messages this folder contains.
 375    * If this operation is invoked on a closed folder, the implementation
 376    * may choose to return -1 to avoid the expense of opening the folder.
 377    *
 378    * @return the number of messages, or -1 if unknown
 379    * @throws MessagingException if there was a problem accessing the store
 380    */
 381    public abstract int getMessageCount() throws MessagingException;
 382   
 383    /**
 384    * Return the numbew of messages in this folder that have the {@link Flag.RECENT} flag set.
 385    * If this operation is invoked on a closed folder, the implementation
 386    * may choose to return -1 to avoid the expense of opening the folder.
 387    * The default implmentation of this method iterates over all messages
 388    * in the folder; subclasses should override if possible to provide a more
 389    * efficient implementation.
 390    *
 391    * @return the number of new messages, or -1 if unknown
 392    * @throws MessagingException if there was a problem accessing the store
 393    */
 394  0 public int getNewMessageCount() throws MessagingException {
 395  0 return getCount(Flags.Flag.RECENT, true);
 396    }
 397   
 398    /**
 399    * Return the numbew of messages in this folder that do not have the {@link Flag.SEEN} flag set.
 400    * If this operation is invoked on a closed folder, the implementation
 401    * may choose to return -1 to avoid the expense of opening the folder.
 402    * The default implmentation of this method iterates over all messages
 403    * in the folder; subclasses should override if possible to provide a more
 404    * efficient implementation.
 405    *
 406    * @return the number of new messages, or -1 if unknown
 407    * @throws MessagingException if there was a problem accessing the store
 408    */
 409  0 public int getUnreadMessageCount() throws MessagingException {
 410  0 return getCount(Flags.Flag.SEEN, false);
 411    }
 412   
 413    /**
 414    * Return the numbew of messages in this folder that have the {@link Flag.DELETED} flag set.
 415    * If this operation is invoked on a closed folder, the implementation
 416    * may choose to return -1 to avoid the expense of opening the folder.
 417    * The default implmentation of this method iterates over all messages
 418    * in the folder; subclasses should override if possible to provide a more
 419    * efficient implementation.
 420    *
 421    * @return the number of new messages, or -1 if unknown
 422    * @throws MessagingException if there was a problem accessing the store
 423    */
 424  0 public int getDeletedMessageCount() throws MessagingException {
 425  0 return getCount(Flags.Flag.DELETED, true);
 426    }
 427   
 428  0 private int getCount(Flag flag, boolean value) throws MessagingException {
 429  0 if (!isOpen()) {
 430  0 return -1;
 431    }
 432  0 Message[] messages = getMessages();
 433  0 int total = 0;
 434  0 for (int i = 0; i < messages.length; i++) {
 435  0 if (messages[i].getFlags().contains(flag) == value) {
 436  0 total++;
 437    }
 438    }
 439  0 return total;
 440    }
 441   
 442    /**
 443    * Retrieve the message with the specified index in this Folder;
 444    * messages indices start at 1 not zero.
 445    * Clients should note that the index for a specific message may change
 446    * if the folder is expunged; {@link Message} objects should be used as
 447    * references instead.
 448    *
 449    * @param index the index of the message to fetch
 450    * @return the message
 451    * @throws MessagingException if there was a problem accessing the store
 452    */
 453    public abstract Message getMessage(int index) throws MessagingException;
 454   
 455    /**
 456    * Retrieve messages with index between start and end inclusive
 457    *
 458    * @param start index of first message
 459    * @param end index of last message
 460    * @return an array of messages from start to end inclusive
 461    * @throws MessagingException if there was a problem accessing the store
 462    */
 463  0 public Message[] getMessages(int start, int end) throws MessagingException {
 464  0 Message[] result = new Message[end - start + 1];
 465  0 for (int i = 0; i < result.length; i++) {
 466  0 result[i] = getMessage(start++);
 467    }
 468  0 return result;
 469    }
 470   
 471    /**
 472    * Retrieve messages with the specified indices.
 473    *
 474    * @param ids the indices of the messages to fetch
 475    * @return the specified messages
 476    * @throws MessagingException if there was a problem accessing the store
 477    */
 478  0 public Message[] getMessages(int ids[]) throws MessagingException {
 479  0 Message[] result = new Message[ids.length];
 480  0 for (int i = 0; i < ids.length; i++) {
 481  0 result[i] = getMessage(ids[i]);
 482    }
 483  0 return result;
 484    }
 485   
 486    /**
 487    * Retrieve all messages.
 488    *
 489    * @return all messages in this folder
 490    * @throws MessagingException if there was a problem accessing the store
 491    */
 492  0 public Message[] getMessages() throws MessagingException {
 493  0 return getMessages(1, getMessageCount());
 494    }
 495   
 496    /**
 497    * Append the supplied messages to this folder. A {@link MessageCountEvent} is sent
 498    * to all listeners registered with this folder when all messages have been appended.
 499    * If the array contains a previously expunged message, it must be re-appended to the Store
 500    * and implementations must not abort this operation.
 501    *
 502    * @param messages the messages to append
 503    * @throws MessagingException if there was a problem accessing the store
 504    */
 505    public abstract void appendMessages(Message[] messages) throws MessagingException;
 506   
 507    /**
 508    * Hint to the store to prefetch information on the supplied messaged.
 509    * Subclasses should override this method to provide an efficient implementation;
 510    * the default implementation in this class simply returns.
 511    *
 512    * @param messages messages for which information should be fetched
 513    * @param profile the information to fetch
 514    * @throws MessagingException if there was a problem accessing the store
 515    * @see FetchProfile
 516    */
 517  0 public void fetch(Message[] messages, FetchProfile profile) throws MessagingException {
 518  0 return;
 519    }
 520   
 521    /**
 522    * Set flags on the messages to the supplied value; all messages must belong to this folder.
 523    * This method may be overridden by subclasses that can optimize the setting
 524    * of flags on multiple messages at once; the default implementation simply calls
 525    * {@link Message#setFlags(Flags, boolean)} for each supplied messages.
 526    *
 527    * @param messages whose flags should be set
 528    * @param flags the set of flags to modify
 529    * @param value the value the flags should be set to
 530    * @throws MessagingException if there was a problem accessing the store
 531    */
 532  0 public void setFlags(Message[] messages, Flags flags, boolean value) throws MessagingException {
 533  0 for (int i = 0; i < messages.length; i++) {
 534  0 Message message = messages[i];
 535  0 message.setFlags(flags, value);
 536    }
 537    }
 538   
 539    /**
 540    * Set flags on a range of messages to the supplied value.
 541    * This method may be overridden by subclasses that can optimize the setting
 542    * of flags on multiple messages at once; the default implementation simply
 543    * gets each message and then calls {@link Message#setFlags(Flags, boolean)}.
 544    *
 545    * @param start first message end set
 546    * @param end last message end set
 547    * @param flags the set of flags end modify
 548    * @param value the value the flags should be set end
 549    * @throws MessagingException if there was a problem accessing the store
 550    */
 551  0 public void setFlags(int start, int end, Flags flags, boolean value) throws MessagingException {
 552  0 for (int i = start; i <= end; i++) {
 553  0 Message message = getMessage(i);
 554  0 message.setFlags(flags, value);
 555    }
 556    }
 557   
 558    /**
 559    * Set flags on a set of messages to the supplied value.
 560    * This method may be overridden by subclasses that can optimize the setting
 561    * of flags on multiple messages at once; the default implementation simply
 562    * gets each message and then calls {@link Message#setFlags(Flags, boolean)}.
 563    *
 564    * @param ids the indexes of the messages to set
 565    * @param flags the set of flags end modify
 566    * @param value the value the flags should be set end
 567    * @throws MessagingException if there was a problem accessing the store
 568    */
 569  0 public void setFlags(int ids[], Flags flags, boolean value) throws MessagingException {
 570  0 for (int i = 0; i < ids.length; i++) {
 571  0 Message message = getMessage(ids[i]);
 572  0 message.setFlags(flags, value);
 573    }
 574    }
 575   
 576    /**
 577    * Copy the specified messages to another folder.
 578    * The default implementation simply appends the supplied messages to the
 579    * target folder using {@link #appendMessages(Message[])}.
 580    * @param messages the messages to copy
 581    * @param folder the folder to copy to
 582    * @throws MessagingException if there was a problem accessing the store
 583    */
 584  0 public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
 585  0 folder.appendMessages(messages);
 586    }
 587   
 588    /**
 589    * Permanently delete all supplied messages that have the DELETED flag set from the Store.
 590    * The original message indices of all messages actually deleted are returned and a
 591    * {@link MessageCountEvent} event is sent to all listeners with this folder. The expunge
 592    * may cause the indices of all messaged that remain in the folder to change.
 593    *
 594    * @return the original indices of messages that were actually deleted
 595    * @throws MessagingException if there was a problem accessing the store
 596    */
 597    public abstract Message[] expunge() throws MessagingException;
 598   
 599    /**
 600    * Search this folder for messages matching the supplied search criteria.
 601    * The default implementation simply invoke <code>search(term, getMessages())
 602    * applying the search over all messages in the folder; subclasses may provide
 603    * a more efficient mechanism.
 604    *
 605    * @param term the search criteria
 606    * @return an array containing messages that match the criteria
 607    * @throws MessagingException if there was a problem accessing the store
 608    */
 609  0 public Message[] search(SearchTerm term) throws MessagingException {
 610  0 return search(term, getMessages());
 611    }
 612   
 613    /**
 614    * Search the supplied messages for those that match the supplied criteria;
 615    * messages must belong to this folder.
 616    * The default implementation iterates through the messages, returning those
 617    * whose {@link Message#match(javax.mail.search.SearchTerm)} method returns true;
 618    * subclasses may provide a more efficient implementation.
 619    *
 620    * @param term the search criteria
 621    * @param messages the messages to search
 622    * @return an array containing messages that match the criteria
 623    * @throws MessagingException if there was a problem accessing the store
 624    */
 625  0 public Message[] search(SearchTerm term, Message[] messages) throws MessagingException {
 626  0 List result = new ArrayList(messages.length);
 627  0 for (int i = 0; i < messages.length; i++) {
 628  0 Message message = messages[i];
 629  0 if (message.match(term)) {
 630  0 result.add(message);
 631    }
 632    }
 633  0 return (Message[]) result.toArray(new Message[result.size()]);
 634    }
 635   
 636  0 public void addConnectionListener(ConnectionListener listener) {
 637  0 connectionListeners.add(listener);
 638    }
 639   
 640  0 public void removeConnectionListener(ConnectionListener listener) {
 641  0 connectionListeners.remove(listener);
 642    }
 643   
 644  0 protected void notifyConnectionListeners(int type) {
 645  0 queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
 646    }
 647   
 648  0 public void addFolderListener(FolderListener listener) {
 649  0 folderListeners.add(listener);
 650    }
 651   
 652  0 public void removeFolderListener(FolderListener listener) {
 653  0 folderListeners.remove(listener);
 654    }
 655   
 656  0 protected void notifyFolderListeners(int type) {
 657  0 queue.queueEvent(new FolderEvent(this, this, type), folderListeners);
 658    }
 659   
 660  0 protected void notifyFolderRenamedListeners(Folder newFolder) {
 661  0 queue.queueEvent(new FolderEvent(this, this, newFolder, FolderEvent.RENAMED), folderListeners);
 662    }
 663   
 664  0 public void addMessageCountListener(MessageCountListener listener) {
 665  0 messageCountListeners.add(listener);
 666    }
 667   
 668  0 public void removeMessageCountListener(MessageCountListener listener) {
 669  0 messageCountListeners.remove(listener);
 670    }
 671   
 672  0 protected void notifyMessageAddedListeners(Message[] messages) {
 673  0 queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages), messageChangedListeners);
 674    }
 675   
 676  0 protected void notifyMessageRemovedListeners(boolean removed, Message[] messages) {
 677  0 queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages), messageChangedListeners);
 678    }
 679   
 680  0 public void addMessageChangedListener(MessageChangedListener listener) {
 681  0 messageChangedListeners.add(listener);
 682    }
 683   
 684  0 public void removeMessageChangedListener(MessageChangedListener listener) {
 685  0 messageChangedListeners.remove(listener);
 686    }
 687   
 688  0 protected void notifyMessageChangedListeners(int type, Message message) {
 689  0 queue.queueEvent(new MessageChangedEvent(this, type, message), messageChangedListeners);
 690    }
 691   
 692    /**
 693    * Unregisters all listeners.
 694    */
 695  5 protected void finalize() throws Throwable {
 696  5 queue.stop();
 697  5 connectionListeners.clear();
 698  5 folderListeners.clear();
 699  5 messageChangedListeners.clear();
 700  5 messageCountListeners.clear();
 701  5 store = null;
 702  5 super.finalize();
 703    }
 704   
 705    /**
 706    * Returns the full name of this folder; if null, returns the value from the superclass.
 707    * @return a string form of this folder
 708    */
 709  0 public String toString() {
 710  0 String name = getFullName();
 711  0 return name == null ? super.toString() : name;
 712    }
 713    }