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