KickJava   Java API By Example, From Geeks To Geeks.

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


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 package org.netbeans.modules.tasklist.core;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.Reader JavaDoc;
24 import java.io.Writer JavaDoc;
25 import java.util.*;
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28 import javax.swing.event.EventListenerList JavaDoc;
29 import org.netbeans.modules.tasklist.client.Suggestion;
30 import org.netbeans.modules.tasklist.client.SuggestionPriority;
31 import org.openide.ErrorManager;
32 import org.openide.nodes.Node;
33 import org.openide.nodes.Node.Cookie;
34
35 /**
36  * Class which represents a task in the tasklist.
37  *
38  * @author Tor Norbye
39  * @author tl
40  */

41 public class Task extends Suggestion implements Cloneable JavaDoc, Cookie {
42     private static final Logger JavaDoc LOGGER = TLUtils.getLogger(Task.class);
43
44     static {
45         LOGGER.setLevel(Level.OFF);
46     }
47
48     /**
49      * Some of this items attributes (such as its description - anything
50      * except the subtask list) has changed
51      *
52      * @deprecated Suggestion provides specialized properties
53      */

54     static final String JavaDoc PROP_ATTRS_CHANGED = "attrs"; // NOI18N
55

56     /** Set<TaskListener> */
57     protected EventListenerList JavaDoc listeners = new EventListenerList JavaDoc();
58
59     private boolean visitable;
60
61     private Task parent;
62
63     /** key shared by all clones */
64     private Object JavaDoc key;
65
66     /** If this item has subtasks, they are stored in this list */
67     private List JavaDoc subtasks = null;
68
69     /** Last unmodifiable subtasks copy distributed to clients. */
70     private List JavaDoc subtasksCopy;
71
72     /** When true, this item has been removed from a list.
73         The old list reference is still kept around so that
74         we can use it to search for a reincarnation of the task.
75
76         @deprecated duplicates isValid property
77      */

78     private boolean zombie = false;
79
80     public Task() {
81         super(null, null, null, null);
82         parent = null;
83         visitable = true;
84         key = new Object JavaDoc();
85     }
86
87     public Task(String JavaDoc desc, Task parent) {
88         super(null, null, desc, null);
89         this.parent = parent;
90         visitable = true;
91         key = new Object JavaDoc();
92     }
93
94     /**
95      * Searches for a task
96      *
97      * @param t task to be found
98      * @return index of the task or -1
99      */

100     public int indexOf(Task t) {
101         if (!hasSubtasks())
102             return -1;
103         return subtasks.indexOf(t);
104     }
105     
106     /**
107      * Removes all subtasks.
108      */

109     public void clear() {
110         if (hasSubtasks()) {
111             subtasks.clear();
112             subtasksCopy = null;
113             fireStructureChanged();
114         }
115     }
116
117     /**
118      * Returns indent level for this task. If parent == null returns 0
119      *
120      * @return indent level for this task
121      */

122     public int getLevel() {
123         Task t = getParent();
124         int level = 0;
125         while (t != null) {
126             level++;
127             t = t.getParent();
128         }
129         return level;
130     }
131
132     /**
133      * Set the description/summary of the task.
134      *
135      * @param ndesc The new description text
136      */

137     public void setSummary(String JavaDoc ndesc) {
138         super.setSummary(ndesc);
139     }
140
141     public void setDetails(String JavaDoc ndesc) {
142         super.setDetails(ndesc);
143     }
144
145     public void setPriority(SuggestionPriority priority) {
146         super.setPriority(priority);
147     }
148
149     /**
150      * @return true iff this task is "visitable"; returns true
151      * if this node has its own content, false if it's just a "category"
152      * node. Used for keyboard traversal: if you press Next (F12) you
153      * don't want it to skip over all nonvisitable nodes.
154      */

155     public boolean isVisitable() {
156         return visitable;
157     }
158
159     /**
160      * Set whether or not this task is "visitable".
161      *
162      * @param visitable true if this node has its own content, false
163      * if it's just a "category" node. Used for keyboard traversal: if
164      * you press Next (F12) you don't want it to skip over all
165      * nonvisitable nodes.
166      */

167     public void setVisitable(boolean visitable) {
168         this.visitable = visitable;
169     }
170
171     /**
172      * Fires a PropertyChangeEvent
173      *
174      * @param propertyName changed property
175      * @param oldValue old value (may be null)
176      * @param newValue new value (may be null)
177      */

178     protected void firePropertyChange(String JavaDoc propertyName, Object JavaDoc oldValue,
179     Object JavaDoc newValue) {
180         super.firePropertyChange(propertyName, oldValue, newValue);
181     }
182
183     public void addTaskListener(TaskListener l) {
184         if (LOGGER.isLoggable(Level.FINE))
185             Thread.dumpStack();
186         listeners.add(TaskListener.class, l);
187     }
188
189     public void removeTaskListener(TaskListener l) {
190         if (LOGGER.isLoggable(Level.FINE))
191             Thread.dumpStack();
192         listeners.remove(TaskListener.class, l);
193     }
194
195     protected final void fireStructureChanged() {
196         // Guaranteed to return a non-null array
197
Object JavaDoc[] l = listeners.getListenerList();
198         for (int i = l.length - 2; i >= 0; i -= 2) {
199             if (l[i] == TaskListener.class) {
200                 ((TaskListener) l[i+1]).structureChanged(this);
201             }
202         }
203
204         if (this instanceof TaskListener) {
205             ((TaskListener) this).structureChanged(this);
206         }
207     }
208
209     /**
210      * Fires an addedTask event
211      *
212      * @param t task that was added
213      */

214     protected final void fireAddedTask(Task t) {
215         // Guaranteed to return a non-null array
216
Object JavaDoc[] l = listeners.getListenerList();
217         for (int i = l.length - 2; i >= 0; i -= 2) {
218             if (l[i] == TaskListener.class) {
219                 ((TaskListener) l[i+1]).addedTask(t);
220             }
221         }
222         if (this instanceof TaskListener) {
223             ((TaskListener) this).addedTask(t);
224         }
225     }
226
227
228     /**
229      * Fires an removedTask event
230      *
231      * @param t task that was removed
232      * @param index old index of the task
233      */

234     protected final void fireRemovedTask(Task t, int index) {
235         // Guaranteed to return a non-null array
236
Object JavaDoc[] l = listeners.getListenerList();
237         for (int i = l.length - 2; i >= 0; i -= 2) {
238             if (l[i] == TaskListener.class) {
239                 ((TaskListener) l[i+1]).removedTask(this, t, index);
240             }
241         }
242
243         if (this instanceof TaskListener) {
244             ((TaskListener) this).removedTask(this, t, index);
245         }
246     }
247     
248     protected void recursivePropertyChange() {
249         firePropertyChange(PROP_ATTRS_CHANGED, null, null);
250         if (subtasks != null) {
251             Iterator it = getSubtasks().iterator();
252             while (it.hasNext()) {
253                 Task item = (Task)it.next();
254                 item.recursivePropertyChange();
255             }
256         }
257     }
258
259     /**
260      * Returns subtasks of this task
261      *
262      * @todo all usages require iterator() or size() calls only, so it could be replaced by
263      * subtasksIterator and subtasksCount methods. Add TLUtil.iteratorToCollection.
264      * @return children never null
265      */

266     public final List JavaDoc getSubtasks() {
267         if (subtasks == null) {
268             return Collections.EMPTY_LIST;
269         } else {
270             // #48953 clone it for iterators safeness
271
if (subtasksCopy == null) {
272                 synchronized (subtasks) {
273                     subtasksCopy = Collections.unmodifiableList(
274                             new ArrayList(subtasks));
275                 }
276             }
277             return subtasksCopy;
278         }
279     }
280
281     /**
282      * Create subtasks iterator. It's remove method is not
283      * supported yet. You need to call removeTask().
284      *
285      * @return non-recursive subtask iterator
286      */

287     public final Iterator subtasksIterator() { // in JRE 1.5 could be turned to Iterable by renaming to Iterator<T> iterator().
288
return getSubtasks().iterator();
289     }
290
291     /**
292      * Returns the number of subtasks.
293      *
294      * @return subtasks count
295      */

296     public final int subtasksCount() {
297         if (subtasks == null) {
298             return 0;
299         } else {
300             return subtasks.size();
301         }
302     }
303
304     /**
305      * @return true if task exits in non-recursive subtasks.
306      */

307     public final boolean containsSubtask(Task task) {
308         if (subtasks == null) {
309             return false;
310         } else {
311             return subtasks.contains(task);
312         }
313     }
314
315     /**
316      * Add subtask to this task. The task will be prepended
317      * to the task list.
318      *
319      * @param subtask task to be added as a subtask, to the front
320      * of the list.
321      */

322     public void addSubtask(Task subtask) {
323         addSubtask(subtask, false);
324     }
325
326     /**
327      * Add subtask in a particular place in the parent's
328      * subtask list
329      *
330      * @param subtask The subtask to be added
331      * @param after The task which will be immediately before
332      * the new subtask after the addition (e.g. add
333      * this subtask directly AFTER the specified
334      * task)
335      */

336     public void addSubtask(Task subtask, Task after) {
337         subtask.parent = this;
338         if (subtasks == null) {
339             // Internal error - shouldn't call this unless you already have a subtask "after")
340
ErrorManager.getDefault().log("addSubtask(subtask,after) called where subtasks==null"); // NOI18N
341
return;
342         }
343         int pos = subtasks.indexOf(after);
344         subtasks.add(pos+1, subtask);
345         subtasksCopy = null;
346         fireAddedTask(subtask);
347     }
348
349     /** Add a list of subtasks to this task.
350      * @param subtasks The tasks to add
351      * @param append When true, append to the list, otherwise prepend. Ignored
352      * if after is not null.
353      * @param after The task which will be immediately before
354      * the new subtask after the addition (e.g. add
355      * this subtask directly AFTER the specified
356      * task). Overrides the append parameter.
357     */

358     public void addSubtasks(List JavaDoc tasks, boolean append, Task after) {
359         ListIterator it = tasks.listIterator();
360         while (it.hasNext()) {
361             Task task = (Task)it.next();
362             task.parent = this;
363         }
364
365         if (subtasks == null) {
366             subtasks = Collections.synchronizedList(new LinkedList());
367         }
368         if (after != null) {
369             int pos = subtasks.indexOf(after);
370             subtasks.addAll(pos+1, tasks);
371         } else if (append) {
372             subtasks.addAll(tasks);
373         } else {
374             subtasks.addAll(0, tasks);
375         }
376         subtasksCopy = null;
377         fireStructureChanged();
378     }
379
380    /**
381     * Add a subtask to this task.
382     * @param append When true, add to the end of the list of subtasks instead
383     * of the beginning.
384     */

385     public void addSubtask(Task subtask, boolean append) {
386         subtask.parent = this;
387         if (subtasks == null) {
388             subtasks = Collections.synchronizedList(new LinkedList());
389         }
390
391         // XXX does not work with SuggetionList.addCategory:152
392
// assert !subtasks.contains(subtask);
393
if (subtasks.contains(subtask)) return;
394
395         if (append) {
396             subtasks.add(subtask);
397         } else {
398             subtasks.add(0, subtask);
399         }
400         subtasksCopy = null;
401         fireAddedTask(subtask);
402     }
403
404     /**
405      * Remove a particular subtask
406      *
407      * @param subtask The subtask to be removed
408      */

409     public void removeSubtask(Task subtask) {
410         // We need the list reference later, when looking for a reincarnation
411
// of the task. So instead use the zombie field to mark deleted items.
412
subtask.zombie = true;
413         if (subtasks == null) {
414             return;
415         }
416         int index = subtasks.indexOf(subtask);
417         subtasks.remove(index);
418         if (subtasks.size() == 0) {
419             subtasks = null;
420         }
421
422         subtasksCopy = null;
423         fireRemovedTask(subtask, index);
424     }
425
426     /**
427      * Indicate whether or not this task has any subtasks
428      * @return true iff the item has any subtasks
429      */

430     public final boolean hasSubtasks() {
431         return ((subtasks != null) && (subtasks.size() != 0));
432     }
433
434     public final Task getParent() {
435         return parent;
436     }
437
438     /** Traverse to root task (or self)*/
439     public final Task getRoot() {
440         Task parent = getParent();
441         if (parent != null) {
442             return parent.getRoot();
443         } else {
444             return this;
445         }
446     }
447
448
449     /** Determines whether given task lies in this context. */
450     public final boolean isParentOf(Task task) {
451         if (task.getKey() == getKey()) return true;
452         Task nextLevel = task.getParent();
453         if (nextLevel == null) return false;
454         return isParentOf(nextLevel); // recursion
455
}
456
457     /**
458      * Indicate if this item is a "zombie" (e.g. it has been removed
459      * from a tasklist. The list it was removed from is still pointed to
460      * by the list field. See the Suggestion module's FixAction for an
461      * example of why this is useful.
462      */

463     public boolean isZombie() {
464         return zombie;
465     }
466
467     /**
468      * Write a TodoItem to a text stream. NOT DONE.
469      * @param item The task to write out
470      * @param w The writer to write the string to
471      * @throws IOException Not thrown explicitly by this code, but perhaps
472      * by the call it makes to w's write() method
473      *
474      * @todo Finish the implementation here such that it
475      * writes out all the fields, not just the
476      * description.
477      */

478     public static void generate(Task item, Writer JavaDoc w) throws IOException JavaDoc {
479     w.write(item.getSummary());
480     }
481
482     /**
483      * Parse a task from a text stream.
484      *
485      * @param r The reader to read the task from
486      * @throws IOException Not thrown directly by this method, but
487      * possibly by r's read() method which it calls
488      * @return A new task object which represents the
489      * data read from the reader
490      * @todo Finish the implementation
491      * @see generate
492      */

493     public static Task parse(Reader JavaDoc r) throws IOException JavaDoc {
494         LOGGER.fine("parsing");
495
496         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(r);
497         //List notes = new LinkedList(); // List<Note>
498
String JavaDoc line;
499         while ((line = reader.readLine()) != null) {
500             // XXX TodoTransfer's convert
501
// method never seems to get called (see explanations in
502
// TaskNode.clipboardCopy), so I haven't been
503
// able to test this, that's why I haven't expanded the
504
// code as much as it should be.
505
Task item = new Task();
506             item.setSummary(line);
507             return item;
508         }
509         return null;
510     }
511
512
513     /**
514      * Counts all subtasks of this task recursively.
515      *
516      * @return number of subtasks
517      */

518     public int getSubtaskCountRecursively() {
519         if(subtasks == null) return 0;
520
521         int n = 0;
522         synchronized(subtasks) {
523             Iterator it = subtasks.iterator();
524             while(it.hasNext()) {
525                 Task t = (Task) it.next();
526                 n += t.getSubtaskCountRecursively() + 1;
527             }
528             return n;
529         }
530     }
531
532     /**
533      * Create default nodes for this item.
534      * Actual view may use them or use any replacement.
535      */

536     public Node[] createNode() {
537         //if (hasSubtasks()) {
538
if (subtasks != null) { // Want to make root a non-leaf; empty list, not null
539
return new Node[] {new TaskNode(this, new TaskChildren(this))};
540         } else {
541             return new Node[] {new TaskNode(this)};
542         }
543     }
544
545     /**
546      * Create an identical copy of a task (a deep copy, e.g. the
547      * list of subtasks will be cloned as well
548      */

549     protected Object JavaDoc clone() {
550         Task t = new Task();
551         t.copyFrom(this);
552         return t;
553     }
554
555     /**
556      * Returns a key shared by all task clones.
557      */

558     public final Object JavaDoc getKey() {
559         return key;
560     }
561
562     /**
563      * Get the provider. Not defined for tasks - will be subclassed
564      * in SuggestionImpl but we don't want Task to be abstract...
565      */

566     public Object JavaDoc getSeed() {
567          return null;
568     }
569
570
571     /** Copy all the fields in the given task into this object.
572         Should only be called on an object of the EXACT same type.
573         Thus, if you're implementing a subclass of Task, say
574         UserTask, you can implement copy assuming that the passed
575         in Task parameter is of type UserTask. When overriding,
576         remember to call super.copy.
577         <p>
578         Make a deep copy - except when that doesn't make sense.
579         For example, you can share the same icon reference.
580         And in particular, the tasklist reference should be the same.
581         But the list of subitems should be unique. You get the idea.
582     */

583     protected void copyFrom(Task from) {
584         visitable = from.visitable;
585         zombie = from.zombie;
586
587         assert from.key != null;
588         key = from.key;
589
590         // Copy fields from the parent implementation
591
super.setSummary(from.getSummary());
592         super.setPriority(from.getPriority());
593         super.setIcon(from.getIcon());
594         super.setType(from.getType());
595         super.setLine(from.getLine());
596         super.setAction(from.getAction());
597         super.setDetails(from.getDetails());
598
599         // Copying the parent reference may seem odd, since for children
600
// it should be changed - but this only affects the root node.
601
// For children nodes, we override the parent reference after
602
// cloning the child.
603
parent = from.parent;
604
605         // Copy the subtasks reference
606

607         // XXX
608
// Please note -- I'm NOT copying the universal id, these have to
609
// be unique, even for copies
610
if (from.subtasks != null) {
611             synchronized(from.subtasks) {
612                 Iterator it = from.subtasks.iterator();
613                 subtasks = Collections.synchronizedList(new LinkedList());
614                 while (it.hasNext()) {
615                     Task task = (Task)it.next();
616                     Task mycopy = (Task)task.clone();
617                     mycopy.parent = this;
618                     subtasks.add(mycopy);
619                 }
620                 subtasksCopy = null;
621             }
622         }
623     }
624 }
625
626
Popular Tags