KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > mail > folder > AbstractLocalFolder


1 //The contents of this file are subject to the Mozilla Public License Version 1.1
2
//(the "License"); you may not use this file except in compliance with the
3
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
4
//
5
//Software distributed under the License is distributed on an "AS IS" basis,
6
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
7
//for the specific language governing rights and
8
//limitations under the License.
9
//
10
//The Original Code is "The Columba Project"
11
//
12
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
13
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
14
//
15
//All Rights Reserved.
16

17 package org.columba.mail.folder;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Arrays JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28
29 import org.columba.core.base.ListTools;
30 import org.columba.core.filter.FilterList;
31 import org.columba.core.io.DiskIO;
32 import org.columba.core.xml.XmlElement;
33 import org.columba.mail.config.FolderItem;
34 import org.columba.mail.folder.headercache.BerkeleyDBHeaderList;
35 import org.columba.mail.folder.headercache.CachedHeaderfields;
36 import org.columba.mail.folder.headercache.SyncHeaderList;
37 import org.columba.mail.folder.search.DefaultSearchEngine;
38 import org.columba.mail.message.ColumbaHeader;
39 import org.columba.mail.message.ColumbaMessage;
40 import org.columba.mail.message.IColumbaHeader;
41 import org.columba.mail.message.IColumbaMessage;
42 import org.columba.mail.message.IHeaderList;
43 import org.columba.mail.message.IPersistantHeaderList;
44 import org.columba.ristretto.io.Source;
45 import org.columba.ristretto.io.SourceInputStream;
46 import org.columba.ristretto.message.Attributes;
47 import org.columba.ristretto.message.Flags;
48 import org.columba.ristretto.message.Header;
49 import org.columba.ristretto.message.LocalMimePart;
50 import org.columba.ristretto.message.MimeTree;
51 import org.columba.ristretto.parser.HeaderParser;
52 import org.columba.ristretto.parser.MessageParser;
53 import org.columba.ristretto.parser.ParserException;
54
55 /**
56  * AbstractLocalFolder is a near-to working folder, which only needs a specific
57  * {@link IDataStorage},{@link DefaultSearchEngine}and
58  * {@link IHeaderListStorage}"plugged in" to make it work.
59  * <p>
60  * This class is abstract becaused, instead use {@link MHCachedFolder}a
61  * complete implementation.
62  * <p>
63  * AbstractLocalFolder uses an internal {@link ColumbaMessage}object as cache.
64  * This allows parsing of a message only once, while accessing the data of the
65  * message multiple times.
66  * <p>
67  * Attribute <code>nextMessageUid</code> handles the next unique message ID.
68  * When adding a new message to this folder, it gets this ID assigned for later
69  * reference. Then nextMessageUid is simply increased.
70  * <p>
71  *
72  * @see org.columba.mail.folder.mh.MHCachedFolder
73  *
74  * @author fdietz
75  */

76 public abstract class AbstractLocalFolder extends AbstractMessageFolder {
77
78     /** JDK 1.4+ logging framework logger, used for logging. */
79     private static final Logger JavaDoc LOG = Logger
80             .getLogger("org.columba.mail.folder");
81     
82     /**
83      * the next messag which gets added to this folder receives this unique ID
84      */

85     protected int nextMessageUid = -1;
86
87     /**
88      * we keep one message in cache in order to not needing to parse it twice
89      * times
90      */

91     protected ColumbaMessage aktMessage;
92
93     private boolean firstOpen = true;
94
95     /**
96      * implement your own mailbox format here
97      */

98
99     protected IDataStorage dataStorage;
100
101     protected IPersistantHeaderList headerList;
102
103     /**
104      * @param item
105      * <class>FolderItem </class> contains xml configuration of this
106      * folder
107      */

108     public AbstractLocalFolder(FolderItem item, String JavaDoc path) {
109         super(item, path);
110
111         // TODO (@author fdietz): move this to AbstractMessageFolder constructor
112
// create filterlist datastructure
113
XmlElement filterListElement = node.getElement(FilterList.XML_NAME);
114
115         if (filterListElement == null) {
116             // no filterlist treenode found
117
// -> create a new one
118
filterListElement = new XmlElement(FilterList.XML_NAME);
119             getConfiguration().getRoot().addElement(filterListElement);
120         }
121
122         filterList = new FilterList(filterListElement);
123
124         setSearchEngine(new DefaultSearchEngine(this));
125     }
126
127     /**
128      * @param name
129      * the name of the folder.
130      * @param type
131      * type of folder.
132      */

133     public AbstractLocalFolder(String JavaDoc name, String JavaDoc type, String JavaDoc path) {
134         super(name, type, path);
135
136         // create filterlist datastructure
137
XmlElement filterListElement = node.getElement(FilterList.XML_NAME);
138
139         if (filterListElement == null) {
140             // no filterlist treenode found
141
// -> create a new one
142
filterListElement = new XmlElement(FilterList.XML_NAME);
143             getConfiguration().getRoot().addElement(filterListElement);
144         }
145
146         filterList = new FilterList(filterListElement);
147
148         setSearchEngine(new DefaultSearchEngine(this));
149     }
150
151     /**
152      * Remove folder from tree
153      *
154      * @see org.columba.mail.folder.FolderTreeNode#removeFolder()
155      */

156     public void removeFolder() throws Exception JavaDoc {
157         // delete folder from your harddrive
158
boolean b = DiskIO.deleteDirectory(directoryFile);
159
160         // if this worked, remove it from tree.xml configuration, too
161
if (b) {
162             super.removeFolder();
163         }
164     }
165
166     /**
167      *
168      * Generate new unique message ID
169      *
170      * @return <class>Integer </class> containing UID
171      */

172     protected Object JavaDoc generateNextMessageUid() {
173         if (nextMessageUid == -1) {
174             try {
175                 if (getHeaderList().count() > 0) {
176                     List JavaDoc _headerList = new ArrayList JavaDoc();
177                     Object JavaDoc[] _uidList = headerList.getUids();
178                     for (int i = 0; i < _uidList.length; i++) {
179                         _headerList.add(_uidList[i]);
180                     }
181                     Integer JavaDoc maxUid = (Integer JavaDoc) Collections.max(_headerList);
182                     nextMessageUid = maxUid.intValue() + 1;
183                 } else {
184                     nextMessageUid = 0;
185                 }
186             } catch (Exception JavaDoc e) {
187                 nextMessageUid = 0;
188             }
189         }
190
191         return new Integer JavaDoc(nextMessageUid++);
192     }
193
194     /**
195      *
196      * Set next unique message ID
197      *
198      * @param next
199      * number of next message
200      */

201     public void setNextMessageUid(int next) {
202         nextMessageUid = next;
203     }
204
205     /**
206      *
207      * Implement a <class>IDataStorage </class> for the mailbox format of your
208      * pleasure.
209      *
210      * @return instance of <class>IDataStorage </class>
211      */

212     public abstract IDataStorage getDataStorageInstance();
213
214     /**
215      * @see org.columba.mail.folder.IMailbox#getMimePartTree(java.lang.Object)
216      */

217     public MimeTree getMimePartTree(Object JavaDoc uid) throws Exception JavaDoc {
218         // get message with UID
219
IColumbaMessage message = getMessage(uid);
220
221         // get tree-like structure of mimeparts
222
MimeTree mptree = message.getMimePartTree();
223
224         return mptree;
225     }
226
227     /** {@inheritDoc} */
228     public InputStream JavaDoc getMessageSourceStream(Object JavaDoc uid) throws Exception JavaDoc {
229         return getDataStorageInstance().getMessageStream(uid);
230     }
231
232     /** {@inheritDoc} */
233     public InputStream JavaDoc getMimePartBodyStream(Object JavaDoc uid, Integer JavaDoc[] address)
234             throws Exception JavaDoc {
235         // get message with UID
236
IColumbaMessage message = getMessage(uid);
237
238         // Get the mimepart
239
LocalMimePart mimepart = (LocalMimePart) message.getMimePartTree()
240                 .getFromAddress(address);
241
242         return mimepart.getInputStream();
243     }
244
245     /** {@inheritDoc} */
246     public InputStream JavaDoc getMimePartSourceStream(Object JavaDoc uid, Integer JavaDoc[] address)
247             throws Exception JavaDoc {
248         // get message with UID
249
IColumbaMessage message = getMessage(uid);
250
251         // Get the mimepart
252
LocalMimePart mimepart = (LocalMimePart) message.getMimePartTree()
253                 .getFromAddress(address);
254
255         return new SourceInputStream(mimepart.getSource());
256     }
257
258     /**
259      * Copies a set of messages from this folder to a destination folder.
260      * <p>
261      * First we copy the message source to the destination folder. Then we also
262      * copy the flags attribute of this message.
263      *
264      * @see org.columba.mail.folder.IMailbox#innerCopy(org.columba.mail.folder.IMailbox,
265      * java.lang.Object[])
266      */

267     public void innerCopy(IMailbox destFolder, Object JavaDoc[] uids) throws Exception JavaDoc {
268         if (getObservable() != null) {
269             getObservable().setMax(uids.length);
270         }
271
272         for (int i = 0; i < uids.length; i++) {
273             // skip this message, if it doesn't exist in source folder
274
if (!exists(uids[i])) {
275                 continue;
276             }
277
278             InputStream JavaDoc messageSourceStream = getMessageSourceStream(uids[i]);
279             destFolder.addMessage(messageSourceStream, getAttributes(uids[i]),
280                     getFlags(uids[i]));
281             messageSourceStream.close();
282
283             /*
284              * ((AbstractLocalFolder) destFolder).setFlags(destuid, (Flags)
285              * getFlags( uids[i]).clone());
286              */

287             // destFolder.fireMessageAdded(uids[i]);
288
if (getObservable() != null) {
289                 getObservable().setCurrent(i);
290             }
291         }
292
293         // we are done - clear the progress bar
294
if (getObservable() != null) {
295             getObservable().resetCurrent();
296         }
297     }
298
299     public Object JavaDoc addMessage(InputStream JavaDoc in) throws Exception JavaDoc {
300         return addMessage(in, null, null);
301     }
302
303     public Object JavaDoc addMessage(InputStream JavaDoc in, Attributes attributes, Flags flags)
304             throws Exception JavaDoc {
305         // generate UID for new message
306
Object JavaDoc newUid = generateNextMessageUid();
307
308         // save message stream to file
309
getDataStorageInstance().saveMessage(newUid, in);
310
311         // close stream
312
in.close();
313
314         // parse header
315
Source source = getDataStorageInstance().getMessageSource(newUid);
316         int messageSize = source.length();
317
318         Header header = HeaderParser.parse(source);
319         ColumbaHeader h;
320         if ((attributes != null) && (flags != null)) {
321             // save header and attributes. Copy the flags!
322
h = new ColumbaHeader(header, (Attributes) attributes.clone(),
323                     new Flags(flags.getFlags()));
324         } else {
325             h = new ColumbaHeader(header);
326             h.set("columba.size", new Integer JavaDoc(messageSize / 1024));
327         }
328         source.close();
329         h.set("columba.uid", newUid);
330         getHeaderList().add(h, newUid);
331
332         fireMessageAdded(newUid, getFlags(newUid));
333         return newUid;
334     }
335
336     /** {@inheritDoc} */
337     public boolean isInboxFolder() {
338         return getId().equals("101");
339     }
340
341     /** {@inheritDoc} */
342     public boolean isTrashFolder() {
343         return getId().equals("105");
344     }
345
346     /** {@inheritDoc} */
347     public boolean supportsAddFolder(String JavaDoc newFolderType) {
348         return (FolderFactory.getInstance().getGroup(newFolderType).equals(
349                 "local") || newFolderType.equals("VirtualFolder"));
350     }
351
352     /**
353      * Returns true since local folders can be moved.
354      *
355      * @return true.
356      */

357     public boolean supportsMove() {
358         return true;
359     }
360
361     /**
362      * @param uid
363      * @return
364      * @throws Exception
365      */

366     protected ColumbaMessage getMessage(Object JavaDoc uid) throws Exception JavaDoc {
367         // Check if the message is already cached
368
if (aktMessage != null) {
369             if (aktMessage.getUID().equals(uid)) {
370                 // this message is already cached
371
return aktMessage;
372             }
373         }
374
375         ColumbaMessage message;
376
377         try {
378
379             Source source = null;
380
381             source = getDataStorageInstance().getMessageSource(uid);
382
383             // Parse Message from DataStorage
384
try {
385                 message = new ColumbaMessage(MessageParser.parse(source));
386             } catch (ParserException e1) {
387                 LOG.fine(e1.getSource().toString());
388                 throw e1;
389             }
390             source.close();
391
392             message.setUID(uid);
393
394             aktMessage = message;
395
396             // TODO: fix parser exception
397
} catch (FolderInconsistentException e) {
398             super.removeMessage(uid);
399
400             throw e;
401         }
402
403         // We use the attributes and flags from the cache
404
// but the parsed header from the parsed message
405
IColumbaHeader header = getHeaderList().get(uid);
406         header.setHeader(message.getHeader().getHeader());
407         message.setHeader(header);
408
409         return message;
410     }
411
412     /**
413      * @see org.columba.mail.folder.IMailbox#getMessageHeader(java.lang.Object)
414      * @TODO dont use deprecated method
415      */

416     protected IColumbaHeader getMessageHeader(Object JavaDoc uid) throws Exception JavaDoc {
417         if ((aktMessage != null) && (aktMessage.getUID().equals(uid))) {
418             // message is already cached
419
// try to compare the headerfield count of
420
// the actually parsed message with the cached
421
// headerfield count
422
IColumbaMessage message = getMessage(uid);
423
424             // number of headerfields
425
int size = message.getHeader().count();
426
427             // get header from cache
428
IColumbaHeader h = getHeaderList().get(uid);
429
430             // message doesn't exist (this shouldn't happen here)
431
if (h == null) {
432                 return null;
433             }
434
435             // number of headerfields
436
int cachedSize = h.count();
437
438             // if header contains more fields than the cached header
439
if (size > cachedSize) {
440                 return (ColumbaHeader) message.getHeader();
441             }
442
443             return (ColumbaHeader) h;
444         } else {
445             // message isn't cached
446
// -> just return header from cache
447
return getHeaderList().get(uid);
448         }
449     }
450
451     /**
452      * @see org.columba.mail.folder.IMailbox#remove(java.lang.Object)
453      */

454     public void removeMessage(Object JavaDoc uid) throws Exception JavaDoc {
455         // remove message from disk
456
getDataStorageInstance().removeMessage(uid);
457
458         // fireMessageRemoved(uid, getFlags(uid));
459
super.removeMessage(uid);
460
461     }
462
463     /**
464      * Changes the selected message flags and updates the {@link MailFolderInfo}
465      * accordingly.
466      * <p>
467      * This method is only used for innerCopy().
468      *
469      * @param uid
470      * selected message UID
471      * @param flags
472      * new flags
473      * @throws Exception
474      */

475     protected void setFlags(Object JavaDoc uid, Flags flags) throws Exception JavaDoc {
476         IColumbaHeader h = getHeaderList().get(uid);
477
478         Flags oldFlags = h.getFlags();
479         h.setFlags(flags);
480
481         // update MessageFolderInfo
482
if (oldFlags.get(Flags.RECENT) && !flags.get(Flags.RECENT)) {
483             getMessageFolderInfo().decRecent();
484         }
485
486         if (!oldFlags.get(Flags.RECENT) && flags.get(Flags.RECENT)) {
487             getMessageFolderInfo().incRecent();
488         }
489
490         if (oldFlags.get(Flags.SEEN) && !flags.get(Flags.SEEN)) {
491             getMessageFolderInfo().incUnseen();
492         }
493
494         if (!oldFlags.get(Flags.SEEN) && flags.get(Flags.SEEN)) {
495             getMessageFolderInfo().decUnseen();
496         }
497     }
498
499     /**
500      * This method first tries to find the requested header in the header cache.
501      * If the headerfield is not cached, the message source is parsed.
502      *
503      * @see org.columba.mail.folder.IMailbox#getHeaderFields(java.lang.Object,
504      * java.lang.String[])
505      *
506      */

507     public Header getHeaderFields(Object JavaDoc uid, String JavaDoc[] keys) throws Exception JavaDoc {
508         // cached headerfield list
509
List JavaDoc cachedList = Arrays.asList(CachedHeaderfields
510                 .getDefaultHeaderfields());
511
512         LinkedList JavaDoc keyList = new LinkedList JavaDoc(Arrays.asList(keys));
513
514         ListTools.substract(keyList, cachedList);
515
516         if (keyList.size() == 0) {
517             return getHeaderList().get(uid).getHeader();
518         } else {
519             // We need to parse
520
// get message with UID
521
IColumbaMessage message = getMessage(uid);
522
523             Header header = message.getHeader().getHeader();
524
525             Header subHeader = new Header();
526             String JavaDoc value;
527
528             for (int i = 0; i < keys.length; i++) {
529                 value = header.get(keys[i]);
530
531                 if (value != null) {
532                     subHeader.set(keys[i], value);
533                 }
534             }
535
536             return subHeader;
537         }
538     }
539
540     /**
541      * @see org.columba.mail.folder.IMailbox#expungeFolder()
542      */

543     public void expungeFolder() throws Exception JavaDoc {
544         // make sure to close all file handles
545
// to the currently cached message
546
// -> necessary for windows to be able to delete the local file
547
if (aktMessage != null) {
548             aktMessage.close();
549             aktMessage = null;
550         }
551
552         super.expungeFolder();
553     }
554
555     /**
556      * @see org.columba.mail.folder.IMailbox#getAllHeaderFields(java.lang.Object)
557      */

558     public Header getAllHeaderFields(Object JavaDoc uid) throws Exception JavaDoc {
559
560         IColumbaMessage message = getMessage(uid);
561
562         Header header = message.getHeader().getHeader();
563
564         return header;
565     }
566
567     public synchronized IHeaderList getHeaderList() throws Exception JavaDoc {
568         if (headerList == null) {
569             // header cache is stored in "headerlist" subfolder
570
File JavaDoc headercacheDirectory = new File JavaDoc(getDirectoryFile(),"headerlist");
571             headerList = new BerkeleyDBHeaderList(headercacheDirectory, getId());
572             final AbstractMessageFolder folder = this;
573             headerList
574                     .addHeaderListCorruptedListener(new IHeaderListCorruptedListener() {
575
576                         public void headerListCorrupted(IHeaderList headerList) {
577                             try {
578                                 SyncHeaderList.sync(folder, headerList);
579                             } catch (IOException JavaDoc e) {
580                                 LOG.severe(e.getMessage());
581                             }
582                         }
583                     });
584         }
585
586         if (firstOpen) {
587
588             if (headerList.count() != getDataStorageInstance()
589                     .getMessageCount()) {
590                 // Must be out of sync!
591
SyncHeaderList.sync(this, headerList);
592             }
593
594             firstOpen = false;
595         }
596
597         return headerList;
598     }
599
600     /**
601      * @see org.columba.mail.folder.AbstractMessageFolder#save()
602      */

603     public void save() throws Exception JavaDoc {
604         super.save();
605         if (headerList != null)
606             headerList.persist();
607     }
608
609     /*
610      * (non-Javadoc)
611      *
612      * @see org.columba.mail.folder.AbstractMessageFolder#loadMessageFolderInfo()
613      */

614     protected void loadMessageFolderInfo() {
615         super.loadMessageFolderInfo();
616
617         int storedCount = getDataStorageInstance().getMessageCount();
618         // Check if still consistent
619
if (messageFolderInfo.getExists() != storedCount) {
620             recreateMessageFolderInfo();
621         }
622
623     }
624 }
Popular Tags