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