001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.mail.folder;
018:
019: import java.util.logging.Logger;
020:
021: import javax.swing.event.EventListenerList;
022: import javax.swing.tree.DefaultMutableTreeNode;
023: import javax.swing.tree.TreeNode;
024: import javax.swing.tree.TreePath;
025:
026: import org.columba.core.base.Lock;
027: import org.columba.core.xml.XmlElement;
028: import org.columba.mail.config.FolderItem;
029: import org.columba.mail.config.IFolderItem;
030: import org.columba.mail.folder.event.FolderEvent;
031: import org.columba.mail.folder.event.FolderEventDelegator;
032: import org.columba.mail.folder.event.IFolderListener;
033:
034: /**
035: * Represents a treenode and is the abstract class every folder extends.
036: * <p>
037: * See tree.xml configuration file.
038: *
039: * @author fdietz
040: */
041: public abstract class AbstractFolder extends DefaultMutableTreeNode
042: implements IMailFolder {
043:
044: /** JDK 1.4+ logging framework logger, used for logging. */
045: private static final Logger LOG = Logger
046: .getLogger("org.columba.mail.folder");
047:
048: // the next new folder will get this UID
049: private static int nextUid = 0;
050:
051: // folderitem wraps xml configuration from tree.xml
052: protected IFolderItem node;
053:
054: // locking mechanism
055: protected Lock myLock = new Lock();
056:
057: // Root folder cache
058: private IMailFolder rootFolder;
059:
060: protected EventListenerList listenerList = new EventListenerList();
061:
062: public AbstractFolder(String name, String type) {
063: super ();
064:
065: XmlElement defaultElement = new XmlElement("folder");
066: defaultElement.addAttribute("type", type);
067: defaultElement.addAttribute("uid", Integer.toString(nextUid++));
068: defaultElement.addElement(new XmlElement("property"));
069:
070: setConfiguration(new FolderItem(defaultElement));
071: try {
072: setName(name);
073: } catch (Exception e) {
074: LOG.severe(e.getMessage());
075: }
076:
077: // register interest on tree node changes
078: addFolderListener(FolderEventDelegator.getInstance());
079: }
080:
081: public AbstractFolder() {
082: super ();
083:
084: // register interest on tree node changes
085: addFolderListener(FolderEventDelegator.getInstance());
086: }
087:
088: public AbstractFolder(FolderItem node) {
089: super ();
090: setConfiguration(node);
091:
092: // register interest on tree node changes
093: addFolderListener(FolderEventDelegator.getInstance());
094: }
095:
096: /**
097: * Overwritten DefaultMutableTreeNode.add(MutableTreeNode) in favor of
098: * our interface
099: *
100: * @see org.columba.mail.folder.IMailFolder#add(org.columba.mail.folder.IMailFolder)
101: */
102: public void add(IMailFolder treeNode) {
103: super .add(treeNode);
104: }
105:
106: /**
107: * Adds a listener.
108: */
109: public void addFolderListener(IFolderListener l) {
110: listenerList.add(IFolderListener.class, l);
111: }
112:
113: /**
114: * Removes a previously registered listener.
115: */
116: public void removeFolderListener(IFolderListener l) {
117: listenerList.remove(IFolderListener.class, l);
118: }
119:
120: /**
121: * Propagates an event to all registered listeners notifying them that this
122: * folder has been renamed.
123: */
124: public void fireFolderPropertyChanged() {
125: FolderEvent e = new FolderEvent(this );
126: // Guaranteed to return a non-null array
127: Object[] listeners = listenerList.getListenerList();
128:
129: // Process the listeners last to first, notifying
130: // those that are interested in this event
131: for (int i = listeners.length - 2; i >= 0; i -= 2) {
132: if (listeners[i] == IFolderListener.class) {
133: ((IFolderListener) listeners[i + 1])
134: .folderPropertyChanged(e);
135: }
136: }
137: }
138:
139: /**
140: * Propagates an event to all registered listeners notifying them that a
141: * subfolder has been added to this folder.
142: */
143: public void fireFolderAdded(IMailFolder folder) {
144: FolderEvent e = new FolderEvent(this , folder);
145: // Guaranteed to return a non-null array
146: Object[] listeners = listenerList.getListenerList();
147:
148: // Process the listeners last to first, notifying
149: // those that are interested in this event
150: for (int i = listeners.length - 2; i >= 0; i -= 2) {
151: if (listeners[i] == IFolderListener.class) {
152: ((IFolderListener) listeners[i + 1]).folderAdded(e);
153: }
154: }
155: }
156:
157: /**
158: * Propagates an event to all registered listeners notifying them that this
159: * folder has been removed from its parent folder. This method removes all
160: * registered listeners.
161: */
162: public void fireFolderRemoved() {
163: FolderEvent e = new FolderEvent(this , this );
164: // Guaranteed to return a non-null array
165: Object[] listeners = listenerList.getListenerList();
166:
167: // Process the listeners last to first, notifying
168: // those that are interested in this event
169: for (int i = listeners.length - 2; i >= 0; i -= 2) {
170: if (listeners[i] == IFolderListener.class) {
171: ((IFolderListener) listeners[i + 1]).folderRemoved(e);
172: listenerList.remove(IFolderListener.class,
173: (IFolderListener) listeners[i + 1]);
174: }
175: }
176: }
177:
178: /**
179: * Method getSelectionTreePath.
180: *
181: * @return TreePath
182: */
183: public TreePath getSelectionTreePath() {
184: return new TreePath(getPathToRoot(this , 0));
185: }
186:
187: /**
188: * @see javax.swing.tree.DefaultMutableTreeNode#getPathToRoot(TreeNode, int)
189: */
190: protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
191: TreeNode[] retNodes;
192:
193: if (aNode == null) {
194: if (depth == 0) {
195: return null;
196: } else {
197: retNodes = new TreeNode[depth];
198: }
199: } else {
200: depth++;
201: retNodes = getPathToRoot(aNode.getParent(), depth);
202: retNodes[retNodes.length - depth] = aNode;
203: }
204:
205: return retNodes;
206: }
207:
208: /**
209: * Return tree path as string
210: *
211: * @return String tree path
212: */
213: public String getTreePath() {
214: TreeNode[] treeNode = getPathToRoot(this , 0);
215:
216: StringBuffer path = new StringBuffer();
217:
218: for (int i = 1; i < treeNode.length; i++) {
219: AbstractFolder folder = (AbstractFolder) treeNode[i];
220: path.append("/" + folder.getName());
221: }
222:
223: return path.toString();
224: }
225:
226: /**
227: * Returns the folder's UID.
228: */
229: public String getId() {
230: return node.get("uid");
231: }
232:
233: /**
234: * Returns the folder's configuration.
235: */
236: public IFolderItem getConfiguration() {
237: return node;
238: }
239:
240: /**
241: * Sets the folder's configuration.
242: */
243: public void setConfiguration(IFolderItem node) {
244: this .node = node;
245:
246: try {
247: if (node.getInteger("uid") >= nextUid) {
248: nextUid = node.getInteger("uid") + 1;
249: }
250: } catch (NumberFormatException ex) {
251: node.setInteger("uid", nextUid++);
252: }
253: }
254:
255: public String getType() {
256: IFolderItem item = getConfiguration();
257:
258: return item.get("type");
259: }
260:
261: /**
262: * Returns the folder's name.
263: */
264: public String getName() {
265: String name = null;
266:
267: IFolderItem item = getConfiguration();
268: name = item.getString("property", "name");
269:
270: //(tstich) it is necessary to return null because imap account
271: //creation needs it!
272: //if ( name == null ) name = "FIXME";
273:
274: return name;
275: }
276:
277: public String toString() {
278: return getName();
279: }
280:
281: /**
282: * Sets the folder's name. This method notifies registered FolderListeners.
283: */
284: public void setName(String newName) throws Exception {
285: IFolderItem item = getConfiguration();
286: item.setString("property", "name", newName);
287: fireFolderPropertyChanged();
288: }
289:
290: /*
291: public MailFolderCommandReference getCommandReference(MailFolderCommandReference r) {
292: return r;
293: }
294: */
295:
296: /**
297: * ******************************** locking mechanism
298: * ***************************
299: */
300: public boolean tryToGetLock(Object locker) {
301: return myLock.tryToGetLock(locker);
302: }
303:
304: public void releaseLock(Object locker) {
305: myLock.release(locker);
306: }
307:
308: /**
309: * ************************** treenode management
310: * ******************************
311: */
312: public void insert(IMailFolder newFolder, int newIndex) {
313: IMailFolder oldParent = (IMailFolder) newFolder.getParent();
314: int oldIndex = oldParent.getIndex(newFolder);
315: oldParent.remove(oldIndex);
316:
317: XmlElement oldParentNode = oldParent.getConfiguration()
318: .getRoot();
319: XmlElement newChildNode = newFolder.getConfiguration()
320: .getRoot();
321: oldParentNode.removeElement(newChildNode);
322:
323: newFolder.setParent(this );
324: children.insertElementAt(newFolder, newIndex);
325:
326: XmlElement newParentNode = getConfiguration().getRoot();
327:
328: int j = -1;
329: boolean inserted = false;
330:
331: for (int i = 0; i < newParentNode.count(); i++) {
332: XmlElement n = newParentNode.getElement(i);
333: String name = n.getName();
334:
335: if (name.equals("folder")) {
336: j++;
337: }
338:
339: if (j == newIndex) {
340: newParentNode.insertElement(newChildNode, i);
341: inserted = true;
342: }
343: }
344:
345: if (!inserted) {
346: if ((j + 1) == newIndex) {
347: newParentNode.append(newChildNode);
348: }
349: }
350: }
351:
352: /**
353: * Removes this folder from its parent. This method will notify registered
354: * FolderListeners.
355: */
356: public void removeFolder() throws Exception {
357: // remove XmlElement
358: getConfiguration().getRoot().getParent().removeElement(
359: getConfiguration().getRoot());
360:
361: // notify listeners
362: fireFolderRemoved();
363:
364: }
365:
366: /**
367: * Adds a child folder to this folder. This method will notify registered
368: * FolderListeners.
369: */
370: public void addSubfolder(IMailFolder child) throws Exception {
371: getConfiguration().getRoot().addElement(
372: child.getConfiguration().getRoot());
373: fireFolderAdded(child);
374: }
375:
376: public AbstractFolder findChildWithName(String str, boolean recurse) {
377: return findChildWithName(str, recurse, null);
378: }
379:
380: public AbstractFolder findChildWithName(String str,
381: boolean recurse, Class type) {
382: for (int i = 0; i < getChildCount(); i++) {
383: AbstractFolder child = (AbstractFolder) getChildAt(i);
384: // Check the type
385: if (type != null && !type.equals(child.getClass()))
386: continue;
387:
388: String name = child.getName();
389:
390: if (name.equalsIgnoreCase(str)) {
391: return child;
392: } else if (recurse) {
393: AbstractFolder subchild = child.findChildWithName(str,
394: true);
395:
396: if (subchild != null) {
397: return subchild;
398: }
399: }
400: }
401:
402: return null;
403: }
404:
405: public AbstractFolder findChildWithUID(String uid, boolean recurse) {
406: for (int i = 0; i < getChildCount(); i++) {
407: AbstractFolder child = (AbstractMessageFolder) getChildAt(i);
408: String childUid = child.getId();
409:
410: if (uid.equals(childUid)) {
411: return child;
412: } else if (recurse) {
413: AbstractFolder subchild = child.findChildWithUID(uid,
414: true);
415:
416: if (subchild != null) {
417: return subchild;
418: }
419: }
420: }
421:
422: return null;
423: }
424:
425: /**
426: *
427: * AbstractFolder wraps XmlElement
428: *
429: * all treenode manipulation is passed to the corresponding XmlElement
430: */
431: public void moveTo(IMailFolder newParent) {
432: // do the same for the XmlElement node
433: getConfiguration().getRoot().removeFromParent();
434:
435: // do the same for the XmlElement of child
436: newParent.getConfiguration().getRoot().addElement(
437: getConfiguration().getRoot());
438:
439: newParent.fireFolderAdded(this );
440: }
441:
442: /** ******************* capabilities ************************************* */
443: /**
444: * Does this treenode support adding messages?
445: *
446: * @return true, if this folder is able to contain messages, false otherwise
447: *
448: */
449: public boolean supportsAddMessage() {
450: return false;
451: }
452:
453: /**
454: * Returns true if this folder can have sub folders of the specified type;
455: * false otherwise.
456: *
457: * @param newFolderType
458: * the folder that is going to be inserted as a child.
459: * @return true if this folder can have sub folders; false otherwise.
460: */
461: public boolean supportsAddFolder(String newFolderType) {
462: return false;
463: }
464:
465: /**
466: * Returns true if this folder type can be moved around in the folder tree.
467: *
468: * @return true if this folder type can be moved around in the folder tree.
469: */
470: public boolean supportsMove() {
471: return false;
472: }
473:
474: /**
475: * Return the root folder of this folder.
476: * <p>
477: * This is especially useful when using IMAP. IMAP has a root folder which
478: * is labelled with the account name.
479: *
480: * @return root parent folder of this folder
481: */
482: public IMailFolder getRootFolder() {
483: // If rootFolder is not cached traverse the tree
484: if (rootFolder == null) {
485: IMailFolder parent = (IMailFolder) getParent();
486:
487: // There is no parent
488: if (parent == null) {
489: return this ;
490: }
491:
492: if (parent instanceof RootFolder) {
493: rootFolder = parent;
494: } else {
495: rootFolder = parent.getRootFolder();
496: }
497: }
498:
499: return rootFolder;
500: }
501:
502: }
|