1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.mail;
  6. import java.io.*;
  7. import java.lang.*;
  8. import java.util.Vector;
  9. import java.util.StringTokenizer;
  10. import javax.mail.search.SearchTerm;
  11. import javax.mail.event.*;
  12. /**
  13. * Folder is an abstract class that represents a folder for mail
  14. * messages. Subclasses implement protocol specific Folders.<p>
  15. *
  16. * Folders can contain Messages, other Folders or both, thus providing
  17. * a tree-like hierarchy rooted at the Store's default folder. (Note
  18. * that some Folder implementations may not allow both Messages and
  19. * other Folders in the same Folder).<p>
  20. *
  21. * The interpretation of folder names is implementation dependent.
  22. * The different levels of hierarchy in a folder's full name
  23. * are separated from each other by the hierarchy delimiter
  24. * character.<p>
  25. *
  26. * The case-insensitive full folder name (that is, the full name
  27. * relative to the default folder for a Store) <strong>INBOX</strong>
  28. * is reserved to mean the "primary folder for this user on this
  29. * server". Not all Stores will provide an INBOX folder, and not
  30. * all users will have an INBOX folder at all times. The name
  31. * <strong>INBOX</strong> is reserved to refer to this folder,
  32. * when it exists, in Stores that provide it. <p>
  33. *
  34. * A Folder object obtained from a Store need not actually exist
  35. * in the backend store. The <code>exists()</code> method tests whether
  36. * the folder exists or not. The <code>create()</code> method
  37. * creates a Folder. <p>
  38. *
  39. * A Folder is initially in the closed state. Certain methods are valid
  40. * in this state; the documentation for those methods note this. A
  41. * Folder is opened by calling its 'open' method. All Folder methods,
  42. * except <code>open()</code>, <code>delete()</code> and
  43. * <code>renameTo()</code>, are valid in this state. <p>
  44. *
  45. * The only way to get a Folder is by invoking the
  46. * <code>getFolder()</code> method on Store or Folder, or by invoking
  47. * the <code>list()</code> or <code>listSubscribed()</code> methods
  48. * on Folder. Folder objects returned by the above methods are not
  49. * cached by the Store. Thus, invoking <code>getFolder(folder_name)</code>
  50. * on the same folder_name multiple times will return distinct Folder
  51. * objects. Likewise for list() and listSubscribed(). <p>
  52. *
  53. * The Message objects within the Folder are cached by the Folder.
  54. * Thus, invoking <code>getMessage(msgno)</code> on the same message number
  55. * multiple times will return the same Message object, until an
  56. * expunge is done on this Folder. <p>
  57. *
  58. * Note that a Message's message number can change within a
  59. * session if the containing Folder is expunged using the expunge
  60. * method. Clients that use message numbers as references to messages
  61. * should be aware of this and should be prepared to deal with
  62. * situation (probably by flushing out existing message number references
  63. * and reloading them). Because of this complexity, it is better for
  64. * clients to use Message objects as references to messages, rather than
  65. * message numbers. Expunged Message objects still have to be
  66. * pruned, but other Message objects in that folder are not affected by the
  67. * expunge.
  68. *
  69. * @author John Mani
  70. * @author Bill Shannon
  71. */
  72. public abstract class Folder {
  73. /**
  74. * The parent store.
  75. */
  76. protected Store store;
  77. /**
  78. * The open mode of this folder. The open mode is
  79. * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>,
  80. * or -1 if not known.
  81. * @since JavaMail 1.1
  82. */
  83. protected int mode = -1;
  84. /**
  85. * Constructor that takes a Store object.
  86. *
  87. * @param store the Store that holds this folder
  88. */
  89. protected Folder(Store store) {
  90. this.store = store;
  91. }
  92. /**
  93. * Returns the name of this Folder. <p>
  94. *
  95. * This method can be invoked on a closed Folder.
  96. *
  97. * @return name of the Folder
  98. */
  99. public abstract String getName();
  100. /**
  101. * Returns the full name of this Folder. If the folder resides under
  102. * the root hierarchy of this Store, the returned name is relative
  103. * to the root. Otherwise an absolute name, starting with the
  104. * hierarchy delimiter, is returned. <p>
  105. *
  106. * This method can be invoked on a closed Folder.
  107. *
  108. * @return full name of the Folder
  109. */
  110. public abstract String getFullName();
  111. /**
  112. * Return a URLName representing this folder. The returned URLName
  113. * does <em>not</em> include the password used to access the store.
  114. *
  115. * @return the URLName representing this folder
  116. * @see URLName
  117. * @since JavaMail 1.1
  118. */
  119. public URLName getURLName() throws MessagingException {
  120. URLName storeURL = getStore().getURLName();
  121. String fullname = getFullName();
  122. StringBuffer encodedName = new StringBuffer();
  123. char separator = getSeparator();
  124. if (fullname != null) {
  125. /*
  126. // We need to encode each of the folder's names.
  127. StringTokenizer tok = new StringTokenizer(
  128. fullname, new Character(separator).toString(), true);
  129. while (tok.hasMoreTokens()) {
  130. String s = tok.nextToken();
  131. if (s.charAt(0) == separator)
  132. encodedName.append(separator);
  133. else
  134. // XXX - should encode, but since there's no decoder...
  135. //encodedName.append(java.net.URLEncoder.encode(s));
  136. encodedName.append(s);
  137. }
  138. */
  139. // append the whole thing, until we can encode
  140. encodedName.append(fullname);
  141. }
  142. /*
  143. * Sure would be convenient if URLName had a
  144. * constructor that took a base URLName.
  145. */
  146. return new URLName(storeURL.getProtocol(), storeURL.getHost(),
  147. storeURL.getPort(), encodedName.toString(),
  148. storeURL.getUsername(),
  149. null /* no password */);
  150. }
  151. /**
  152. * Returns the Store that owns this Folder object.
  153. * This method can be invoked on a closed Folder.
  154. * @return the Store
  155. */
  156. public Store getStore() {
  157. return store;
  158. }
  159. /**
  160. * Returns the parent folder of this folder.
  161. * This method can be invoked on a closed Folder. If this folder
  162. * is the top of a folder hierarchy, this method returns null. <p>
  163. *
  164. * Note that since Folder objects are not cached, invoking this method
  165. * returns a new distinct Folder object.
  166. *
  167. * @return Parent folder
  168. */
  169. public abstract Folder getParent() throws MessagingException;
  170. /**
  171. * Tests if this folder physically exists on the Store.
  172. * This method can be invoked on a closed Folder.
  173. *
  174. * @return true if the folder exists, otherwise false
  175. * @see #create
  176. * @exception MessagingException typically if the connection
  177. * to the server is lost.
  178. */
  179. public abstract boolean exists() throws MessagingException;
  180. /**
  181. * Returns a list of Folders belonging to this Folder's namespace
  182. * that match the specified pattern. Patterns may contain the wildcard
  183. * characters <code>"%"</code>, which matches any character except hierarchy
  184. * delimiters, and <code>"*"</code>, which matches any character. <p>
  185. *
  186. * As an example, given the folder hierarchy: <pre>
  187. * Personal/
  188. * Finance/
  189. * Stocks
  190. * Bonus
  191. * StockOptions
  192. * Jokes
  193. * </pre>
  194. * <code>list("*")</code> on "Personal" will return the whole
  195. * hierarchy. <br>
  196. * <code>list("%")</code> on "Personal" will return "Finance" and
  197. * "Jokes". <br>
  198. * <code>list("Jokes")</code> on "Personal" will return "Jokes".<br>
  199. * <code>list("Stock*")</code> on "Finance" will return "Stocks"
  200. * and "StockOptions". <p>
  201. *
  202. * Folder objects are not cached by the Store, so invoking this
  203. * method on the same pattern multiple times will return that many
  204. * distinct Folder objects. <p>
  205. *
  206. * This method can be invoked on a closed Folder.
  207. *
  208. * @param pattern the match pattern
  209. * @return array of matching Folder objects. An empty
  210. * array is returned if no matching Folders exist.
  211. * @see #listSubscribed
  212. * @exception FolderNotFoundException if this folder does
  213. * not exist.
  214. * @exception MessagingException
  215. */
  216. public abstract Folder[] list(String pattern) throws MessagingException;
  217. /**
  218. * Returns a list of subscribed Folders belonging to this Folder's
  219. * namespace that match the specified pattern. If the folder does
  220. * not support subscription, this method should resolve to
  221. * <code>list</code>.
  222. * (The default implementation provided here, does just this).
  223. * The pattern can contain wildcards as for <code>list</code>. <p>
  224. *
  225. * Folder objects are not cached by the Store, so invoking this
  226. * method on the same pattern multiple times will return that many
  227. * distinct Folder objects. <p>
  228. *
  229. * This method can be invoked on a closed Folder.
  230. *
  231. * @param pattern the match pattern
  232. * @return array of matching subscribed Folder objects. An
  233. * empty array is returned of no matching
  234. * subscribed folders exist.
  235. * @see #list
  236. * @exception FolderNotFoundException if this folder does
  237. * not exist.
  238. * @exception MessagingException
  239. */
  240. public Folder[] listSubscribed(String pattern) throws MessagingException {
  241. return list(pattern);
  242. }
  243. /**
  244. * Convenience method that returns the list of folders under this
  245. * Folder. This method just calls the <code>list(String pattern)</code>
  246. * method with <code>"%"</code> as the match pattern. This method can
  247. * be invoked on a closed Folder.
  248. *
  249. * @return array of Folder objects under this Folder. An
  250. * empty array is returned if no subfolders exist.
  251. * @see #list
  252. * @exception FolderNotFoundException if this folder does
  253. * not exist.
  254. * @exception MessagingException
  255. */
  256. public Folder[] list() throws MessagingException {
  257. return list("%");
  258. }
  259. /**
  260. * Convenience method that returns the list of subscribed folders
  261. * under this Folder. This method just calls the
  262. * <code>listSubscribed(String pattern)</code> method with <code>"%"</code>
  263. * as the match pattern. This method can be invoked on a closed Folder.
  264. *
  265. * @return array of subscribed Folder objects under this
  266. * Folder. An empty array is returned if no subscribed
  267. * subfolders exist.
  268. * @see #listSubscribed
  269. * @exception FolderNotFoundException if this folder does
  270. * not exist.
  271. * @exception MessagingException
  272. */
  273. public Folder[] listSubscribed() throws MessagingException {
  274. return listSubscribed("%");
  275. }
  276. /**
  277. * Return the delimiter character that separates this Folder's pathname
  278. * from the names of immediate subfolders. This method can be invoked
  279. * on a closed Folder.
  280. *
  281. * @exception FolderNotFoundException if this folder does
  282. * not exist
  283. * @return Hierarchy separator character
  284. */
  285. public abstract char getSeparator() throws MessagingException;
  286. /**
  287. * This folder can contain messages
  288. */
  289. public final static int HOLDS_MESSAGES = 0x01;
  290. /**
  291. * This folder can contain other folders
  292. */
  293. public final static int HOLDS_FOLDERS = 0x02;
  294. /**
  295. * Returns the type of this Folder, that is, whether this folder can hold
  296. * messages or subfolders or both. The returned value is an integer
  297. * bitfield with the appropriate bits set. This method can be invoked
  298. * on a closed folder.
  299. *
  300. * @return integer with appropriate bits set
  301. * @exception FolderNotFoundException if this folder does
  302. * not exist.
  303. * @see #HOLDS_FOLDERS
  304. * @see #HOLDS_MESSAGES
  305. */
  306. public abstract int getType() throws MessagingException;
  307. /**
  308. * Create this folder on the Store. When this folder is created, any
  309. * folders in its path that do not exist are also created. <p>
  310. *
  311. * If the creation is successful, a CREATED FolderEvent is delivered
  312. * to any FolderListeners registered on this Folder and this Store.
  313. *
  314. * @param type The type of this folder.
  315. *
  316. * @return true if the creation succeeds, else false.
  317. * @exception MessagingException
  318. * @see #HOLDS_FOLDERS
  319. * @see #HOLDS_MESSAGES
  320. * @see javax.mail.event.FolderEvent
  321. */
  322. public abstract boolean create(int type) throws MessagingException;
  323. /**
  324. * Returns true if this Folder is subscribed. <p>
  325. *
  326. * This method can be invoked on a closed Folder. <p>
  327. *
  328. * The default implementation provided here just returns true.
  329. *
  330. * @return true if this Folder is subscribed
  331. */
  332. public boolean isSubscribed() {
  333. return true;
  334. }
  335. /**
  336. * Subscribe or unsubscribe this Folder. Not all Stores support
  337. * subscription. <p>
  338. *
  339. * This method can be invoked on a closed Folder. <p>
  340. *
  341. * The implementation provided here just throws the
  342. * MethodNotSupportedException.
  343. *
  344. * @param subscribe true to subscribe, false to unsubscribe
  345. * @exception FolderNotFoundException if this folder does
  346. * not exist.
  347. * @exception MethodNotSupportedException if this store
  348. * does not support subscription
  349. * @exception MessagingException
  350. */
  351. public void setSubscribed(boolean subscribe)
  352. throws MessagingException {
  353. throw new MethodNotSupportedException();
  354. }
  355. /**
  356. * Returns true if this Folder has new messages since the last time
  357. * this indication was reset. When this indication is set or reset
  358. * depends on the Folder implementation (and in the case of IMAP,
  359. * depends on the server). This method can be used to implement
  360. * a lightweight "check for new mail" operation on a Folder without
  361. * opening it. (For example, a thread that monitors a mailbox and
  362. * flags when it has new mail.) This method should indicate whether
  363. * any messages in the Folder have the <code>RECENT</code> flag set. <p>
  364. *
  365. * Note that this is not an incremental check for new mail, i.e.,
  366. * it cannot be used to determine whether any new messages have
  367. * arrived since the last time this method was invoked. To
  368. * implement incremental checks, the Folder needs to be opened. <p>
  369. *
  370. * This method can be invoked on a closed Folder that can contain
  371. * Messages.
  372. *
  373. * @return true if the Store has new Messages
  374. * @exception FolderNotFoundException if this folder does
  375. * not exist.
  376. * @exception MessagingException
  377. */
  378. public abstract boolean hasNewMessages() throws MessagingException;
  379. /**
  380. * Return the Folder object corresponding to the given name. Note that
  381. * this folder does not physically have to exist in the Store. The
  382. * <code>exists()</code> method on a Folder indicates whether it really
  383. * exists on the Store. <p>
  384. *
  385. * In some Stores, name can be an absolute path if it starts with the
  386. * hierarchy delimiter. Otherwise, it is interpreted relative to
  387. * this Folder. <p>
  388. *
  389. * Folder objects are not cached by the Store, so invoking this
  390. * method on the same name multiple times will return that many
  391. * distinct Folder objects. <p>
  392. *
  393. * This method can be invoked on a closed Folder.
  394. *
  395. * @param name name of the Folder
  396. * @return Folder object
  397. * @exception MessagingException
  398. */
  399. public abstract Folder getFolder(String name)
  400. throws MessagingException;
  401. /**
  402. * Delete this Folder. This method will succeed only on a closed
  403. * Folder. <p>
  404. *
  405. * The <code>recurse</code> flag controls whether the deletion affects
  406. * subfolders or not. If true, all subfolders are deleted, then this
  407. * folder itself is deleted. If false, the behaviour is dependent on
  408. * the folder type and is elaborated below: <p>
  409. *
  410. * <ul>
  411. * <li>
  412. * The folder can contain only messages: (type == HOLDS_MESSAGES).
  413. * <br>
  414. * All messages within the folder are removed. The folder
  415. * itself is then removed. An appropriate FolderEvent is generated by
  416. * the Store and this folder. <p>
  417. *
  418. * <li>
  419. * The folder can contain only subfolders: (type == HOLDS_FOLDERS).
  420. * <br>
  421. * If this folder is empty (does not contain any
  422. * subfolders at all), it is removed. An appropriate FolderEvent is
  423. * generated by the Store and this folder.<br>
  424. * If this folder contains any subfolders, the delete fails
  425. * and returns false. <p>
  426. *
  427. * <li>
  428. * The folder can contain subfolders as well as messages: <br>
  429. * If the folder is empty (no messages or subfolders), it
  430. * is removed. If the folder contains no subfolders, but only messages,
  431. * then all messages are removed. The folder itself is then removed.
  432. * In both the above cases, an appropriate FolderEvent is
  433. * generated by the Store and this folder. <p>
  434. *
  435. * If the folder contains subfolders there are 3 possible
  436. * choices an implementation is free to do: <p>
  437. *
  438. * <ol>
  439. * <li> The operation fails, irrespective of whether this folder
  440. * contains messages or not. Some implementations might elect to go
  441. * with this simple approach. The delete() method returns false.
  442. *
  443. * <li> Any messages within the folder are removed. Subfolders
  444. * are not removed. The folder itself is not removed or affected
  445. * in any manner. The delete() method returns true. And the
  446. * exists() method on this folder will return true indicating that
  447. * this folder still exists. <br>
  448. * An appropriate FolderEvent is generated by the Store and this folder.
  449. *
  450. * <li> Any messages within the folder are removed. Subfolders are
  451. * not removed. The folder itself changes its type from
  452. * HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new
  453. * messages cannot be added to this folder, but new subfolders can
  454. * be created underneath. The delete() method returns true indicating
  455. * success. The exists() method on this folder will return true
  456. * indicating that this folder still exists. <br>
  457. * An appropriate FolderEvent is generated by the Store and this folder.
  458. * </ol>
  459. * </ul>
  460. *
  461. * @return true if the Folder is deleted successfully
  462. * @exception FolderNotFoundException if this folder does
  463. * not exist
  464. * @exception IllegalStateException if this folder is not in
  465. * the closed state.
  466. * @exception MessagingException
  467. * @see javax.mail.event.FolderEvent
  468. */
  469. public abstract boolean delete(boolean recurse)
  470. throws MessagingException;
  471. /**
  472. * Rename this Folder. This method will succeed only on a closed
  473. * Folder. <p>
  474. *
  475. * If the rename is successful, a RENAMED FolderEvent is delivered
  476. * to FolderListeners registered on this folder and its containing
  477. * Store.
  478. *
  479. * @param f a folder representing the new name for this Folder
  480. * @return true if the Folder is renamed successfully
  481. * @exception FolderNotFoundException if this folder does
  482. * not exist
  483. * @exception IllegalStateException if this folder is not in
  484. * the closed state.
  485. * @exception MessagingException
  486. * @see javax.mail.event.FolderEvent
  487. */
  488. public abstract boolean renameTo(Folder f) throws MessagingException;
  489. /**
  490. * The Folder is read only. The state and contents of this
  491. * folder cannot be modified.
  492. */
  493. public static final int READ_ONLY = 1;
  494. /**
  495. * The state and contents of this folder can be modified.
  496. */
  497. public static final int READ_WRITE = 2;
  498. /**
  499. * Open this Folder. This method is valid only on Folders that
  500. * can contain Messages and that are closed. <p>
  501. *
  502. * If this folder is opened successfully, an OPENED ConnectionEvent
  503. * is delivered to any ConnectionListeners registered on this
  504. * Folder. <p>
  505. *
  506. * The effect of opening multiple connections to the same folder
  507. * on a specifc Store is implementation dependent. Some implementations
  508. * allow multiple readers, but only one writer. Others allow
  509. * multiple writers as well as readers.
  510. *
  511. * @param mode open the Folder READ_ONLY or READ_WRITE
  512. * @exception FolderNotFoundException if this folder does
  513. * not exist.
  514. * @exception IllegalStateException if this folder is not in
  515. * the closed state.
  516. * @exception MessagingException
  517. * @see #READ_ONLY
  518. * @see #READ_WRITE
  519. * @see #getType()
  520. * @see javax.mail.event.ConnectionEvent
  521. */
  522. public abstract void open(int mode) throws MessagingException;
  523. /**
  524. * Close this Folder. This method is valid only on open Folders. <p>
  525. *
  526. * A CLOSED ConnectionEvent is delivered to any ConnectionListeners
  527. * registered on this Folder. Note that the folder is closed even
  528. * if this method terminates abnormally by throwing a
  529. * MessagingException.
  530. *
  531. * @param expunge expunges all deleted messages if this flag is true
  532. * @exception IllegalStateException if this folder is not opened
  533. * @exception MessagingException
  534. * @see javax.mail.event.ConnectionEvent
  535. */
  536. public abstract void close(boolean expunge) throws MessagingException;
  537. /**
  538. * Indicates whether this Folder is in the 'open' state.
  539. * @return true if this Folder is in the 'open' state.
  540. */
  541. public abstract boolean isOpen();
  542. /**
  543. * Return the open mode of this folder. Returns
  544. * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>,
  545. * or -1 if the open mode is not known (usually only because an older
  546. * <code>Folder</code> provider has not been updated to use this new
  547. * method).
  548. *
  549. * @exception IllegalStateException if this folder is not opened
  550. * @return the open mode of this folder
  551. * @since JavaMail 1.1
  552. */
  553. public int getMode() {
  554. if (!isOpen())
  555. throw new IllegalStateException("Folder not open");
  556. return mode;
  557. }
  558. /**
  559. * Get the permanent flags supported by this Folder. Returns a Flags
  560. * object that contains all the flags supported. <p>
  561. *
  562. * The special flag <code>Flags.USER </code> indicates that this Folder
  563. * supports arbitrary user-defined flags. <p>
  564. *
  565. * The supported permanent flags for a folder may not be available
  566. * until the folder is opened.
  567. *
  568. * @return permanent flags, or null if not known
  569. */
  570. public abstract Flags getPermanentFlags();
  571. /**
  572. * Get total number of messages in this Folder. <p>
  573. *
  574. * This method can be invoked on a closed folder. However, note
  575. * that for some folder implementations, getting the total message
  576. * count can be an expensive operation involving actually opening
  577. * the folder. In such cases, a provider can choose not to support
  578. * this functionality in the closed state, in which case this method
  579. * must return -1. <p>
  580. *
  581. * Clients invoking this method on a closed folder must be aware
  582. * that this is a potentially expensive operation. Clients must
  583. * also be prepared to handle a return value of -1 in this case.
  584. *
  585. * @return total number of messages. -1 may be returned
  586. * by certain implementations if this method is
  587. * invoked on a closed folder.
  588. * @exception FolderNotFoundException if this folder does
  589. * not exist.
  590. * @exception MessagingException
  591. */
  592. public abstract int getMessageCount() throws MessagingException;
  593. /**
  594. * Get the number of new messages in this Folder. <p>
  595. *
  596. * This method can be invoked on a closed folder. However, note
  597. * that for some folder implementations, getting the new message
  598. * count can be an expensive operation involving actually opening
  599. * the folder. In such cases, a provider can choose not to support
  600. * this functionality in the closed state, in which case this method
  601. * must return -1. <p>
  602. *
  603. * Clients invoking this method on a closed folder must be aware
  604. * that this is a potentially expensive operation. Clients must
  605. * also be prepared to handle a return value of -1 in this case. <p>
  606. *
  607. * This implementation returns -1 if this folder is closed. Else
  608. * this implementation gets each Message in the folder using
  609. * <code>getMessage(int)</code> and checks whether its
  610. * <code>RECENT</code> flag is set. The total number of messages
  611. * that have this flag set is returned.
  612. *
  613. * @return number of new messages. -1 may be returned
  614. * by certain implementations if this method is
  615. * invoked on a closed folder.
  616. * @exception FolderNotFoundException if this folder does
  617. * not exist.
  618. * @exception MessagingException
  619. */
  620. public synchronized int getNewMessageCount()
  621. throws MessagingException {
  622. if (!isOpen())
  623. return -1;
  624. int newmsgs = 0;
  625. int total = getMessageCount();
  626. for (int i = 1; i <= total; i++) {
  627. try {
  628. if (getMessage(i).isSet(Flags.Flag.RECENT))
  629. newmsgs++;
  630. } catch (MessageRemovedException me) {
  631. // This is an expunged message, ignore it.
  632. continue;
  633. }
  634. }
  635. return newmsgs;
  636. }
  637. /**
  638. * Get the total number of unread messages in this Folder. <p>
  639. *
  640. * This method can be invoked on a closed folder. However, note
  641. * that for some folder implementations, getting the unread message
  642. * count can be an expensive operation involving actually opening
  643. * the folder. In such cases, a provider can choose not to support
  644. * this functionality in the closed state, in which case this method
  645. * must return -1. <p>
  646. *
  647. * Clients invoking this method on a closed folder must be aware
  648. * that this is a potentially expensive operation. Clients must
  649. * also be prepared to handle a return value of -1 in this case. <p>
  650. *
  651. * This implementation returns -1 if this folder is closed. Else
  652. * this implementation gets each Message in the folder using
  653. * <code>getMessage(int)</code> and checks whether its
  654. * <code>SEEN</code> flag is set. The total number of messages
  655. * that do not have this flag set is returned.
  656. *
  657. * @return total number of unread messages. -1 may be returned
  658. * by certain implementations if this method is
  659. * invoked on a closed folder.
  660. * @exception FolderNotFoundException if this folder does
  661. * not exist.
  662. * @exception MessagingException
  663. */
  664. public synchronized int getUnreadMessageCount()
  665. throws MessagingException {
  666. if (!isOpen())
  667. return -1;
  668. int unread = 0;
  669. int total = getMessageCount();
  670. for (int i = 1; i <= total; i++) {
  671. try {
  672. if (!getMessage(i).isSet(Flags.Flag.SEEN))
  673. unread++;
  674. } catch (MessageRemovedException me) {
  675. // This is an expunged message, ignore it.
  676. continue;
  677. }
  678. }
  679. return unread;
  680. }
  681. /**
  682. * Get the Message object corresponding to the given message
  683. * number. A Message object's message number is the relative
  684. * position of this Message in its Folder. Messages are numbered
  685. * starting at 1 through the total number of message in the folder.
  686. * Note that the message number for a particular Message can change
  687. * during a session if other messages in the Folder are deleted and
  688. * the Folder is expunged. <p>
  689. *
  690. * Message objects are light-weight references to the actual message
  691. * that get filled up on demand. Hence Folder implementations are
  692. * expected to provide light-weight Message objects. <p>
  693. *
  694. * Unlike Folder objects, repeated calls to getMessage with the
  695. * same message number will return the same Message object, as
  696. * long as no messages in this folder have been expunged. <p>
  697. *
  698. * Since message numbers can change within a session if the folder
  699. * is expunged , clients are advised not to use message numbers as
  700. * references to messages. Use Message objects instead.
  701. *
  702. * @param msgnum the message number
  703. * @return the Message object
  704. * @see #getMessageCount
  705. * @see #fetch
  706. * @exception FolderNotFoundException if this folder does
  707. * not exist.
  708. * @exception IllegalStateException if this folder is not opened
  709. * @exception IndexOutOfBoundsException if the message number
  710. * is out of range.
  711. * @exception MessagingException
  712. */
  713. public abstract Message getMessage(int msgnum)
  714. throws MessagingException;
  715. /**
  716. * Get the Message objects for message numbers ranging from start
  717. * through end, both start and end inclusive. Note that message
  718. * numbers start at 1, not 0. <p>
  719. *
  720. * Message objects are light-weight references to the actual message
  721. * that get filled up on demand. Hence Folder implementations are
  722. * expected to provide light-weight Message objects. <p>
  723. *
  724. * This implementation uses getMessage(index) to obtain the required
  725. * Message objects. Note that the returned array must contain
  726. * <code>(end-start+1)</code> Message objects.
  727. *
  728. * @param start the number of the first message
  729. * @param end the number of the last message
  730. * @return the Message objects
  731. * @see #fetch
  732. * @exception FolderNotFoundException if this folder does
  733. * not exist.
  734. * @exception IllegalStateException if this folder is not opened.
  735. * @exception IndexOutOfBoundsException if the start or end
  736. * message numbers are out of range.
  737. * @exception MessagingException
  738. */
  739. public synchronized Message[] getMessages(int start, int end)
  740. throws MessagingException {
  741. Message[] msgs = new Message[end - start +1];
  742. for (int i = start; i <= end; i++)
  743. msgs[i - start] = getMessage(i);
  744. return msgs;
  745. }
  746. /**
  747. * Get the Message objects for message numbers specified in
  748. * the array. <p>
  749. *
  750. * Message objects are light-weight references to the actual message
  751. * that get filled up on demand. Hence Folder implementations are
  752. * expected to provide light-weight Message objects. <p>
  753. *
  754. * This implementation uses getMessage(index) to obtain the required
  755. * Message objects. Note that the returned array must contain
  756. * <code>msgnums.length</code> Message objects
  757. *
  758. * @param msgnums the array of message numbers
  759. * @return the array of Message objects.
  760. * @see #fetch
  761. * @exception FolderNotFoundException if this folder does
  762. * not exist.
  763. * @exception IllegalStateException if this folder is not opened.
  764. * @exception IndexOutOfBoundsException if any message number
  765. * in the given array is out of range.
  766. * @exception MessagingException
  767. */
  768. public synchronized Message[] getMessages(int[] msgnums)
  769. throws MessagingException {
  770. int len = msgnums.length;
  771. Message[] msgs = new Message[len];
  772. for (int i = 0; i < len; i++)
  773. msgs[i] = getMessage(msgnums[i]);
  774. return msgs;
  775. }
  776. /**
  777. * Get all Message objects from this Folder. Returns an empty array
  778. * if the folder is empty.
  779. *
  780. * Clients can use Message objects (instead of sequence numbers)
  781. * as references to the messages within a folder; this method supplies
  782. * the Message objects to the client. Folder implementations are
  783. * expected to provide light-weight Message objects, which get
  784. * filled on demand. <p>
  785. *
  786. * This implementation invokes <code>getMessageCount()</code> to get
  787. * the current message count and then uses <code>getMessage()</code>
  788. * to get Message objects from 1 till the message count.
  789. *
  790. * @return array of Message objects, empty array if folder
  791. * is empty.
  792. * @see #fetch
  793. * @exception FolderNotFoundException if this folder does
  794. * not exist.
  795. * @exception IllegalStateException if this folder is not opened.
  796. * @exception MessagingException
  797. */
  798. public synchronized Message[] getMessages() throws MessagingException {
  799. if (!isOpen()) // otherwise getMessageCount might return -1
  800. throw new IllegalStateException("Folder not open");
  801. int total = getMessageCount();
  802. Message[] msgs = new Message[total];
  803. for (int i = 1; i <= total; i++)
  804. msgs[i-1] = getMessage(i);
  805. return msgs;
  806. }
  807. /**
  808. * Append given Messages to this folder. This method can be
  809. * invoked on a closed Folder. An appropriate MessageCountEvent
  810. * is delivered to any MessageCountListener registered on this
  811. * folder when the messages arrive in the folder. <p>
  812. *
  813. * Folder implementations must not abort this operation if a
  814. * Message in the given message array turns out to be an
  815. * expunged Message.
  816. *
  817. * @param msgs array of Messages to be appended
  818. * @exception FolderNotFoundException if this folder does
  819. * not exist.
  820. * @exception MessagingException if the append failed.
  821. */
  822. public abstract void appendMessages(Message[] msgs)
  823. throws MessagingException;
  824. /**
  825. * Prefetch the items specified in the FetchProfile for the
  826. * given Messages. <p>
  827. *
  828. * Clients use this method to indicate that the specified items are
  829. * needed en-masse for the given message range. Implementations are
  830. * expected to retrieve these items for the given message range in
  831. * a efficient manner. Note that this method is just a hint to the
  832. * implementation to prefetch the desired items. <p>
  833. *
  834. * An example is a client filling its header-view window with
  835. * the Subject, From and X-mailer headers for all messages in the
  836. * folder.<p>
  837. * <blockquote><pre>
  838. *
  839. * Message[] msgs = folder.getMessages();
  840. *
  841. * FetchProfile fp = new FetchProfile();
  842. * fp.add(FetchProfile.Item.ENVELOPE);
  843. * fp.add("X-mailer");
  844. * folder.fetch(msgs, fp);
  845. *
  846. * for (int i = 0; i < folder.getMessageCount(); i++) {
  847. * display(msg[i].getFrom());
  848. * display(msg[i].getSubject());
  849. * display(msg[i].getHeader("X-mailer"));
  850. * }
  851. *
  852. * </pre></blockquote><p>
  853. *
  854. * The implementation provided here just returns without
  855. * doing anything useful. Providers wanting to provide a real
  856. * implementation for this method should override this method.
  857. *
  858. * @param msgs fetch items for these messages
  859. * @param fp the FetchProfile
  860. * @exception IllegalStateException if this folder is not opened
  861. * @exception MessagingException.
  862. */
  863. public void fetch(Message[] msgs, FetchProfile fp)
  864. throws MessagingException {
  865. return;
  866. }
  867. /**
  868. * Set the specified flags on the messages specified in the array.
  869. * This will result in appropriate MessageChangedEvents being
  870. * delivered to any MessageChangedListener registered on this
  871. * Message's containing folder. <p>
  872. *
  873. * Note that the specified Message objects <strong>must</strong>
  874. * belong to this folder. Certain Folder implementations can
  875. * optimize the operation of setting Flags for a group of messages,
  876. * so clients might want to use this method, rather than invoking
  877. * <code>Message.setFlags</code> for each Message. <p>
  878. *
  879. * This implementation degenerates to invoking <code>setFlags()</code>
  880. * on each Message object. Specific Folder implementations that can
  881. * optimize this case should do so.
  882. * Also, an implementation must not abort the operation if a Message
  883. * in the array turns out to be an expunged Message.
  884. *
  885. * @param msgnums the array of message objects
  886. * @param flag Flags object containing the flags to be set
  887. * @param value set the flags to this boolean value
  888. * @exception IllegalStateException if this folder is not opened
  889. * or if it has been opened READ_ONLY.
  890. * @exception MessagingException
  891. * @see Message#setFlags
  892. * @see javax.mail.event.MessageChangedEvent
  893. */
  894. public synchronized void setFlags(Message[] msgs,
  895. Flags flag, boolean value) throws MessagingException {
  896. for (int i = 0; i < msgs.length; i++) {
  897. try {
  898. msgs[i].setFlags(flag, value);
  899. } catch (MessageRemovedException me) {
  900. // This message is expunged, skip
  901. }
  902. }
  903. }
  904. /**
  905. * Set the specified flags on the messages numbered from start
  906. * through end, both start and end inclusive. Note that message
  907. * numbers start at 1, not 0.
  908. * This will result in appropriate MessageChangedEvents being
  909. * delivered to any MessageChangedListener registered on this
  910. * Message's containing folder. <p>
  911. *
  912. * Certain Folder implementations can
  913. * optimize the operation of setting Flags for a group of messages,
  914. * so clients might want to use this method, rather than invoking
  915. * <code>Message.setFlags</code> for each Message. <p>
  916. *
  917. * The default implementation uses <code>getMessage(int)</code> to
  918. * get each <code>Message</code> object and then invokes
  919. * <code>setFlags</code> on that object to set the flags.
  920. * Specific Folder implementations that can optimize this case should do so.
  921. * Also, an implementation must not abort the operation if a message
  922. * number refers to an expunged message.
  923. *
  924. * @param start the number of the first message
  925. * @param end the number of the last message
  926. * @param flag Flags object containing the flags to be set
  927. * @param value set the flags to this boolean value
  928. * @exception IllegalStateException if this folder is not opened
  929. * or if it has been opened READ_ONLY.
  930. * @exception IndexOutOfBoundsException if the start or end
  931. * message numbers are out of range.
  932. * @exception MessagingException
  933. * @see Message#setFlags
  934. * @see javax.mail.event.MessageChangedEvent
  935. */
  936. public synchronized void setFlags(int start, int end,
  937. Flags flag, boolean value) throws MessagingException {
  938. for (int i = start; i <= end; i++) {
  939. try {
  940. Message msg = getMessage(i);
  941. msg.setFlags(flag, value);
  942. } catch (MessageRemovedException me) {
  943. // This message is expunged, skip
  944. }
  945. }
  946. }
  947. /**
  948. * Set the specified flags on the messages whose message numbers
  949. * are in the array.
  950. * This will result in appropriate MessageChangedEvents being
  951. * delivered to any MessageChangedListener registered on this
  952. * Message's containing folder. <p>
  953. *
  954. * Certain Folder implementations can
  955. * optimize the operation of setting Flags for a group of messages,
  956. * so clients might want to use this method, rather than invoking
  957. * <code>Message.setFlags</code> for each Message. <p>
  958. *
  959. * The default implementation uses <code>getMessage(int)</code> to
  960. * get each <code>Message</code> object and then invokes
  961. * <code>setFlags</code> on that object to set the flags.
  962. * Specific Folder implementations that can optimize this case should do so.
  963. * Also, an implementation must not abort the operation if a message
  964. * number refers to an expunged message.
  965. *
  966. * @param msgnums the array of message numbers
  967. * @param flag Flags object containing the flags to be set
  968. * @param value set the flags to this boolean value
  969. * @exception IllegalStateException if this folder is not opened
  970. * or if it has been opened READ_ONLY.
  971. * @exception IndexOutOfBoundsException if any message number
  972. * in the given array is out of range.
  973. * @exception MessagingException
  974. * @see Message#setFlags
  975. * @see javax.mail.event.MessageChangedEvent
  976. */
  977. public synchronized void setFlags(int[] msgnums,
  978. Flags flag, boolean value) throws MessagingException {
  979. for (int i = 0; i < msgnums.length; i++) {
  980. try {
  981. Message msg = getMessage(msgnums[i]);
  982. msg.setFlags(flag, value);
  983. } catch (MessageRemovedException me) {
  984. // This message is expunged, skip
  985. }
  986. }
  987. }
  988. /**
  989. * Copy the specified Messages from this Folder into another
  990. * Folder. This operation appends these Messages to the
  991. * destination Folder. The destination Folder does not have to
  992. * be opened. An appropriate MessageCountEvent
  993. * is delivered to any MessageCountListener registered on the
  994. * destination folder when the messages arrive in the folder. <p>
  995. *
  996. * Note that the specified Message objects <strong>must</strong>
  997. * belong to this folder. Folder implementations might be able
  998. * to optimize this method by doing server-side copies. <p>
  999. *
  1000. * This implementation just invokes <code>appendMessages()</code>
  1001. * on the destination folder to append the given Messages. Specific
  1002. * folder implementations that support server-side copies should
  1003. * do so, if the destination folder's Store is the same as this
  1004. * folder's Store.
  1005. * Also, an implementation must not abort the operation if a
  1006. * Message in the array turns out to be an expunged Message.
  1007. *
  1008. * @param msgnums the array of message objects
  1009. * @param folder the folder to copy the messages to
  1010. * @exception FolderNotFoundException if the destination
  1011. * folder does not exist.
  1012. * @exception IllegalStateException if this folder is not opened.
  1013. * @exception MessagingException
  1014. * @see #appendMessages
  1015. */
  1016. public void copyMessages(Message[] msgs, Folder folder)
  1017. throws MessagingException {
  1018. if (!folder.exists())
  1019. throw new FolderNotFoundException(
  1020. folder.getFullName() + " does not exist",
  1021. folder);
  1022. folder.appendMessages(msgs);
  1023. }
  1024. /**
  1025. * Expunge (permanently remove) messages marked DELETED. Returns an
  1026. * array containing the expunged message objects. The
  1027. * <code>getMessageNumber</code> method
  1028. * on each of these message objects returns that Message's original
  1029. * (that is, prior to the expunge) sequence number. A MessageCountEvent
  1030. * containing the expunged messages is delivered to any
  1031. * MessageCountListeners registered on the folder. <p>
  1032. *
  1033. * Expunge causes the renumbering of Message objects subsequent to
  1034. * the expunged messages. Clients that use message numbers as
  1035. * references to messages should be aware of this and should be
  1036. * prepared to deal with the situation (probably by flushing out
  1037. * existing message number caches and reloading them). Because of
  1038. * this complexity, it is better for clients to use Message objects
  1039. * as references to messages, rather than message numbers. Any
  1040. * expunged Messages objects still have to be pruned, but other
  1041. * Messages in that folder are not affected by the expunge. <p>
  1042. *
  1043. * After a message is expunged, only the <code>isExpunged</code> and
  1044. * <code>getMessageNumber</code> methods are still valid on the
  1045. * corresponding Message object; other methods may throw
  1046. * <code>MessageRemovedException</code>
  1047. *
  1048. * @return array of expunged Message objects
  1049. * @exception FolderNotFoundException if this folder does not
  1050. * exist
  1051. * @exception IllegalStateException if this folder is not opened.
  1052. * @exception MessagingException
  1053. * @see Message#isExpunged
  1054. * @see javax.mail.event.MessageCountEvent
  1055. */
  1056. public abstract Message[] expunge() throws MessagingException;
  1057. /**
  1058. * Search this Folder for messages matching the specified
  1059. * search criterion. Returns an array containing the matching
  1060. * messages . Returns an empty array if no matches were found. <p>
  1061. *
  1062. * This implementation invokes
  1063. * <code>search(term, getMessages())</code>, to apply the search
  1064. * over all the messages in this folder. Providers that can implement
  1065. * server-side searching might want to override this method to provide
  1066. * a more efficient implementation.
  1067. *
  1068. * @param term the search criterion
  1069. * @return array of matching messages
  1070. * @exception javax.mail.search.SearchException if the search
  1071. * term is too complex for the implementation to handle.
  1072. * @exception FolderNotFoundException if this folder does
  1073. * not exist.
  1074. * @exception IllegalStateException if this folder is not opened.
  1075. * @exception MessagingException
  1076. * @see javax.mail.search.SearchTerm
  1077. */
  1078. public Message[] search(SearchTerm term) throws MessagingException {
  1079. return search(term, getMessages());
  1080. }
  1081. /**
  1082. * Search the given array of messages for those that match the
  1083. * specified search criterion. Returns an array containing the
  1084. * matching messages. Returns an empty array if no matches were
  1085. * found. <p>
  1086. *
  1087. * Note that the specified Message objects <strong>must</strong>
  1088. * belong to this folder. <p>
  1089. *
  1090. * This implementation iterates through the given array of messages,
  1091. * and applies the search criterion on each message by calling
  1092. * its <code>match()</code> method with the given term. The
  1093. * messages that succeed in the match are returned. Providers
  1094. * that can implement server-side searching might want to override
  1095. * this method to provide a more efficient implementation. If the
  1096. * search term is too complex or contains user-defined terms that
  1097. * cannot be executed on the server, providers may elect to either
  1098. * throw a SearchException or degenerate to client-side searching by
  1099. * calling <code>super.search()</code> to invoke this implementation.
  1100. *
  1101. * @param term the search criterion
  1102. * @param msgs the messages to be searched
  1103. * @return array of matching messages
  1104. * @exception javax.mail.search.SearchException if the search
  1105. * term is too complex for the implementation to handle.
  1106. * @exception IllegalStateException if this folder is not opened
  1107. * @exception MessagingException
  1108. * @see javax.mail.search.SearchTerm
  1109. */
  1110. public Message[] search(SearchTerm term, Message[] msgs)
  1111. throws MessagingException {
  1112. Vector matchedMsgs = new Vector();
  1113. // Run thru the given messages
  1114. for (int i = 0; i < msgs.length; i++) {
  1115. try {
  1116. if (msgs[i].match(term)) // matched
  1117. matchedMsgs.addElement(msgs[i]); // add it
  1118. } catch(MessageRemovedException mrex) { }
  1119. }
  1120. Message[] m = new Message[matchedMsgs.size()];
  1121. matchedMsgs.copyInto(m);
  1122. return m;
  1123. }
  1124. /*
  1125. * The set of listeners are stored in Vectors appropriate to their
  1126. * type. We mark all listener Vectors as "volatile" because, while
  1127. * we initialize them inside this folder's synchronization lock,
  1128. * they are accessed (checked for null) in the "notify" methods,
  1129. * which can't be synchronized due to lock ordering constraints.
  1130. * Since the listener fields (the handles on the Vector objects)
  1131. * are only ever set, and are never cleared, we believe this is
  1132. * safe. The code that dispatches the notifications will either
  1133. * see the null and assume there are no listeners or will see the
  1134. * Vector and will process the listeners. There's an inherent race
  1135. * between adding a listener and notifying the listeners; the lack
  1136. * of synchronization during notification does not make the race
  1137. * condition significantly worse. If one thread is setting a
  1138. * listener at the "same" time an event is being dispatched, the
  1139. * dispatch code might not see the listener right away. The
  1140. * dispatch code doesn't have to worry about the Vector handle
  1141. * being set to null, and thus using an out-of-date set of
  1142. * listeners, because we never set the field to null.
  1143. */
  1144. // Vector of connection listeners.
  1145. private volatile Vector connectionListeners = null;
  1146. /**
  1147. * Add a listener for Connection events on this Folder. <p>
  1148. *
  1149. * The implementation provided here adds this listener
  1150. * to an internal list of ConnectionListeners.
  1151. *
  1152. * @param l the Listener for Connection events
  1153. * @see javax.mail.event.ConnectionEvent
  1154. */
  1155. public synchronized void
  1156. addConnectionListener(ConnectionListener l) {
  1157. if (connectionListeners == null)
  1158. connectionListeners = new Vector();
  1159. connectionListeners.addElement(l);
  1160. }
  1161. /**
  1162. * Remove a Connection event listener. <p>
  1163. *
  1164. * The implementation provided here removes this listener
  1165. * from the internal list of ConnectionListeners.
  1166. *
  1167. * @param l the listener
  1168. * @see #addConnectionListener
  1169. */
  1170. public synchronized void
  1171. removeConnectionListener(ConnectionListener l) {
  1172. if (connectionListeners != null)
  1173. connectionListeners.removeElement(l);
  1174. }
  1175. /**
  1176. * Notify all ConnectionListeners. Folder implementations are
  1177. * expected to use this method to broadcast connection events. <p>
  1178. *
  1179. * The provided implementation queues the event into
  1180. * an internal event queue. An event dispatcher thread dequeues
  1181. * events from the queue and dispatches them to the registered
  1182. * ConnectionListeners. Note that the event dispatching occurs
  1183. * in a separate thread, thus avoiding potential deadlock problems.
  1184. *
  1185. * @param type the ConnectionEvent type
  1186. * @see javax.mail.event.ConnectionEvent
  1187. */
  1188. protected void notifyConnectionListeners(int type) {
  1189. if (connectionListeners != null) {
  1190. ConnectionEvent e = new ConnectionEvent(this, type);
  1191. queueEvent(e, connectionListeners);
  1192. }
  1193. /* Fix for broken JDK1.1.x Garbage collector :
  1194. * The 'conservative' GC in JDK1.1.x occasionally fails to
  1195. * garbage-collect Threads which are in the wait state.
  1196. * This would result in thread (and consequently memory) leaks.
  1197. *
  1198. * We attempt to fix this by sending a 'terminator' event
  1199. * to the queue, after we've sent the CLOSED event. The
  1200. * terminator event causes the event-dispatching thread to
  1201. * self destruct.
  1202. */
  1203. if (type == ConnectionEvent.CLOSED)
  1204. terminateQueue();
  1205. }
  1206. // Vector of folder listeners
  1207. private volatile Vector folderListeners = null;
  1208. /**
  1209. * Add a listener for Folder events on this Folder. <p>
  1210. *
  1211. * The implementation provided here adds this listener
  1212. * to an internal list of FolderListeners.
  1213. *
  1214. * @param l the Listener for Folder events
  1215. * @see javax.mail.event.FolderEvent
  1216. */
  1217. public synchronized void addFolderListener(FolderListener l) {
  1218. if (folderListeners == null)
  1219. folderListeners = new Vector();
  1220. folderListeners.addElement(l);
  1221. }
  1222. /**
  1223. * Remove a Folder event listener. <p>
  1224. *
  1225. * The implementation provided here removes this listener
  1226. * from the internal list of FolderListeners.
  1227. *
  1228. * @param l the listener
  1229. * @see #addFolderListener
  1230. */
  1231. public synchronized void removeFolderListener(FolderListener l) {
  1232. if (folderListeners != null)
  1233. folderListeners.removeElement(l);
  1234. }
  1235. /**
  1236. * Notify all FolderListeners registered on this Folder and
  1237. * this folder's Store. Folder implementations are expected
  1238. * to use this method to broadcast Folder events. <p>
  1239. *
  1240. * The implementation provided here queues the event into
  1241. * an internal event queue. An event dispatcher thread dequeues
  1242. * events from the queue and dispatches them to the
  1243. * FolderListeners registered on this folder. The implementation
  1244. * also invokes <code>notifyFolderListeners</code> on this folder's
  1245. * Store to notify any FolderListeners registered on the store.
  1246. *
  1247. * @param type type of FolderEvent
  1248. * @see #notifyFolderRenamedListeners
  1249. */
  1250. protected void notifyFolderListeners(int type) {
  1251. if (folderListeners != null) {
  1252. FolderEvent e = new FolderEvent(this, this, type);
  1253. queueEvent(e, folderListeners);
  1254. }
  1255. store.notifyFolderListeners(type, this);
  1256. }
  1257. /**
  1258. * Notify all FolderListeners registered on this Folder and
  1259. * this folder's Store about the renaming of this folder.
  1260. * Folder implementations are expected to use this method to
  1261. * broadcast Folder events indicating the renaming of folders. <p>
  1262. *
  1263. * The implementation provided here queues the event into
  1264. * an internal event queue. An event dispatcher thread dequeues
  1265. * events from the queue and dispatches them to the
  1266. * FolderListeners registered on this folder. The implementation
  1267. * also invokes <code>notifyFolderRenamedListeners</code> on this
  1268. * folder's Store to notify any FolderListeners registered on the store.
  1269. *
  1270. * @param folder Folder representing the new name.
  1271. * @see #notifyFolderListeners
  1272. * @since JavaMail 1.1
  1273. */
  1274. protected void notifyFolderRenamedListeners(Folder folder) {
  1275. if (folderListeners != null) {
  1276. FolderEvent e = new FolderEvent(this, this, folder,
  1277. FolderEvent.RENAMED);
  1278. queueEvent(e, folderListeners);
  1279. }
  1280. store.notifyFolderRenamedListeners(this, folder);
  1281. }
  1282. // Vector of MessageCount listeners
  1283. private volatile Vector messageCountListeners = null;
  1284. /**
  1285. * Add a listener for MessageCount events on this Folder. <p>
  1286. *
  1287. * The implementation provided here adds this listener
  1288. * to an internal list of MessageCountListeners.
  1289. *
  1290. * @param l the Listener for MessageCount events
  1291. * @see javax.mail.event.MessageCountEvent
  1292. */
  1293. public synchronized void addMessageCountListener(MessageCountListener l) {
  1294. if (messageCountListeners == null)
  1295. messageCountListeners = new Vector();
  1296. messageCountListeners.addElement(l);
  1297. }
  1298. /**
  1299. * Remove a MessageCount listener. <p>
  1300. *
  1301. * The implementation provided here removes this listener
  1302. * from the internal list of MessageCountListeners.
  1303. *
  1304. * @param l the listener
  1305. * @see #addMessageCountListener
  1306. */
  1307. public synchronized void
  1308. removeMessageCountListener(MessageCountListener l) {
  1309. if (messageCountListeners != null)
  1310. messageCountListeners.removeElement(l);
  1311. }
  1312. /**
  1313. * Notify all MessageCountListeners about the addition of messages
  1314. * into this folder. Folder implementations are expected to use this
  1315. * method to broadcast MessageCount events for indicating arrival of
  1316. * new messages. <p>
  1317. *
  1318. * The provided implementation queues the event into
  1319. * an internal event queue. An event dispatcher thread dequeues
  1320. * events from the queue and dispatches them to the registered
  1321. * MessageCountListeners. Note that the event dispatching occurs
  1322. * in a separate thread, thus avoiding potential deadlock problems.
  1323. */
  1324. protected void notifyMessageAddedListeners(Message[] msgs) {
  1325. if (messageCountListeners == null)
  1326. return;
  1327. MessageCountEvent e = new MessageCountEvent(
  1328. this,
  1329. MessageCountEvent.ADDED,
  1330. false,
  1331. msgs);
  1332. queueEvent(e, messageCountListeners);
  1333. }
  1334. /**
  1335. * Notify all MessageCountListeners about the removal of messages
  1336. * from this Folder. Folder implementations are expected to use this
  1337. * method to broadcast MessageCount events indicating removal of
  1338. * messages. <p>
  1339. *
  1340. * The provided implementation queues the event into
  1341. * an internal event queue. An event dispatcher thread dequeues
  1342. * events from the queue and dispatches them to the registered
  1343. * MessageCountListeners. Note that the event dispatching occurs
  1344. * in a separate thread, thus avoiding potential deadlock problems.
  1345. */
  1346. protected void notifyMessageRemovedListeners(boolean removed,
  1347. Message[] msgs) {
  1348. if (messageCountListeners == null)
  1349. return;
  1350. MessageCountEvent e = new MessageCountEvent(
  1351. this,
  1352. MessageCountEvent.REMOVED,
  1353. removed,
  1354. msgs);
  1355. queueEvent(e, messageCountListeners);
  1356. }
  1357. // Vector of MessageChanged listeners.
  1358. private volatile Vector messageChangedListeners = null;
  1359. /**
  1360. * Add a listener for MessageChanged events on this Folder. <p>
  1361. *
  1362. * The implementation provided here adds this listener
  1363. * to an internal list of MessageChangedListeners.
  1364. *
  1365. * @param l the Listener for MessageChanged events
  1366. * @see javax.mail.event.MessageChangedEvent
  1367. */
  1368. public synchronized void
  1369. addMessageChangedListener(MessageChangedListener l) {
  1370. if (messageChangedListeners == null)
  1371. messageChangedListeners = new Vector();
  1372. messageChangedListeners.addElement(l);
  1373. }
  1374. /**
  1375. * Remove a MessageChanged listener. <p>
  1376. *
  1377. * The implementation provided here removes this listener
  1378. * from the internal list of MessageChangedListeners.
  1379. *
  1380. * @param l the listener
  1381. * @see #addMessageChangedListener
  1382. */
  1383. public synchronized void
  1384. removeMessageChangedListener(MessageChangedListener l) {
  1385. if (messageChangedListeners != null)
  1386. messageChangedListeners.removeElement(l);
  1387. }
  1388. /**
  1389. * Notify all MessageChangedListeners. Folder implementations are
  1390. * expected to use this method to broadcast MessageChanged events. <p>
  1391. *
  1392. * The provided implementation queues the event into
  1393. * an internal event queue. An event dispatcher thread dequeues
  1394. * events from the queue and dispatches them to registered
  1395. * MessageChangedListeners. Note that the event dispatching occurs
  1396. * in a separate thread, thus avoiding potential deadlock problems.
  1397. */
  1398. protected void notifyMessageChangedListeners(int type, Message msg) {
  1399. if (messageChangedListeners == null)
  1400. return;
  1401. MessageChangedEvent e = new MessageChangedEvent(this, type, msg);
  1402. queueEvent(e, messageChangedListeners);
  1403. }
  1404. /*
  1405. * The queue of events to be delivered.
  1406. */
  1407. private EventQueue q;
  1408. /*
  1409. * A lock for creating the EventQueue object. Only one thread should
  1410. * create an EventQueue for this folder. We can't synchronize on the
  1411. * folder's lock because that would violate the locking hierarchy in
  1412. * some cases. For details, see the IMAP provider.
  1413. */
  1414. private Object qLock = new Object();
  1415. /*
  1416. * Add the event and vector of listeners to the queue to be delivered.
  1417. */
  1418. private void queueEvent(MailEvent event, Vector vector) {
  1419. // synchronize creation of the event queue
  1420. synchronized (qLock) {
  1421. if (q == null)
  1422. q = new EventQueue();
  1423. }
  1424. /*
  1425. * Copy the vector in order to freeze the state of the set
  1426. * of EventListeners the event should be delivered to prior
  1427. * to delivery. This ensures that any changes made to the
  1428. * Vector from a target listener's method during the delivery
  1429. * of this event will not take effect until after the event is
  1430. * delivered.
  1431. */
  1432. Vector v = (Vector)vector.clone();
  1433. q.enqueue(event, v);
  1434. }
  1435. // Dispatch the terminator
  1436. private void terminateQueue() {
  1437. synchronized (qLock) {
  1438. if (q != null) {
  1439. Vector dummyListeners = new Vector();
  1440. dummyListeners.setSize(1); // need atleast one listener
  1441. q.enqueue(
  1442. new MailEvent(new Object()) { // The Terminator Event
  1443. public void dispatch(Object listener) {
  1444. // Kill the event dispatching thread.
  1445. Thread.currentThread().interrupt();
  1446. }
  1447. },
  1448. dummyListeners
  1449. );
  1450. q = null;
  1451. }
  1452. }
  1453. }
  1454. protected void finalize() throws Throwable {
  1455. super.finalize();
  1456. terminateQueue();
  1457. }
  1458. /**
  1459. * override the default toString(), it will return the String
  1460. * from Folder.getFullName() or if that is null, it will use
  1461. * the default toString() behavior.
  1462. */
  1463. public String toString() {
  1464. String s = getFullName();
  1465. if (s != null)
  1466. return s;
  1467. else
  1468. return super.toString();
  1469. }
  1470. }