001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.mail.folder.event;
019:
020: import java.awt.event.ActionEvent;
021: import java.awt.event.ActionListener;
022: import java.util.ArrayList;
023: import java.util.Collections;
024: import java.util.Comparator;
025: import java.util.Iterator;
026: import java.util.List;
027:
028: import javax.swing.Timer;
029: import javax.swing.event.EventListenerList;
030: import javax.swing.tree.TreeNode;
031:
032: import org.columba.core.base.Mutex;
033: import org.columba.mail.folder.IMailFolder;
034: import org.columba.mail.gui.table.model.TableModelChangedEvent;
035: import org.columba.mail.gui.table.model.TableModelChangedListener;
036: import org.columba.mail.gui.tree.FolderTreeModel;
037:
038: /**
039: * Collects all folder events generated by AbstractFolder and
040: * AbstractMessageFolder.
041: * <p>
042: * Accumulates matching events to one big event. Afterwards it delegates this
043: * final event to FolderTreeModel and all TableModel objects.
044: * <p>
045: * A swing timer is used to trigger ui updates.
046: *
047: * @author fdietz
048: * @author tstich
049: */
050: public class FolderEventDelegator implements ActionListener,
051: FolderListener {
052:
053: private static final int UPDATE_DELAY = 500;
054:
055: private static FolderEventDelegator instance;
056:
057: private Timer timer;
058:
059: private Mutex mutex;
060:
061: private List[] messageRemovedList;
062:
063: private List[] messageFlagChangedList;
064:
065: private List[] messageAddedList;
066:
067: private List[] folderAddedList;
068:
069: private List[] folderRemovedList;
070:
071: private List[] folderPropertyChangedList;
072:
073: private int swap = 0;
074:
075: protected EventListenerList tableListenerList = new EventListenerList();
076:
077: private FolderEventDelegator() {
078: super ();
079:
080: messageRemovedList = new List[] { new ArrayList(500),
081: new ArrayList(500) };
082: messageFlagChangedList = new List[] { new ArrayList(500),
083: new ArrayList(500) };
084: messageAddedList = new List[] { new ArrayList(500),
085: new ArrayList(500) };
086:
087: folderAddedList = new List[] { new ArrayList(500),
088: new ArrayList(500) };
089: folderRemovedList = new List[] { new ArrayList(500),
090: new ArrayList(500) };
091: folderPropertyChangedList = new List[] { new ArrayList(500),
092: new ArrayList(500) };
093:
094: mutex = new Mutex();
095:
096: timer = new Timer(UPDATE_DELAY, this );
097: timer.start();
098: }
099:
100: public static FolderEventDelegator getInstance() {
101: if (instance == null)
102: instance = new FolderEventDelegator();
103:
104: return instance;
105: }
106:
107: /**
108: * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
109: */
110: public void actionPerformed(ActionEvent arg0) {
111: // process all events collected until now
112: mutex.lock();
113:
114: swap = 1 - swap;
115:
116: mutex.release();
117:
118: processTableEvents();
119:
120: processTreeEvents();
121:
122: clearAllLists();
123:
124: }
125:
126: /**
127: *
128: */
129: private void clearAllLists() {
130: messageAddedList[swap].clear();
131: messageRemovedList[swap].clear();
132: messageFlagChangedList[swap].clear();
133:
134: folderAddedList[swap].clear();
135: folderRemovedList[swap].clear();
136: folderPropertyChangedList[swap].clear();
137: }
138:
139: /**
140: * @param events
141: */
142: private void processTableEvents() {
143:
144: if (messageAddedList[swap].size() > 0) {
145:
146: // First sort so that Events from one folder stick together
147: Collections.sort(messageAddedList[swap],
148: FolderEventComparator.getInstance());
149: IMailFolder lastFolder = (IMailFolder) ((FolderEvent) messageAddedList[swap]
150: .get(0)).getSource();
151:
152: fireTableEvent(new TableModelChangedEvent(
153: TableModelChangedEvent.UPDATE, lastFolder));
154:
155: // Process the events
156: for (int i = 1; i < messageAddedList[swap].size(); i++) {
157: FolderEvent next = (FolderEvent) messageAddedList[swap]
158: .get(i);
159: if (next.getSource() != lastFolder) {
160: lastFolder = (IMailFolder) next.getSource();
161: fireTableEvent(new TableModelChangedEvent(
162: TableModelChangedEvent.UPDATE, lastFolder));
163: }
164: }
165: }
166:
167: if (messageRemovedList[swap].size() > 0) {
168: // First sort so that Events from one folder stick together
169: Collections.sort(messageRemovedList[swap],
170: FolderEventComparator.getInstance());
171: FolderEvent event = (FolderEvent) messageRemovedList[swap]
172: .get(0);
173: IMailFolder lastFolder = (IMailFolder) event.getSource();
174:
175: // Collect the uids for one folder
176: List collectedUids = new ArrayList();
177: collectedUids.add(event.getChanges());
178:
179: // Process the events
180: for (int i = 1; i < messageRemovedList[swap].size(); i++) {
181: FolderEvent next = (FolderEvent) messageRemovedList[swap]
182: .get(i);
183: if (next.getSource() != lastFolder) {
184: // new folder, fire to the old folder
185: fireTableEvent(new TableModelChangedEvent(
186: TableModelChangedEvent.REMOVE, lastFolder,
187: collectedUids.toArray()));
188:
189: // clear list and collect uids for new folder
190: collectedUids.clear();
191: collectedUids.add(next.getChanges());
192:
193: lastFolder = (IMailFolder) next.getSource();
194: } else {
195: collectedUids.add(next.getChanges());
196: }
197: }
198:
199: fireTableEvent(new TableModelChangedEvent(
200: TableModelChangedEvent.REMOVE, lastFolder,
201: collectedUids.toArray()));
202: }
203:
204: if (messageFlagChangedList[swap].size() > 0) {
205:
206: // First sort so that Events from one folder stick together
207: Collections.sort(messageFlagChangedList[swap],
208: FolderEventComparator.getInstance());
209: FolderEvent event = (FolderEvent) messageFlagChangedList[swap]
210: .get(0);
211: IMailFolder lastFolder = (IMailFolder) event.getSource();
212:
213: // Collect the uids for one folder
214: List collectedUids = new ArrayList();
215: collectedUids.add(event.getChanges());
216:
217: // Process the events
218: for (int i = 1; i < messageFlagChangedList[swap].size(); i++) {
219: FolderEvent next = (FolderEvent) messageFlagChangedList[swap]
220: .get(i);
221: if (next.getSource() != lastFolder) {
222: // new folder, fire to the old folder
223: fireTableEvent(new TableModelChangedEvent(
224: TableModelChangedEvent.MARK, lastFolder,
225: collectedUids.toArray()));
226:
227: // clear list and collect uids for new folder
228: collectedUids.clear();
229: collectedUids.add(next.getChanges());
230:
231: lastFolder = (IMailFolder) next.getSource();
232: } else {
233: collectedUids.add(next.getChanges());
234: }
235: }
236:
237: fireTableEvent(new TableModelChangedEvent(
238: TableModelChangedEvent.MARK, lastFolder,
239: collectedUids.toArray()));
240: }
241:
242: }
243:
244: /**
245: * @param events
246: */
247: private void processTreeEvents() {
248:
249: if (folderRemovedList[swap].size() > 0) {
250: IMailFolder lastFolder = null;
251:
252: // Process the events
253: for (int i = 0; i < folderRemovedList[swap].size(); i++) {
254: FolderEvent next = (FolderEvent) folderRemovedList[swap]
255: .get(i);
256:
257: lastFolder = (IMailFolder) next.getSource();
258:
259: TreeNode parent = lastFolder.getParent();
260: lastFolder.removeFromParent();
261: FolderTreeModel.getInstance().nodeStructureChanged(
262: parent);
263: }
264: }
265:
266: if (folderAddedList[swap].size() > 0) {
267:
268: // First sort so that Events from one folder stick together
269: Collections.sort(folderAddedList[swap],
270: FolderEventComparator.getInstance());
271:
272: FolderEvent e = (FolderEvent) folderAddedList[swap].get(0);
273:
274: IMailFolder lastFolder = (IMailFolder) e.getSource();
275: IMailFolder child = (IMailFolder) e.getChanges();
276:
277: // If there is still a parent -> remove it
278: if (child.getParent() != null) {
279: TreeNode parent = lastFolder.getParent();
280: child.removeFromParent();
281: FolderTreeModel.getInstance().nodeStructureChanged(
282: parent);
283: }
284:
285: // add child to this node
286: lastFolder.add(child);
287:
288: FolderTreeModel.getInstance().nodeStructureChanged(
289: lastFolder);
290:
291: // Process the events
292: for (int i = 1; i < folderAddedList[swap].size(); i++) {
293: FolderEvent next = (FolderEvent) folderAddedList[swap]
294: .get(i);
295: if (next.getSource() != lastFolder) {
296: lastFolder = (IMailFolder) next.getSource();
297: FolderTreeModel.getInstance().nodeStructureChanged(
298: lastFolder);
299: }
300: }
301: }
302:
303: if (folderPropertyChangedList[swap].size() > 0) {
304:
305: // First sort so that Events from one folder stick together
306: Collections.sort(folderPropertyChangedList[swap],
307: FolderEventComparator.getInstance());
308:
309: IMailFolder lastFolder = (IMailFolder) ((FolderEvent) folderPropertyChangedList[swap]
310: .get(0)).getSource();
311:
312: FolderTreeModel.getInstance().nodeChanged(lastFolder);
313:
314: // Process the events
315: for (int i = 1; i < folderPropertyChangedList[swap].size(); i++) {
316: FolderEvent next = (FolderEvent) folderPropertyChangedList[swap]
317: .get(i);
318: if (next.getSource() != lastFolder) {
319: lastFolder = (IMailFolder) next.getSource();
320: FolderTreeModel.getInstance().nodeChanged(
321: lastFolder);
322: }
323: }
324: }
325:
326: }
327:
328: /** ********************* Folder Listener *************************** */
329: /**
330: * @see org.columba.mail.folder.event.FolderListener#folderAdded(IFolderEvent)
331: */
332: public void folderAdded(IFolderEvent e) {
333: mutex.lock();
334:
335: Iterator it = folderAddedList[1 - swap].iterator();
336: boolean found = false;
337: while (it.hasNext() && !found) {
338: FolderEvent event = (FolderEvent) it.next();
339: found = (event.getSource() == e.getSource());
340: }
341:
342: if (!found)
343: folderAddedList[1 - swap].add(e);
344:
345: mutex.release();
346: }
347:
348: /**
349: * @see org.columba.mail.folder.event.FolderListener#folderRemoved(IFolderEvent)
350: */
351: public void folderRemoved(IFolderEvent e) {
352: mutex.lock();
353:
354: Iterator it = folderRemovedList[1 - swap].iterator();
355: boolean found = false;
356: while (it.hasNext() && !found) {
357: FolderEvent event = (FolderEvent) it.next();
358: found = (event.getSource() == e.getSource());
359: }
360:
361: if (!found)
362: folderRemovedList[1 - swap].add(e);
363:
364: mutex.release();
365: }
366:
367: /**
368: * @see org.columba.mail.folder.event.FolderListener#folderPropertyChanged(IFolderEvent)
369: */
370: public void folderPropertyChanged(IFolderEvent e) {
371: mutex.lock();
372:
373: Iterator it = folderPropertyChangedList[1 - swap].iterator();
374: boolean found = false;
375: while (it.hasNext() && !found) {
376: FolderEvent event = (FolderEvent) it.next();
377: found = (event.getSource() == e.getSource());
378: }
379:
380: if (!found)
381: folderPropertyChangedList[1 - swap].add(e);
382:
383: mutex.release();
384:
385: }
386:
387: /**
388: * @see org.columba.mail.folder.event.FolderListener#messageAdded(IFolderEvent)
389: */
390: public void messageAdded(IFolderEvent e) {
391: mutex.lock();
392:
393: Iterator it = messageAddedList[1 - swap].iterator();
394: boolean found = false;
395: while (it.hasNext() && !found) {
396: FolderEvent event = (FolderEvent) it.next();
397: found = (event.getSource() == e.getSource());
398: }
399:
400: if (!found)
401: messageAddedList[1 - swap].add(e);
402:
403: mutex.release();
404: }
405:
406: /**
407: * @see org.columba.mail.folder.event.FolderListener#messageFlagChanged(iFolderEvent)
408: */
409: public void messageFlagChanged(IFolderEvent e) {
410: mutex.lock();
411:
412: messageFlagChangedList[1 - swap].add(e);
413:
414: mutex.release();
415:
416: }
417:
418: /**
419: * @see org.columba.mail.folder.event.FolderListener#messageRemoved(IFolderEvent)
420: */
421: public void messageRemoved(IFolderEvent e) {
422: mutex.lock();
423:
424: messageRemovedList[1 - swap].add(e);
425:
426: mutex.release();
427: }
428:
429: /** ********************* Table Listener **************************** */
430:
431: /**
432: * Adds a listener.
433: */
434: public void addTableListener(TableModelChangedListener l) {
435: tableListenerList.add(TableModelChangedListener.class, l);
436: }
437:
438: /**
439: * Removes a previously registered listener.
440: */
441: public void removeTableListener(TableModelChangedListener l) {
442: tableListenerList.remove(TableModelChangedListener.class, l);
443: }
444:
445: private void fireTableEvent(TableModelChangedEvent e) {
446: // Guaranteed to return a non-null array
447: Object[] listeners = tableListenerList.getListenerList();
448:
449: // Process the listeners last to first, notifying
450: // those that are interested in this event
451: for (int i = listeners.length - 2; i >= 0; i -= 2) {
452: if (listeners[i] == TableModelChangedListener.class) {
453: TableModelChangedListener listener = (TableModelChangedListener) listeners[i + 1];
454: if (listener.isInterestedIn(e.getSrcFolder())) {
455: listener.tableChanged(e);
456: }
457: }
458: }
459: }
460:
461: }
462:
463: class FolderEventComparator implements Comparator {
464:
465: private static FolderEventComparator instance = new FolderEventComparator();
466:
467: private FolderEventComparator() {
468: }
469:
470: public static FolderEventComparator getInstance() {
471: return instance;
472: }
473:
474: /**
475: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
476: */
477: public int compare(Object arg0, Object arg1) {
478: FolderEvent a = (FolderEvent) arg0;
479: FolderEvent b = (FolderEvent) arg1;
480:
481: return (a.getSource() != b.getSource()) ? 0 : 1;
482: }
483:
484: }
|