KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > core > TaskList


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tasklist.core;
21
22
23 import java.io.*;
24 import java.util.*;
25
26 /**
27  * This class models flat list or hierarchical tasks.
28  * It dispatches membership events.
29  *
30  * @author Tor Norbye
31  */

32 public class TaskList implements ObservableList, TaskListener {
33
34     // List category
35
final static String JavaDoc USER_CATEGORY = "usertasks"; // NOI18N
36

37     /**
38      * Data holder, you must synchonize on it and keep listCopy updated (reset it to null on modification).
39      * <p>
40      * <pre>
41      * e.g. write operation: e.g. read operation:
42      * synchronized(tasks) { synchronized(tasks) {
43      * // tasks.modify(); // iterator = tasks.iterator();
44      * listCopy = null; // return tasks.size();
45      * } }
46      * </pre>
47      */

48     private List tasks = new LinkedList();
49
50     // #43166 this one is distributed to clients
51
private List listCopy;
52
53     private final ArrayList listeners = new ArrayList(67);
54     
55     /** Has the options set changed such that we need to save */
56     protected boolean needSave = false;
57     protected boolean dontSave = false;
58     
59     /**
60      * Creates a new instance of TaskList.
61      */

62     public TaskList() {
63     }
64
65     protected void setNeedSave(boolean b) {
66         needSave = b;
67     }
68
69     protected void setDontSave(boolean b) {
70         dontSave = b;
71     }
72
73     /** Read-only access to tasks held by this list. */
74     public final List getTasks() {
75         synchronized(tasks) {
76             if (listCopy == null) {
77                 // #43166 clone it for iterators safeness
78
listCopy = Collections.unmodifiableList(new ArrayList(tasks));
79             }
80             return listCopy;
81         }
82     }
83
84     /** Add a list of tasks to the tasklist, and remove a list of
85      * tasks from the tasklist. This is done instead of a separate
86      * add and remove method such that you can change the tasklist
87      * atomically without having an intermediate screen refresh.
88      * Note that if a task appears in both lists, it will be ADDED.
89      * (Because the removal will occur first.)
90      *
91      * @param addList The list of tasks to be added. Can be null.
92      * @param removeList The list of tasks to be removed. Can be null.
93      * @param append If true, append to the list, otherwise prepend. Ignored
94      * if the after parameter is not null.
95      * @param parent Normally null, but you can specify a parent task
96      * here if you want to add subitems
97      * @param after The task which will be immediately before
98      * the new subtask after the addition (e.g. add
99      * this subtask directly AFTER the specified
100      * task). Overrides the append parameter.
101      *
102      * @todo Consider to add removeList.removeAll(addList) tricks, it could speedup
103      * all clients (eliminating remove foloved by add events).
104      */

105     public void addRemove(List addList, List removeList, boolean append,
106                           Task parent, Task after) {
107         // Disable updates for the duration of the list update
108
setSilentUpdate(true, false);
109
110         boolean modified = false;
111
112         // Remove items
113
// TODO add Task.removeSubtasks(List) ? See addSubtasks below
114
Iterator it;
115         if (removeList != null) {
116             it = removeList.iterator();
117             while (it.hasNext()) {
118                 Task task = (Task) it.next();
119                 if (parent != null) {
120                     parent.removeSubtask(task);
121                     task.removeTaskListener(this);
122                 } else {
123                     removeTask(task);
124                 }
125                 modified = true;
126             }
127         }
128
129 // if (parent == null) {
130
// if (root == null) {
131
// root = getRoot();
132
// }
133
// parent = root;
134
// }
135

136         if (addList != null && addList.size()>0) {
137             modified = true;
138
139             // User insert: prepend to the list
140
if (parent != null) {
141                 it = addList.iterator();
142                 while (it.hasNext()) {
143                     Task next = (Task) it.next();
144                     next.addTaskListener(this);
145                 }
146                 parent.addSubtasks(addList, append, after);
147             } else {
148                 if (after != null) {
149                     addTasks(addList, after);
150                 } else {
151                     addTasks(addList, append);
152                 }
153             }
154         }
155
156         // Update the task list now
157
// Only save if non-temporary items were added
158

159         // XXX - now that I have added a parent reference, should
160
// the property notification happen relative to it? Probably yes.
161
// Need parent reference in setSilentUpdate
162
setSilentUpdate(false, modified);
163     }
164
165     /**
166      * Add top level tasks_ to the list and fire event.
167      * Treats tasks_ as single task keeping their original order.
168      */

169     public final void addTasks(List tasks_, boolean append) {
170         Iterator it = tasks_.iterator();
171         int slot = 0;
172         while (it.hasNext()) {
173             Task task = (Task) it.next();
174             task.addTaskListener(this);
175             if (append) {
176                 synchronized(tasks) {
177                     tasks.add(task);
178                     listCopy = null;
179                 }
180             } else {
181                 synchronized(tasks) {
182                     tasks.add(slot++, task);
183                     listCopy = null;
184                 }
185             }
186             fireAdded(task); // TODO silent update?
187
}
188     }
189
190     private void addTasks(List tasks, Task after) {
191         int index = tasks.indexOf(after);
192         if (index == -1) {
193             addTasks(tasks, true);
194         } else {
195             tasks.addAll(index+1, tasks);
196             Iterator it = tasks.iterator();
197             while (it.hasNext()) {
198                 Task task = (Task) it.next();
199                 task.addTaskListener(this);
200                 fireAdded(task);
201             }
202         }
203     }
204
205     /**
206      * Add top level task to the list and fire event.
207      */

208     public final void appendTask(Task task) {
209         task.addTaskListener(this);
210         synchronized(tasks) {
211             tasks.add(task);
212             listCopy = null;
213         }
214         fireAdded(task);
215     }
216
217     /**
218      * Remove top level tasks from the list and fire event
219      */

220     public final void removeTasks(List tasks) {
221         Iterator it = tasks.iterator();
222         while (it.hasNext()) {
223             Task task = (Task) it.next();
224             removeTask(task);
225         }
226     }
227
228     /**
229      * Remove top level task from the list and fire event
230      */

231     public final void removeTask(Task task) {
232         task.removeTaskListener(this);
233         boolean removed = false;
234         int index;
235         synchronized(tasks) {
236             index = tasks.indexOf(task);
237             removed = tasks.remove(task);
238             listCopy = null;
239         }
240         if (removed) {
241             fireRemoved(null, task, index); // TODO silent update?
242
}
243     }
244
245     /**
246      * Notify the task list that some aspect of it has been changed, so
247      * it should save itself soon. Eventually calls save
248      */

249     public void markChanged() {
250         // For now, save right away....
251
// TODO - make a timer here, for example 10 seconds, such that
252
// everytime we get a markChanged() message, we reset the timer.
253
// When the timer expires, we save.
254
// Then go change the code such that nobody calls save() directly,
255
// only markChanged() - except for the window, perhaps, which may call
256
// save on program shutdown. We don't want edits within 10 seconds
257
// of shutting down the IDE to be lost...
258
needSave = true;
259         save();
260     }
261
262     /** Write tasks out to disk */
263     public void save() {
264         needSave = false;
265     }
266
267     /** Setter for property silentUpdate.
268      * When true, don't notify anybody or save the task list
269      when contents changes. This is used for batch operations
270      (such as inserting a series of tasks when scanning a source file.)
271      * @param silentUpdate New value of property silentUpdate.
272      * @param saveOnFinish If true, save the task when we stop being silent
273      *
274      * @deprecated use {@link #addRemove} that should merge events (but it does not right now)
275      */

276     final void setSilentUpdate(boolean silentUpdate,
277                                boolean saveOnFinish) {// XXX remove the publicness
278
dontSave = silentUpdate;
279         needSave = true;
280 // if (silentUpdate) {
281
// // XXX this is going to generate lots of updates.
282
// // I should set silentUpdate on the root during the
283
// // deletions...
284
// root.setSilentUpdate(true, false);
285
// } else {
286
// // XXX It would be better NOT to do this, so I don't get
287
// // a refresh after the items have been deleted!
288
// root.setSilentUpdate(false, true);
289
// }
290
if (!dontSave && saveOnFinish) {
291             // May do nothing if setSilentUpdate above did a TaskList.markChanged()
292
save();
293         }
294     }
295
296     public void addTaskListener(TaskListener listener) {
297         synchronized (listeners) {
298             listeners.add(listener);
299         }
300     }
301
302     public void removeTaskListener(TaskListener listener) {
303         synchronized (listeners) {
304             listeners.remove(listener);
305         }
306     }
307
308     /** Fire TaskListener.addedTask */
309     protected void fireAdded(Task task) {
310         synchronized (listeners) {
311             int n = listeners.size();
312             for (int i = 0; i < n; i++) {
313                 TaskListener tl = (TaskListener) listeners.get(i);
314                 tl.addedTask(task);
315             }
316         }
317     }
318
319     /** Fire TaskListener.structureChanged */
320     protected void fireStructureChanged(Task task) {
321         TaskListener[] taskListeners; // some listeners are self deregistering on this event causing index exception
322
synchronized (listeners) {
323             taskListeners = new TaskListener[listeners.size()];
324             taskListeners = (TaskListener[]) listeners.toArray(taskListeners);
325         }
326         for (int i = 0; i < taskListeners.length; i++) {
327             taskListeners[i].structureChanged(task);
328         }
329     }
330
331     /** Fire TaskListener.removedTask */
332     protected void fireRemoved(Task pt, Task task, int index) {
333         TaskListener[] taskListeners; // some listeners are self deregistering on this event causing index exception
334
synchronized (listeners) {
335             taskListeners = new TaskListener[listeners.size()];
336             taskListeners = (TaskListener[]) listeners.toArray(taskListeners);
337         }
338         for (int i = 0; i < taskListeners.length; i++) {
339             taskListeners[i].removedTask(pt, task, index);
340         }
341
342     }
343
344     /**
345      * Return a count of the number of tasks in this list.
346      *
347      * @deprecated use TLUtils#recursiveCount
348      */

349     public int size() {
350         synchronized(tasks) {
351             return TLUtils.recursiveCount(tasks.iterator());
352         }
353     }
354
355     /**
356      * Remove all the tasks in this tasklist
357      */

358     public void clear() {
359         synchronized(tasks) {
360             tasks.clear();
361             listCopy = null;
362         }
363         fireStructureChanged(null);
364     }
365
366     /** For debugging purposes, only. Writes directly to serr. */
367     public void print() {
368         System.err.println("\nTask List:\n-------------"); // NOI18N
369
synchronized(tasks) {
370             Iterator it = tasks.iterator();
371             while (it.hasNext()) {
372                 Task next = (Task) it.next();
373                 recursivePrint(next, 0);
374             }
375         }
376
377         System.err.println("\n\n");
378     }
379
380     private void recursivePrint(Task node, int depth) {
381         if (depth > 20) { // probably invalid list
382
Thread.dumpStack();
383             return;
384         }
385         for (int i = 0; i < depth; i++) {
386             System.err.print(" ");
387         }
388         System.err.println(node);
389         if (node.getSubtasks() != null) {
390             List l = node.getSubtasks();
391             ListIterator it = l.listIterator();
392             while (it.hasNext()) {
393                 Task task = (Task) it.next();
394                 recursivePrint(task, depth + 1);
395             }
396         }
397     }
398
399     // TaskListener impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400

401     /** Internal listener implementtaion. Cumulates event from list members to list level. */
402     public void selectedTask(Task t) {
403         // XXX ignore
404
}
405
406     /** Internal listener implementtaion. Cumulates event from list members to list level. */
407     public void warpedTask(Task t) {
408         // XXX ignore
409
}
410
411     /** Internal listener implementtaion. Cumulates event from list members to list level. */
412     public void addedTask(Task t) {
413         fireAdded(t);
414     }
415
416     /** Internal listener implementtaion. Cumulates event from list members to list level. */
417     public void removedTask(Task pt, Task t, int index) {
418         fireRemoved(pt, t, index);
419     }
420
421     /** Internal listener implementtaion. Cumulates event from list members to list level. */
422     public void structureChanged(Task t) {
423         fireStructureChanged(t);
424     }
425
426     /**
427      * Is this task list empty?
428      *
429      * @return true if this task list has no tasks
430      */

431     public boolean isEmpty() {
432         return tasks.size() == 0;
433     }
434 }
435
Popular Tags