Open Source Repository

Home /mail/mail-1.4.3 | Repository Home


javax/mail/Folder.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.mail;

import java.io.*;
import java.lang.*;
import java.util.Vector;
import java.util.StringTokenizer;
import javax.mail.search.SearchTerm;
import javax.mail.event.*;

/**
 * Folder is an abstract class that represents a folder for mail
 * messages. Subclasses implement protocol specific Folders.<p>
 *
 * Folders can contain Messages, other Folders or both, thus providing
 * a tree-like hierarchy rooted at the Store's default folder. (Note 
 * that some Folder implementations may not allow both Messages and 
 * other Folders in the same Folder).<p>
 *
 * The interpretation of folder names is implementation dependent.
 * The different levels of hierarchy in a folder's full name
 * are separated from each other by the hierarchy delimiter 
 * character.<p>
 *
 * The case-insensitive full folder name (that is, the full name
 * relative to the default folder for a Store) <strong>INBOX</strong>
 * is reserved to mean the "primary folder for this user on this
 * server".  Not all Stores will provide an INBOX folder, and not
 * all users will have an INBOX folder at all times.  The name
 <strong>INBOX</strong> is reserved to refer to this folder,
 * when it exists, in Stores that provide it. <p>
 *
 * A Folder object obtained from a Store need not actually exist
 * in the backend store. The <code>exists</code> method tests whether
 * the folder exists or not. The <code>create</code> method
 * creates a Folder. <p>
 *
 * A Folder is initially in the closed state. Certain methods are valid
 * in this state; the documentation for those methods note this.  A
 * Folder is opened by calling its 'open' method. All Folder methods,
 * except <code>open</code><code>delete</code> and 
 <code>renameTo</code>, are valid in this state. <p>
 *
 * The only way to get a Folder is by invoking the 
 <code>getFolder</code> method on Store, Folder, or Session, or by invoking 
 * the <code>list</code> or <code>listSubscribed</code> methods 
 * on Folder. Folder objects returned by the above methods are not 
 * cached by the Store. Thus, invoking the <code>getFolder</code> method
 * with the same folder name multiple times will return distinct Folder 
 * objects.  Likewise for the <code>list</code> and <code>listSubscribed</code>
 * methods. <p>
 *
 * The Message objects within the Folder are cached by the Folder.
 * Thus, invoking <code>getMessage(msgno)</code> on the same message number
 * multiple times will return the same Message object, until an 
 * expunge is done on this Folder. <p>
 *
 * Message objects from a Folder are only valid while a Folder is open
 * and should not be accessed after the Folder is closed, even if the
 * Folder is subsequently reopened.  Instead, new Message objects must
 * be fetched from the Folder after the Folder is reopened. <p>
 *
 * Note that a Message's message number can change within a
 * session if the containing Folder is expunged using the expunge
 * method.  Clients that use message numbers as references to messages
 * should be aware of this and should be prepared to deal with 
 * situation (probably by flushing out existing message number references
 * and reloading them). Because of this complexity, it is better for
 * clients to use Message objects as references to messages, rather than
 * message numbers. Expunged Message objects still have to be
 * pruned, but other Message objects in that folder are not affected by the 
 * expunge.
 *
 @author John Mani
 @author Bill Shannon
 */

public abstract class Folder {

    /**
     * The parent store.
     */
    protected Store store;

    /**
     * The open mode of this folder.  The open mode is
     <code>Folder.READ_ONLY</code><code>Folder.READ_WRITE</code>,
     * or -1 if not known.
     @since  JavaMail 1.1
     */
    protected int mode = -1;

    /**
     * Constructor that takes a Store object.
     *
     @param store the Store that holds this folder
     */
    protected Folder(Store store) {
  this.store = store;
    }

    /**
     * Returns the name of this Folder. <p>
     *
     * This method can be invoked on a closed Folder.
     *
     @return    name of the Folder
     */
    public abstract String getName();

    /**
     * Returns the full name of this Folder. If the folder resides under
     * the root hierarchy of this Store, the returned name is relative
     * to the root. Otherwise an absolute name, starting with the 
     * hierarchy delimiter, is returned. <p>
     *
     * This method can be invoked on a closed Folder.
     *
     @return    full name of the Folder
     */
    public abstract String getFullName();

    /**
     * Return a URLName representing this folder.  The returned URLName
     * does <em>not</em> include the password used to access the store.
     *
     @return  the URLName representing this folder
     @see  URLName
     @since  JavaMail 1.1
     */
    public URLName getURLName() throws MessagingException {
  URLName storeURL = getStore().getURLName();
  String fullname = getFullName();
  StringBuffer encodedName = new StringBuffer();

  if (fullname != null) {
      /*
      // We need to encode each of the folder's names.
      char separator = getSeparator();
      StringTokenizer tok = new StringTokenizer(
    fullname, new Character(separator).toString(), true);

      while (tok.hasMoreTokens()) {
    String s = tok.nextToken();
    if (s.charAt(0) == separator)
        encodedName.append(separator);
    else
        // XXX - should encode, but since there's no decoder...
        //encodedName.append(java.net.URLEncoder.encode(s));
        encodedName.append(s);
      }
      */
      // append the whole thing, until we can encode
      encodedName.append(fullname);
  }

  /*
   * Sure would be convenient if URLName had a
   * constructor that took a base URLName.
   */
  return new URLName(storeURL.getProtocol(), storeURL.getHost(),
          storeURL.getPort(), encodedName.toString(),
          storeURL.getUsername(),
          null /* no password */);
    }

    /**
     * Returns the Store that owns this Folder object.
     * This method can be invoked on a closed Folder.
     @return     the Store
     */
    public Store getStore() {
  return store;
    }

    /**
     * Returns the parent folder of this folder.
     * This method can be invoked on a closed Folder. If this folder
     * is the top of a folder hierarchy, this method returns null. <p>
     *
     * Note that since Folder objects are not cached, invoking this method
     * returns a new distinct Folder object.
     *
     @return    Parent folder
     */
    public abstract Folder getParent() throws MessagingException;

    /**
     * Tests if this folder physically exists on the Store.
     * This method can be invoked on a closed Folder.
     *
     @return true if the folder exists, otherwise false
     @see    #create
     @exception  MessagingException typically if the connection 
     *      to the server is lost.
     */
    public abstract boolean exists() throws MessagingException;

    /**
     * Returns a list of Folders belonging to this Folder's namespace
     * that match the specified pattern. Patterns may contain the wildcard
     * characters <code>"%"</code>, which matches any character except hierarchy
     * delimiters, and <code>"*"</code>, which matches any character. <p>
     *
     * As an example, given the folder hierarchy: <pre>
     *    Personal/
     *       Finance/
     *          Stocks
     *          Bonus
     *          StockOptions
     *       Jokes
     </pre>
     <code>list("*")</code> on "Personal" will return the whole 
     * hierarchy. <br>
     <code>list("%")</code> on "Personal" will return "Finance" and 
     * "Jokes". <br>
     <code>list("Jokes")</code> on "Personal" will return "Jokes".<br>
     <code>list("Stock*")</code> on "Finance" will return "Stocks"
     * and "StockOptions". <p>
     *
     * Folder objects are not cached by the Store, so invoking this
     * method on the same pattern multiple times will return that many
     * distinct Folder objects. <p>
     *
     * This method can be invoked on a closed Folder.
     *
     @param pattern  the match pattern
     @return    array of matching Folder objects. An empty
     *      array is returned if no matching Folders exist.
     @see     #listSubscribed
     @exception   FolderNotFoundException if this folder does 
     *      not exist.
     @exception   MessagingException
     */
    public abstract Folder[] list(String patternthrows MessagingException;

    /**
     * Returns a list of subscribed Folders belonging to this Folder's
     * namespace that match the specified pattern. If the folder does
     * not support subscription, this method should resolve to
     <code>list</code>.
     * (The default implementation provided here, does just this).
     * The pattern can contain wildcards as for <code>list</code><p>
     *
     * Note that, at a given level of the folder hierarchy, a particular
     * folder may not be subscribed, but folders underneath that folder
     * in the folder hierarchy may be subscribed.  In order to allow
     * walking the folder hierarchy, such unsubscribed folders may be
     * returned, indicating that a folder lower in the hierarchy is
     * subscribed.  The <code>isSubscribed</code> method on a folder will
     * tell whether any particular folder is actually subscribed. <p>
     *
     * Folder objects are not cached by the Store, so invoking this
     * method on the same pattern multiple times will return that many
     * distinct Folder objects. <p>
     *
     * This method can be invoked on a closed Folder.
     *
     @param pattern  the match pattern
     @return    array of matching subscribed Folder objects. An
     *      empty array is returned if no matching
     *      subscribed folders exist.
     @see     #list
     @exception   FolderNotFoundException if this folder does
     *      not exist.
     @exception   MessagingException
     */
    public Folder[] listSubscribed(String patternthrows MessagingException {
  return list(pattern);
    }

    /**
     * Convenience method that returns the list of folders under this
     * Folder. This method just calls the <code>list(String pattern)</code>
     * method with <code>"%"</code> as the match pattern. This method can
     * be invoked on a closed Folder.
     *
     @return    array of Folder objects under this Folder. An
     *      empty array is returned if no subfolders exist.
     @see    #list
     @exception   FolderNotFoundException if this folder does
     *      not exist.
     @exception   MessagingException
     */

    public Folder[] list() throws MessagingException {
  return list("%");
    }

    /**
     * Convenience method that returns the list of subscribed folders 
     * under this Folder. This method just calls the
     <code>listSubscribed(String pattern)</code> method with <code>"%"</code>
     * as the match pattern. This method can be invoked on a closed Folder.
     *
     @return    array of subscribed Folder objects under this 
     *      Folder. An empty array is returned if no subscribed 
     *      subfolders exist.
     @see    #listSubscribed
     @exception   FolderNotFoundException if this folder does
     *      not exist.
     @exception   MessagingException
     */
    public Folder[] listSubscribed() throws MessagingException {
  return listSubscribed("%");
    }

    /**
     * Return the delimiter character that separates this Folder's pathname
     * from the names of immediate subfolders. This method can be invoked 
     * on a closed Folder.
     *
     @exception   FolderNotFoundException if the implementation
     *      requires the folder to exist, but it does not
     @return          Hierarchy separator character
     */
    public abstract char getSeparator() throws MessagingException;

    /**
     * This folder can contain messages
     */
    public final static int HOLDS_MESSAGES = 0x01;

    /**
     * This folder can contain other folders
     */
    public final static int HOLDS_FOLDERS  = 0x02;

    /**
     * Returns the type of this Folder, that is, whether this folder can hold
     * messages or subfolders or both. The returned value is an integer
     * bitfield with the appropriate bits set. This method can be invoked
     * on a closed folder.
     
     @return     integer with appropriate bits set
     @exception   FolderNotFoundException if this folder does 
     *      not exist.
     @see     #HOLDS_FOLDERS 
     @see    #HOLDS_MESSAGES
     */
    public abstract int getType() throws MessagingException; 

    /**
     * Create this folder on the Store. When this folder is created, any
     * folders in its path that do not exist are also created. <p>
     *
     * If the creation is successful, a CREATED FolderEvent is delivered
     * to any FolderListeners registered on this Folder and this Store.
     *
     @param  type  The type of this folder. 
     *
     @return    true if the creation succeeds, else false.
     @exception   MessagingException  
     @see     #HOLDS_FOLDERS
     @see    #HOLDS_MESSAGES
     @see    javax.mail.event.FolderEvent
     */
    public abstract boolean create(int typethrows MessagingException;

    /**
     * Returns true if this Folder is subscribed. <p>
     *
     * This method can be invoked on a closed Folder. <p>
     *
     * The default implementation provided here just returns true.
     *
     @return    true if this Folder is subscribed
     */
    public boolean isSubscribed() {
  return true;
    }

    /**
     * Subscribe or unsubscribe this Folder. Not all Stores support
     * subscription. <p>
     *
     * This method can be invoked on a closed Folder. <p>
     *
     * The implementation provided here just throws the
     * MethodNotSupportedException.
     *
     @param subscribe  true to subscribe, false to unsubscribe
     @exception   FolderNotFoundException if this folder does
     *      not exist.
     @exception   MethodNotSupportedException if this store
     *      does not support subscription
     @exception   MessagingException
     */
    public void setSubscribed(boolean subscribe
      throws MessagingException {
  throw new MethodNotSupportedException();
    }

    /**
     * Returns true if this Folder has new messages since the last time
     * this indication was reset.  When this indication is set or reset
     * depends on the Folder implementation (and in the case of IMAP,
     * depends on the server).  This method can be used to implement
     * a lightweight "check for new mail" operation on a Folder without
     * opening it.  (For example, a thread that monitors a mailbox and
     * flags when it has new mail.)  This method should indicate whether
     * any messages in the Folder have the <code>RECENT</code> flag set. <p>
     *
     * Note that this is not an incremental check for new mail, i.e.,
     * it cannot be used to determine whether any new messages have
     * arrived since the last time this method was invoked. To
     * implement incremental checks, the Folder needs to be opened. <p>
     *
     * This method can be invoked on a closed Folder that can contain
     * Messages.
     *
     @return    true if the Store has new Messages
     @exception  FolderNotFoundException if this folder does
     *      not exist.
     @exception   MessagingException
     */
    public abstract boolean hasNewMessages() throws MessagingException;

    /**
     * Return the Folder object corresponding to the given name. Note that
     * this folder does not physically have to exist in the Store. The
     <code>exists()</code> method on a Folder indicates whether it really
     * exists on the Store. <p>
     *
     * In some Stores, name can be an absolute path if it starts with the
     * hierarchy delimiter.  Otherwise, it is interpreted relative to
     * this Folder. <p>
     *
     * Folder objects are not cached by the Store, so invoking this
     * method on the same name multiple times will return that many
     * distinct Folder objects. <p>
     *
     * This method can be invoked on a closed Folder.
     *
     @param name   name of the Folder
     @return    Folder object
     @exception   MessagingException
     */
    public abstract Folder getFolder(String name)
        throws MessagingException;

    /**
     * Delete this Folder. This method will succeed only on a closed
     * Folder. <p>
     *
     * The <code>recurse</code> flag controls whether the deletion affects
     * subfolders or not. If true, all subfolders are deleted, then this
     * folder itself is deleted. If false, the behaviour is dependent on
     * the folder type and is elaborated below: <p>
     *
     <ul>
     <li>
     * The folder can contain only messages: (type == HOLDS_MESSAGES).
     <br>
     * All messages within the folder are removed. The folder 
     * itself is then removed. An appropriate FolderEvent is generated by 
     * the Store and this folder. <p>
     *
     <li>
     * The folder can contain only subfolders: (type == HOLDS_FOLDERS).
     <br>
     * If this folder is empty (does not contain any 
     * subfolders at all), it is removed. An appropriate FolderEvent is 
     * generated by the Store and this folder.<br>
     * If this folder contains any subfolders, the delete fails 
     * and returns false. <p>
     *
     <li>
     * The folder can contain subfolders as well as messages: <br>
     * If the folder is empty (no messages or subfolders), it
     * is removed. If the folder contains no subfolders, but only messages,
     * then all messages are removed. The folder itself is then removed.
     * In both the above cases, an appropriate FolderEvent is
     * generated by the Store and this folder. <p>
     *
     * If the folder contains subfolders there are 3 possible
     * choices an implementation is free to do: <p>
     
     *  <ol>
     *   <li> The operation fails, irrespective of whether this folder
     * contains messages or not. Some implementations might elect to go
     * with this simple approach. The delete() method returns false.
     *
     *   <li> Any messages within the folder are removed. Subfolders
     * are not removed. The folder itself is not removed or affected
     * in any manner. The delete() method returns true. And the 
     * exists() method on this folder will return true indicating that
     * this folder still exists. <br>
     * An appropriate FolderEvent is generated by the Store and this folder.
     *
     *   <li> Any messages within the folder are removed. Subfolders are
     * not removed. The folder itself changes its type from 
     * HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new 
     * messages cannot be added to this folder, but new subfolders can
     * be created underneath. The delete() method returns true indicating
     * success. The exists() method on this folder will return true
     * indicating that this folder still exists. <br>
     * An appropriate FolderEvent is generated by the Store and this folder.
     </ol>
     </ul>
     *
     @return    true if the Folder is deleted successfully
     @exception  FolderNotFoundException if this folder does 
     *      not exist
     @exception  IllegalStateException if this folder is not in 
     *      the closed state.
     @exception       MessagingException
     @see    javax.mail.event.FolderEvent
     */
    public abstract boolean delete(boolean recurse
        throws MessagingException;

    /**
     * Rename this Folder. This method will succeed only on a closed
     * Folder. <p>
     *
     * If the rename is successful, a RENAMED FolderEvent is delivered
     * to FolderListeners registered on this folder and its containing
     * Store.
     *
     @param f    a folder representing the new name for this Folder
     @return    true if the Folder is renamed successfully
     @exception  FolderNotFoundException if this folder does 
     *      not exist
     @exception  IllegalStateException if this folder is not in 
     *      the closed state.
     @exception       MessagingException
     @see    javax.mail.event.FolderEvent
     */
    public abstract boolean renameTo(Folder fthrows MessagingException;

    /**
     * The Folder is read only.  The state and contents of this
     * folder cannot be modified.
     */
    public static final int READ_ONLY   = 1;

    /**
     * The state and contents of this folder can be modified.
     */
    public static final int READ_WRITE   = 2;

    /**
     * Open this Folder. This method is valid only on Folders that
     * can contain Messages and that are closed. <p>
     *
     * If this folder is opened successfully, an OPENED ConnectionEvent
     * is delivered to any ConnectionListeners registered on this 
     * Folder. <p>
     *
     * The effect of opening multiple connections to the same folder
     * on a specifc Store is implementation dependent. Some implementations
     * allow multiple readers, but only one writer. Others allow
     * multiple writers as well as readers.
     *
     @param mode  open the Folder READ_ONLY or READ_WRITE
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not in 
     *      the closed state.
     @exception       MessagingException
     @see     #READ_ONLY
     @see     #READ_WRITE
     @see     #getType()
     @see     javax.mail.event.ConnectionEvent
     */
    public abstract void open(int modethrows MessagingException;

    /**
     * Close this Folder. This method is valid only on open Folders. <p>
     *
     * A CLOSED ConnectionEvent is delivered to any ConnectionListeners
     * registered on this Folder. Note that the folder is closed even
     * if this method terminates abnormally by throwing a
     * MessagingException.
     *
     @param expunge  expunges all deleted messages if this flag is true
     @exception  IllegalStateException if this folder is not opened
     @exception       MessagingException
     @see     javax.mail.event.ConnectionEvent
     */
    public abstract void close(boolean expungethrows MessagingException;

    /**
     * Indicates whether this Folder is in the 'open' state.
     @return  true if this Folder is in the 'open' state.
     */
    public abstract boolean isOpen();

    /**
     * Return the open mode of this folder.  Returns
     <code>Folder.READ_ONLY</code><code>Folder.READ_WRITE</code>,
     * or -1 if the open mode is not known (usually only because an older
     <code>Folder</code> provider has not been updated to use this new
     * method).
     *
     @exception  IllegalStateException if this folder is not opened
     @return          the open mode of this folder
     @since    JavaMail 1.1
     */
    public int getMode() {
  if (!isOpen())
      throw new IllegalStateException("Folder not open");
  return mode;
    }
 
    /**
     * Get the permanent flags supported by this Folder. Returns a Flags
     * object that contains all the flags supported. <p>
     *
     * The special flag <code>Flags.USER </code> indicates that this Folder
     * supports arbitrary user-defined flags. <p>
     *
     * The supported permanent flags for a folder may not be available
     * until the folder is opened.
     
     @return     permanent flags, or null if not known
     */
    public abstract Flags getPermanentFlags();

    /**
     * Get total number of messages in this Folder. <p>
     *
     * This method can be invoked on a closed folder. However, note
     * that for some folder implementations, getting the total message
     * count can be an expensive operation involving actually opening 
     * the folder. In such cases, a provider can choose not to support 
     * this functionality in the closed state, in which case this method
     * must return -1. <p>
     *
     * Clients invoking this method on a closed folder must be aware
     * that this is a potentially expensive operation. Clients must
     * also be prepared to handle a return value of -1 in this case.
     
     @return     total number of messages. -1 may be returned
     *      by certain implementations if this method is
     *      invoked on a closed folder.
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception       MessagingException
     */
    public abstract int getMessageCount() throws MessagingException;

    /**
     * Get the number of new messages in this Folder. <p>
     *
     * This method can be invoked on a closed folder. However, note
     * that for some folder implementations, getting the new message
     * count can be an expensive operation involving actually opening 
     * the folder. In such cases, a provider can choose not to support 
     * this functionality in the closed state, in which case this method
     * must return -1. <p>
     *
     * Clients invoking this method on a closed folder must be aware
     * that this is a potentially expensive operation. Clients must
     * also be prepared to handle a return value of -1 in this case. <p>
     *
     * This implementation returns -1 if this folder is closed. Else
     * this implementation gets each Message in the folder using
     <code>getMessage(int)</code> and checks whether its
     <code>RECENT</code> flag is set. The total number of messages
     * that have this flag set is returned.
     *
     @return     number of new messages. -1 may be returned
     *      by certain implementations if this method is
     *      invoked on a closed folder.
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception       MessagingException
     */
    public synchronized int getNewMessageCount() 
      throws MessagingException {
  if (!isOpen())
      return -1;

  int newmsgs = 0;
  int total = getMessageCount();
  for (int i = 1; i <= total; i++) {
      try {
    if (getMessage(i).isSet(Flags.Flag.RECENT))
        newmsgs++;
      catch (MessageRemovedException me) {
    // This is an expunged message, ignore it.
    continue;
      }
  }
  return newmsgs;
    }

    /**
     * Get the total number of unread messages in this Folder. <p>
     *
     * This method can be invoked on a closed folder. However, note
     * that for some folder implementations, getting the unread message
     * count can be an expensive operation involving actually opening 
     * the folder. In such cases, a provider can choose not to support 
     * this functionality in the closed state, in which case this method
     * must return -1. <p>
     *
     * Clients invoking this method on a closed folder must be aware
     * that this is a potentially expensive operation. Clients must
     * also be prepared to handle a return value of -1 in this case. <p>
     *
     * This implementation returns -1 if this folder is closed. Else
     * this implementation gets each Message in the folder using
     <code>getMessage(int)</code> and checks whether its
     <code>SEEN</code> flag is set. The total number of messages
     * that do not have this flag set is returned.
     *
     @return     total number of unread messages. -1 may be returned
     *      by certain implementations if this method is
     *      invoked on a closed folder.
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception       MessagingException
     */
    public synchronized int getUnreadMessageCount() 
      throws MessagingException {
  if (!isOpen())
      return -1;

  int unread = 0;
  int total = getMessageCount();
  for (int i = 1; i <= total; i++) {
      try {
    if (!getMessage(i).isSet(Flags.Flag.SEEN))
        unread++;
      catch (MessageRemovedException me) {
    // This is an expunged message, ignore it.
    continue;
      }
  }
  return unread;
    }

    /**
     * Get the number of deleted messages in this Folder. <p>
     *
     * This method can be invoked on a closed folder. However, note
     * that for some folder implementations, getting the deleted message
     * count can be an expensive operation involving actually opening 
     * the folder. In such cases, a provider can choose not to support 
     * this functionality in the closed state, in which case this method
     * must return -1. <p>
     *
     * Clients invoking this method on a closed folder must be aware
     * that this is a potentially expensive operation. Clients must
     * also be prepared to handle a return value of -1 in this case. <p>
     *
     * This implementation returns -1 if this folder is closed. Else
     * this implementation gets each Message in the folder using
     <code>getMessage(int)</code> and checks whether its
     <code>DELETED</code> flag is set. The total number of messages
     * that have this flag set is returned.
     *
     @return     number of deleted messages. -1 may be returned
     *      by certain implementations if this method is
     *      invoked on a closed folder.
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception       MessagingException
     @since    JavaMail 1.3
     */
    public synchronized int getDeletedMessageCount() throws MessagingException {
  if (!isOpen())
      return -1;

  int deleted = 0;
  int total = getMessageCount();
  for (int i = 1; i <= total; i++) {
      try {
    if (getMessage(i).isSet(Flags.Flag.DELETED))
        deleted++;
      catch (MessageRemovedException me) {
    // This is an expunged message, ignore it.
    continue;
      }
  }
  return deleted;
    }

    /**
     * Get the Message object corresponding to the given message
     * number.  A Message object's message number is the relative
     * position of this Message in its Folder. Messages are numbered
     * starting at 1 through the total number of message in the folder.
     * Note that the message number for a particular Message can change
     * during a session if other messages in the Folder are deleted and
     * the Folder is expunged. <p>
     *
     * Message objects are light-weight references to the actual message
     * that get filled up on demand. Hence Folder implementations are 
     * expected to provide light-weight Message objects. <p>
     *
     * Unlike Folder objects, repeated calls to getMessage with the
     * same message number will return the same Message object, as
     * long as no messages in this folder have been expunged. <p>
     *
     * Since message numbers can change within a session if the folder
     * is expunged , clients are advised not to use message numbers as 
     * references to messages. Use Message objects instead.
     *
     @param msgnum  the message number
     @return     the Message object
     @see    #getMessageCount
     @see    #fetch
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not opened
     @exception  IndexOutOfBoundsException if the message number
     *      is out of range.
     @exception   MessagingException
     */
    public abstract Message getMessage(int msgnum)
        throws MessagingException;

    /**
     * Get the Message objects for message numbers ranging from start
     * through end, both start and end inclusive. Note that message 
     * numbers start at 1, not 0. <p>
     *
     * Message objects are light-weight references to the actual message
     * that get filled up on demand. Hence Folder implementations are 
     * expected to provide light-weight Message objects. <p>
     *
     * This implementation uses getMessage(index) to obtain the required
     * Message objects. Note that the returned array must contain 
     <code>(end-start+1)</code> Message objects.
     
     @param start  the number of the first message
     @param end  the number of the last message
     @return     the Message objects
     @see    #fetch
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not opened.
     @exception  IndexOutOfBoundsException if the start or end
     *      message numbers are out of range.
     @exception   MessagingException
     */ 
    public synchronized Message[] getMessages(int start, int end
      throws MessagingException {
  Message[] msgs = new Message[end - start +1];
  for (int i = start; i <= end; i++)
      msgs[i - start= getMessage(i);
  return msgs;
    }

    /**
     * Get the Message objects for message numbers specified in
     * the array. <p>
     *
     * Message objects are light-weight references to the actual message
     * that get filled up on demand. Hence Folder implementations are 
     * expected to provide light-weight Message objects. <p>
     *
     * This implementation uses getMessage(index) to obtain the required
     * Message objects. Note that the returned array must contain 
     <code>msgnums.length</code> Message objects
     *
     @param msgnums  the array of message numbers
     @return     the array of Message objects. 
     @see    #fetch
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not opened.
     @exception  IndexOutOfBoundsException if any message number
     *      in the given array is out of range.
     @exception   MessagingException
     */ 
    public synchronized Message[] getMessages(int[] msgnums)
      throws MessagingException {
  int len = msgnums.length;
  Message[] msgs = new Message[len];
  for (int i = 0; i < len; i++)
      msgs[i= getMessage(msgnums[i]);
  return msgs;
    }

    /**
     * Get all Message objects from this Folder. Returns an empty array
     * if the folder is empty.
     *
     * Clients can use Message objects (instead of sequence numbers) 
     * as references to the messages within a folder; this method supplies 
     * the Message objects to the client. Folder implementations are 
     * expected to provide light-weight Message objects, which get
     * filled on demand. <p>
     *
     * This implementation invokes <code>getMessageCount()</code> to get
     * the current message count and then uses <code>getMessage()</code>
     * to get Message objects from 1 till the message count.
     *
     @return     array of Message objects, empty array if folder
     *      is empty.
     @see    #fetch
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not opened.
     @exception   MessagingException
     */ 
    public synchronized Message[] getMessages() throws MessagingException {
  if (!isOpen())  // otherwise getMessageCount might return -1
      throw new IllegalStateException("Folder not open");
  int total = getMessageCount();
  Message[] msgs = new Message[total];
  for (int i = 1; i <= total; i++)
      msgs[i-1= getMessage(i);  
  return msgs;
    }

    /**
     * Append given Messages to this folder. This method can be 
     * invoked on a closed Folder. An appropriate MessageCountEvent 
     * is delivered to any MessageCountListener registered on this 
     * folder when the messages arrive in the folder. <p>
     *
     * Folder implementations must not abort this operation if a
     * Message in the given message array turns out to be an
     * expunged Message.
     *
     @param msgs  array of Messages to be appended
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception   MessagingException if the append failed.
     */
    public abstract void appendMessages(Message[] msgs)
        throws MessagingException;

    /**
     * Prefetch the items specified in the FetchProfile for the
     * given Messages. <p>
     *
     * Clients use this method to indicate that the specified items are 
     * needed en-masse for the given message range. Implementations are 
     * expected to retrieve these items for the given message range in
     * a efficient manner. Note that this method is just a hint to the
     * implementation to prefetch the desired items. <p>
     *
     * An example is a client filling its header-view window with
     * the Subject, From and X-mailer headers for all messages in the 
     * folder.<p>
     <blockquote><pre>
     *
     *  Message[] msgs = folder.getMessages();
     *
     *  FetchProfile fp = new FetchProfile();
     *  fp.add(FetchProfile.Item.ENVELOPE);
     *  fp.add("X-mailer");
     *  folder.fetch(msgs, fp);
     *  
     *  for (int i = 0; i < folder.getMessageCount(); i++) {
     *      display(msg[i].getFrom());
     *      display(msg[i].getSubject());
     *      display(msg[i].getHeader("X-mailer"));
     *  }
     *
     </pre></blockquote><p>
     *
     * The implementation provided here just returns without
     * doing anything useful. Providers wanting to provide a real 
     * implementation for this method should override this method.
     *
     @param msgs  fetch items for these messages
     @param fp  the FetchProfile
     @exception  IllegalStateException if this folder is not opened
     @exception  MessagingException.
     */
    public void fetch(Message[] msgs, FetchProfile fp)
      throws MessagingException {
  return;
    }

    /**
     * Set the specified flags on the messages specified in the array.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder. <p>
     *
     * Note that the specified Message objects <strong>must</strong> 
     * belong to this folder. Certain Folder implementations can
     * optimize the operation of setting Flags for a group of messages,
     * so clients might want to use this method, rather than invoking
     <code>Message.setFlags</code> for each Message. <p>
     *
     * This implementation degenerates to invoking <code>setFlags()</code>
     * on each Message object. Specific Folder implementations that can 
     * optimize this case should do so. 
     * Also, an implementation must not abort the operation if a Message 
     * in the array turns out to be an expunged Message.
     *
     @param msgs  the array of message objects
     @param flag  Flags object containing the flags to be set
     @param value  set the flags to this boolean value
     @exception  IllegalStateException if this folder is not opened
     *      or if it has been opened READ_ONLY.
     @exception   MessagingException
     @see    Message#setFlags
     @see    javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(Message[] msgs,
      Flags flag, boolean valuethrows  MessagingException {
  for (int i = 0; i < msgs.length; i++) {
      try {
    msgs[i].setFlags(flag, value);
      catch (MessageRemovedException me) {
    // This message is expunged, skip 
      }
  }
    }

    /**
     * Set the specified flags on the messages numbered from start
     * through end, both start and end inclusive. Note that message 
     * numbers start at 1, not 0.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder. <p>
     *
     * Certain Folder implementations can
     * optimize the operation of setting Flags for a group of messages,
     * so clients might want to use this method, rather than invoking
     <code>Message.setFlags</code> for each Message. <p>
     *
     * The default implementation uses <code>getMessage(int)</code> to
     * get each <code>Message</code> object and then invokes
     <code>setFlags</code> on that object to set the flags.
     * Specific Folder implementations that can optimize this case should do so.
     * Also, an implementation must not abort the operation if a message 
     * number refers to an expunged message.
     *
     @param start  the number of the first message
     @param end  the number of the last message
     @param flag  Flags object containing the flags to be set
     @param value  set the flags to this boolean value
     @exception  IllegalStateException if this folder is not opened
     *      or if it has been opened READ_ONLY.
     @exception  IndexOutOfBoundsException if the start or end
     *      message numbers are out of range.
     @exception   MessagingException
     @see    Message#setFlags
     @see    javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(int start, int end,
      Flags flag, boolean valuethrows MessagingException {
  for (int i = start; i <= end; i++) {
      try {
    Message msg = getMessage(i);
    msg.setFlags(flag, value);
      catch (MessageRemovedException me) {
    // This message is expunged, skip 
      }
  }
    }

    /**
     * Set the specified flags on the messages whose message numbers
     * are in the array.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder. <p>
     *
     * Certain Folder implementations can
     * optimize the operation of setting Flags for a group of messages,
     * so clients might want to use this method, rather than invoking
     <code>Message.setFlags</code> for each Message. <p>
     *
     * The default implementation uses <code>getMessage(int)</code> to
     * get each <code>Message</code> object and then invokes
     <code>setFlags</code> on that object to set the flags.
     * Specific Folder implementations that can optimize this case should do so.
     * Also, an implementation must not abort the operation if a message 
     * number refers to an expunged message.
     *
     @param msgnums  the array of message numbers
     @param flag  Flags object containing the flags to be set
     @param value  set the flags to this boolean value
     @exception  IllegalStateException if this folder is not opened
     *      or if it has been opened READ_ONLY.
     @exception  IndexOutOfBoundsException if any message number
     *      in the given array is out of range.
     @exception   MessagingException
     @see    Message#setFlags
     @see    javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(int[] msgnums,
      Flags flag, boolean valuethrows MessagingException {
  for (int i = 0; i < msgnums.length; i++) {
      try {
    Message msg = getMessage(msgnums[i]);
    msg.setFlags(flag, value);
      catch (MessageRemovedException me) {
    // This message is expunged, skip 
      }
  }
    }

    /**
     * Copy the specified Messages from this Folder into another 
     * Folder. This operation appends these Messages to the 
     * destination Folder. The destination Folder does not have to 
     * be opened.  An appropriate MessageCountEvent 
     * is delivered to any MessageCountListener registered on the 
     * destination folder when the messages arrive in the folder. <p>
     *
     * Note that the specified Message objects <strong>must</strong> 
     * belong to this folder. Folder implementations might be able
     * to optimize this method by doing server-side copies. <p>
     *
     * This implementation just invokes <code>appendMessages()</code>
     * on the destination folder to append the given Messages. Specific
     * folder implementations that support server-side copies should
     * do so, if the destination folder's Store is the same as this
     * folder's Store. 
     * Also, an implementation must not abort the operation if a 
     * Message in the array turns out to be an expunged Message.
     *
     @param msgs  the array of message objects
     @param folder  the folder to copy the messages to
     @exception  FolderNotFoundException if the destination
     *      folder does not exist.
     @exception  IllegalStateException if this folder is not opened.
     @exception  MessagingException
     @see    #appendMessages
     */
    public void copyMessages(Message[] msgs, Folder folder)
        throws MessagingException {
  if (!folder.exists())
      throw new FolderNotFoundException(
      folder.getFullName() " does not exist",
      folder);

  folder.appendMessages(msgs);
    }

    /**
     * Expunge (permanently remove) messages marked DELETED. Returns an
     * array containing the expunged message objects.  The
     <code>getMessageNumber</code> method
     * on each of these message objects returns that Message's original
     * (that is, prior to the expunge) sequence number. A MessageCountEvent 
     * containing the expunged messages is delivered to any 
     * MessageCountListeners registered on the folder. <p>
     *
     * Expunge causes the renumbering of Message objects subsequent to
     * the expunged messages. Clients that use message numbers as 
     * references to messages should be aware of this and should be 
     * prepared to deal with the situation (probably by flushing out 
     * existing message number caches and reloading them). Because of 
     * this complexity, it is better for clients to use Message objects
     * as references to messages, rather than message numbers. Any 
     * expunged Messages objects still have to be pruned, but other 
     * Messages in that folder are not affected by the expunge. <p>
     *
     * After a message is expunged, only the <code>isExpunged</code> and 
     <code>getMessageNumber</code> methods are still valid on the
     * corresponding Message object; other methods may throw
     <code>MessageRemovedException</code>
     *
     @return    array of expunged Message objects
     @exception  FolderNotFoundException if this folder does not
     *      exist
     @exception  IllegalStateException if this folder is not opened.
     @exception       MessagingException
     @see    Message#isExpunged
     @see    javax.mail.event.MessageCountEvent
     */
    public abstract Message[] expunge() throws MessagingException;

    /**
     * Search this Folder for messages matching the specified
     * search criterion. Returns an array containing the matching
     * messages . Returns an empty array if no matches were found. <p>
     *
     * This implementation invokes 
     <code>search(term, getMessages())</code>, to apply the search 
     * over all the messages in this folder. Providers that can implement
     * server-side searching might want to override this method to provide
     * a more efficient implementation.
     *
     @param term  the search criterion
     @return     array of matching messages 
     @exception       javax.mail.search.SearchException if the search 
     *      term is too complex for the implementation to handle.
     @exception  FolderNotFoundException if this folder does 
     *      not exist.
     @exception  IllegalStateException if this folder is not opened.
     @exception       MessagingException
     @see    javax.mail.search.SearchTerm
     */
    public Message[] search(SearchTerm termthrows MessagingException {
  return search(term, getMessages());
    }

    /**
     * Search the given array of messages for those that match the 
     * specified search criterion. Returns an array containing the 
     * matching messages. Returns an empty array if no matches were 
     * found. <p>
     *
     * Note that the specified Message objects <strong>must</strong> 
     * belong to this folder. <p>
     *
     * This implementation iterates through the given array of messages,
     * and applies the search criterion on each message by calling
     * its <code>match()</code> method with the given term. The
     * messages that succeed in the match are returned. Providers
     * that can implement server-side searching might want to override
     * this method to provide a more efficient implementation. If the
     * search term is too complex or contains user-defined terms that
     * cannot be executed on the server, providers may elect to either
     * throw a SearchException or degenerate to client-side searching by
     * calling <code>super.search()</code> to invoke this implementation.
     *
     @param term  the search criterion
     @param msgs   the messages to be searched
     @return     array of matching messages 
     @exception       javax.mail.search.SearchException if the search 
     *      term is too complex for the implementation to handle.
     @exception  IllegalStateException if this folder is not opened
     @exception       MessagingException
     @see    javax.mail.search.SearchTerm
     */
    public Message[] search(SearchTerm term, Message[] msgs)
        throws MessagingException {
  Vector matchedMsgs = new Vector();

  // Run thru the given messages
  for (int i = 0; i < msgs.length; i++) {
      try {
    if (msgs[i].match(term)) // matched
        matchedMsgs.addElement(msgs[i])// add it
      catch(MessageRemovedException mrex) { }
  }

  Message[] m = new Message[matchedMsgs.size()];
  matchedMsgs.copyInto(m);
  return m;
    }

    /*
     * The set of listeners are stored in Vectors appropriate to their
     * type.  We mark all listener Vectors as "volatile" because, while
     * we initialize them inside this folder's synchronization lock,
     * they are accessed (checked for null) in the "notify" methods,
     * which can't be synchronized due to lock ordering constraints.
     * Since the listener fields (the handles on the Vector objects)
     * are only ever set, and are never cleared, we believe this is
     * safe.  The code that dispatches the notifications will either
     * see the null and assume there are no listeners or will see the
     * Vector and will process the listeners.  There's an inherent race
     * between adding a listener and notifying the listeners; the lack
     * of synchronization during notification does not make the race
     * condition significantly worse.  If one thread is setting a
     * listener at the "same" time an event is being dispatched, the
     * dispatch code might not see the listener right away.  The
     * dispatch code doesn't have to worry about the Vector handle
     * being set to null, and thus using an out-of-date set of
     * listeners, because we never set the field to null.
     */

    // Vector of connection listeners.
    private volatile Vector connectionListeners = null;

    /**
     * Add a listener for Connection events on this Folder. <p>
     *
     * The implementation provided here adds this listener
     * to an internal list of ConnectionListeners.
     *
     @param l   the Listener for Connection events
     @see    javax.mail.event.ConnectionEvent
     */
    public synchronized void
    addConnectionListener(ConnectionListener l) { 
     if (connectionListeners == null
      connectionListeners = new Vector();
  connectionListeners.addElement(l);
    }

    /**
     * Remove a Connection event listener. <p>
     *
     * The implementation provided here removes this listener
     * from the internal list of ConnectionListeners.
     *
     @param l   the listener
     @see    #addConnectionListener
     */
    public synchronized void
    removeConnectionListener(ConnectionListener l) { 
     if (connectionListeners != null
      connectionListeners.removeElement(l);
    }

    /**
     * Notify all ConnectionListeners. Folder implementations are
     * expected to use this method to broadcast connection events. <p>
     *
     * The provided implementation queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to the registered
     * ConnectionListeners. Note that the event dispatching occurs
     * in a separate thread, thus avoiding potential deadlock problems.
     *
     @param type  the ConnectionEvent type
     @see    javax.mail.event.ConnectionEvent
     */
    protected void notifyConnectionListeners(int type) {
     if (connectionListeners != null) {
      ConnectionEvent e = new ConnectionEvent(this, type);
      queueEvent(e, connectionListeners);
  }

  /* Fix for broken JDK1.1.x Garbage collector :
   *  The 'conservative' GC in JDK1.1.x occasionally fails to
   *  garbage-collect Threads which are in the wait state.
   *  This would result in thread (and consequently memory) leaks.
   
   * We attempt to fix this by sending a 'terminator' event
   * to the queue, after we've sent the CLOSED event. The
   * terminator event causes the event-dispatching thread to
   * self destruct.
   */
  if (type == ConnectionEvent.CLOSED)
      terminateQueue();
    }

    // Vector of folder listeners
    private volatile Vector folderListeners = null;

    /**
     * Add a listener for Folder events on this Folder. <p>
     *
     * The implementation provided here adds this listener
     * to an internal list of FolderListeners.
     *
     @param l   the Listener for Folder events
     @see    javax.mail.event.FolderEvent
     */
    public synchronized void addFolderListener(FolderListener l) { 
     if (folderListeners == null)
      folderListeners = new Vector();
  folderListeners.addElement(l);
    }

    /**
     * Remove a Folder event listener. <p>
     *
     * The implementation provided here removes this listener
     * from the internal list of FolderListeners.
     *
     @param l   the listener
     @see    #addFolderListener
     */
    public synchronized void removeFolderListener(FolderListener l) {
  if (folderListeners != null)
      folderListeners.removeElement(l);
    }

    /**
     * Notify all FolderListeners registered on this Folder and
     * this folder's Store. Folder implementations are expected
     * to use this method to broadcast Folder events. <p>
     *
     * The implementation provided here queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to the 
     * FolderListeners registered on this folder. The implementation
     * also invokes <code>notifyFolderListeners</code> on this folder's
     * Store to notify any FolderListeners registered on the store.
     *
     @param type  type of FolderEvent
     @see    #notifyFolderRenamedListeners
     */
    protected void notifyFolderListeners(int type) { 
     if (folderListeners != null) {
      FolderEvent e = new FolderEvent(this, this, type);
      queueEvent(e, folderListeners);
  }
  store.notifyFolderListeners(type, this);
    }

    /**
     * Notify all FolderListeners registered on this Folder and
     * this folder's Store about the renaming of this folder.
     * Folder implementations are expected to use this method to
     * broadcast Folder events indicating the renaming of folders. <p>
     *
     * The implementation provided here queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to the 
     * FolderListeners registered on this folder. The implementation
     * also invokes <code>notifyFolderRenamedListeners</code> on this 
     * folder's Store to notify any FolderListeners registered on the store.
     *
     @param  folder  Folder representing the new name.
     @see    #notifyFolderListeners
     @since    JavaMail 1.1
     */
    protected void notifyFolderRenamedListeners(Folder folder) {
     if (folderListeners != null) {
      FolderEvent e = new FolderEvent(this, this, folder,
              FolderEvent.RENAMED);
      queueEvent(e, folderListeners);
  }
  store.notifyFolderRenamedListeners(this, folder);
    }

    // Vector of MessageCount listeners
    private volatile Vector messageCountListeners = null;

    /**
     * Add a listener for MessageCount events on this Folder. <p>
     *
     * The implementation provided here adds this listener
     * to an internal list of MessageCountListeners.
     *
     @param l   the Listener for MessageCount events
     @see    javax.mail.event.MessageCountEvent
     */
    public synchronized void addMessageCountListener(MessageCountListener l) { 
     if (messageCountListeners == null)
      messageCountListeners = new Vector();
  messageCountListeners.addElement(l);
    }

    /**
     * Remove a MessageCount listener. <p>
     *
     * The implementation provided here removes this listener
     * from the internal list of MessageCountListeners.
     *
     @param l   the listener
     @see    #addMessageCountListener
     */
    public synchronized void
      removeMessageCountListener(MessageCountListener l) { 
     if (messageCountListeners != null
      messageCountListeners.removeElement(l)
    }

    /**
     * Notify all MessageCountListeners about the addition of messages
     * into this folder. Folder implementations are expected to use this 
     * method to broadcast MessageCount events for indicating arrival of
     * new messages. <p>
     *
     * The provided implementation queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to the registered
     * MessageCountListeners. Note that the event dispatching occurs
     * in a separate thread, thus avoiding potential deadlock problems.
     */
    protected void notifyMessageAddedListeners(Message[] msgs) { 
     if (messageCountListeners == null)
      return;

  MessageCountEvent e = new MessageCountEvent(
          this, 
          MessageCountEvent.ADDED, 
          false,
          msgs);

     queueEvent(e, messageCountListeners)
    }

    /**
     * Notify all MessageCountListeners about the removal of messages
     * from this Folder. Folder implementations are expected to use this 
     * method to broadcast MessageCount events indicating removal of
     * messages. <p>
     *
     * The provided implementation queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to the registered
     * MessageCountListeners. Note that the event dispatching occurs
     * in a separate thread, thus avoiding potential deadlock problems.
     */
    protected void notifyMessageRemovedListeners(boolean removed, 
             Message[] msgs) { 
     if (messageCountListeners == null)
      return;

  MessageCountEvent e = new MessageCountEvent(
          this, 
          MessageCountEvent.REMOVED, 
          removed,
          msgs);
     queueEvent(e, messageCountListeners)
    }

    // Vector of MessageChanged listeners.
    private volatile Vector messageChangedListeners = null;

    /**
     * Add a listener for MessageChanged events on this Folder. <p>
     *
     * The implementation provided here adds this listener
     * to an internal list of MessageChangedListeners.
     *
     @param l   the Listener for MessageChanged events
     @see    javax.mail.event.MessageChangedEvent
     */
    public synchronized void
      addMessageChangedListener(MessageChangedListener l) { 
     if (messageChangedListeners == null)
      messageChangedListeners = new Vector();
  messageChangedListeners.addElement(l);
    }

    /**
     * Remove a MessageChanged listener. <p>
     *
     * The implementation provided here removes this listener
     * from the internal list of MessageChangedListeners.
     *
     @param l   the listener
     @see    #addMessageChangedListener
     */
    public synchronized void
    removeMessageChangedListener(MessageChangedListener l) { 
     if (messageChangedListeners != null
      messageChangedListeners.removeElement(l);
    }

    /**
     * Notify all MessageChangedListeners. Folder implementations are
     * expected to use this method to broadcast MessageChanged events. <p>
     *
     * The provided implementation queues the event into
     * an internal event queue. An event dispatcher thread dequeues
     * events from the queue and dispatches them to registered
     * MessageChangedListeners. Note that the event dispatching occurs
     * in a separate thread, thus avoiding potential deadlock problems.
     */
    protected void notifyMessageChangedListeners(int type, Message msg) {
  if (messageChangedListeners == null)
      return;
  
  MessageChangedEvent e = new MessageChangedEvent(this, type, msg);
  queueEvent(e, messageChangedListeners);
    }

    /*
     * The queue of events to be delivered.
     */
    private EventQueue q;

    /*
     * A lock for creating the EventQueue object.  Only one thread should
     * create an EventQueue for this folder.  We can't synchronize on the
     * folder's lock because that would violate the locking hierarchy in
     * some cases.  For details, see the IMAP provider.
     */
    private Object qLock = new Object();

    /*
     * Add the event and vector of listeners to the queue to be delivered.
     */
    private void queueEvent(MailEvent event, Vector vector) {
  // synchronize creation of the event queue
  synchronized (qLock) {
      if (q == null)
    q = new EventQueue();
  }

  /*
         * Copy the vector in order to freeze the state of the set
         * of EventListeners the event should be delivered to prior
         * to delivery.  This ensures that any changes made to the
         * Vector from a target listener's method during the delivery
         * of this event will not take effect until after the event is
         * delivered.
         */
  Vector v = (Vector)vector.clone();
  q.enqueue(event, v);
    }

    static class TerminatorEvent extends MailEvent {
  private static final long serialVersionUID = 3765761925441296565L;

  TerminatorEvent() {
      super(new Object());
  }

  public void dispatch(Object listener) {
      // Kill the event dispatching thread.
      Thread.currentThread().interrupt();
  }
    }

    // Dispatch the terminator
    private void terminateQueue() {
  synchronized (qLock) {
      if (q != null) {
    Vector dummyListeners = new Vector();
    dummyListeners.setSize(1)// need atleast one listener
    q.enqueue(new TerminatorEvent(), dummyListeners);
    q = null;
      }
  }
    }

    protected void finalize() throws Throwable {
  super.finalize();
  terminateQueue();
    }

    /**
     * override the default toString(), it will return the String
     * from Folder.getFullName() or if that is null, it will use
     * the default toString() behavior.
     */

    public String toString() {
  String s = getFullName();
  if (s != null)
      return s;
  else
      return super.toString();
    }
}