KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > usertasks > model > UserTask


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  * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tasklist.usertasks.model;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.io.BufferedReader JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.Reader JavaDoc;
28 import java.io.Serializable JavaDoc;
29 import java.io.Writer JavaDoc;
30 import java.net.InetAddress JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.net.UnknownHostException JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Calendar JavaDoc;
35 import java.util.Date JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.ResourceBundle JavaDoc;
39 import javax.swing.tree.TreePath JavaDoc;
40 import org.netbeans.modules.tasklist.usertasks.util.UTListTreeAbstraction;
41 import org.netbeans.modules.tasklist.usertasks.util.UTUtils;
42 import org.openide.filesystems.FileObject;
43 import org.openide.filesystems.URLMapper;
44 import org.openide.nodes.Node.Cookie;
45 import org.openide.text.Line;
46 import org.openide.util.NbBundle;
47 import org.netbeans.modules.tasklist.core.util.ObjectList;
48 import org.netbeans.modules.tasklist.core.util.ObjectListEvent;
49 import org.netbeans.modules.tasklist.core.util.ObjectListListener;
50 import org.netbeans.modules.tasklist.usertasks.annotations.UTAnnotation;
51 import org.netbeans.modules.tasklist.usertasks.util.UnaryFunction;
52
53
54 /**
55  * Class which represents a task in the
56  * tasklist.
57  *
58  * @author Tor Norbye
59  * @author Trond Norbye
60  * @author tl
61  */

62 public final class UserTask implements Cloneable JavaDoc, Cookie,
63         PropertyChangeListener JavaDoc {
64     /**
65      * A period spent working on a task.
66      */

67     public static class WorkPeriod implements Comparable JavaDoc {
68         private long start;
69         private int duration;
70         
71         /**
72          * Start
73          *
74          * @param start start point as returned from System.currentTimeMillis()
75          * @param duration duration in minutes
76          */

77         public WorkPeriod(long start, int duration) {
78             this.start = start;
79             this.duration = duration;
80         }
81
82         public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
83             return new WorkPeriod(start, duration);
84         }
85         
86         /**
87          * Checks whether this period intersects another one.
88          *
89          * @param wp a work period
90          * @return true = two periods intersect
91          */

92         public boolean intersects(WorkPeriod wp) {
93             long wpend = wp.start + wp.duration * 60 * 1000;
94             long end = start + duration * 60 * 1000;
95             return (duration + wp.duration) * 60 * 1000 >
96                     Math.max(end, wpend) - Math.min(start, wp.start);
97         }
98         
99         /**
100          * Returns the starting point of this period.
101          *
102          * @return time as returned by System.currentTimeMillis()
103          */

104         public long getStart() {
105             return start;
106         }
107         
108         /**
109          * Returns the duration of this period.
110          *
111          * @return duration in minutes
112          */

113         public int getDuration() {
114             return duration;
115         }
116         
117         /**
118          * Changes the duration.
119          *
120          * @param dur new duration in minutes
121          */

122         public void setDuration(int dur) {
123             this.duration = dur;
124         }
125         
126         /**
127          * Test whether this period was started today.
128          *
129          * @return true = yes
130          */

131         public boolean isToday() {
132             Calendar JavaDoc today = Calendar.getInstance();
133             today.set(Calendar.HOUR_OF_DAY, 0);
134             today.set(Calendar.MINUTE, 0);
135             today.set(Calendar.SECOND, 0);
136             today.set(Calendar.MILLISECOND, 0);
137             return today.getTimeInMillis() <= start;
138         }
139     
140         public int compareTo(Object JavaDoc obj) {
141             if (!(obj instanceof WorkPeriod))
142                 return -1;
143             WorkPeriod wp = (WorkPeriod) obj;
144             if (this.start < wp.start)
145                 return -1;
146             else if (this.start > wp.start)
147                 return 1;
148             else
149                 return 0;
150         }
151     }
152     
153     /** Highest priority */
154     public static final int HIGH = 1;
155
156     /** Normal/default priority */
157     public static final int MEDIUM_HIGH = 2;
158
159     /** Normal/default priority */
160     public static final int MEDIUM = 3;
161
162     /** Normal/default priority */
163     public static final int MEDIUM_LOW = 4;
164
165     /** Lowest priority */
166     public static final int LOW = 5;
167     
168     /**
169      * Priority values that correspond to the values returned by
170      * getPriorityNames
171      */

172     public static final int[] PRIORITY_VALUES = {
173         HIGH,
174         MEDIUM_HIGH,
175         MEDIUM,
176         MEDIUM_LOW,
177         LOW
178     };
179     
180     /** Keys for the Bundle.properties */
181     private static final String JavaDoc[] PRIORITIES_KEYS = {
182         "PriorityHigh", // NOI18N
183
"PriorityMediumHigh", // NOI18N
184
"PriorityMedium", // NOI18N
185
"PriorityMediumLow", // NOI18N
186
"PriorityLow" // NOI18N
187
};
188
189     /** Names for priorities */
190     private static String JavaDoc[] PRIORITIES;
191
192     static {
193         PRIORITIES = new String JavaDoc[PRIORITIES_KEYS.length];
194         ResourceBundle JavaDoc rb = NbBundle.getBundle(UserTask.class);
195         for (int i = 0; i < PRIORITIES_KEYS.length; i++) {
196             PRIORITIES[i] = rb.getString(PRIORITIES_KEYS[i]);
197         }
198     }
199     
200     /**
201      * Returns priority text for the specified value.
202      *
203      * @param p priority
204      */

205     public static String JavaDoc getPriorityName(int p) {
206         return PRIORITIES[p - 1];
207     }
208     
209     /**
210      * Reduces a list of tasks in such a way that none of tasks is an
211      * ancestor of another.
212      *
213      * @param tasks user tasks from one task list. None of the element
214      * could be included twice.
215      * @return reduced array
216      */

217     public static UserTask[] reduce(UserTask[] tasks) {
218         List JavaDoc<UserTask> res = new ArrayList JavaDoc<UserTask>();
219         for (int i = 0; i < tasks.length; i++) {
220             boolean ok = true;
221             for (int j = 0; j < tasks.length; j++) {
222                 if (j != i) {
223                     if (tasks[j].isAncestorOf(tasks[i])) {
224                         ok = false;
225                         break;
226                     }
227                 }
228             }
229             if (ok)
230                 res.add(tasks[i]);
231         }
232         return res.toArray(new UserTask[res.size()]);
233     }
234     
235     /**
236      * Returns a priority for the specified localized text.
237      *
238      * @param name localized priority text like "high"
239      * @return priority value or -1 if not found
240      */

241     public static int getPriority(String JavaDoc name) {
242         for (int i = 0; i < PRIORITIES.length; i++) {
243             if (PRIORITIES[i].equals(name))
244                 return PRIORITY_VALUES[i];
245         }
246         return -1;
247     }
248     
249     /**
250      * Returns localized names for priorities
251      *
252      * @return [0] - high, [1] - medium-high, ...
253      */

254     public static String JavaDoc[] getPriorityNames() {
255         return PRIORITIES;
256     }
257
258     private final PropertyChangeSupport JavaDoc supp = new PropertyChangeSupport JavaDoc(this);
259
260     /** Id of bound summary property. */
261     public static final String JavaDoc PROP_SUMMARY = "summary"; // NOI18N
262

263     /** Id of bound icon property. */
264     public static final String JavaDoc PROP_ICON = "icon"; // NOI18N
265

266     /** Id of bound details property. */
267     public static final String JavaDoc PROP_DETAILS = "details"; // NOI18N
268

269     /** Id of bound priority property. */
270     public static final String JavaDoc PROP_PRIORITY = "priority"; // NOI18N
271

272     /** Id of bound "valid" property. */
273     public static final String JavaDoc PROP_VALID = "valid"; // NOI18N
274

275     /** URL property. java.net.URL */
276     public static final String JavaDoc PROP_URL = "url"; // NOI18N
277

278     /** line number property. Integer */
279     public static final String JavaDoc PROP_LINE_NUMBER = "lineNumber"; // NOI18N
280

281     /** property name for the associated line object (org.openide.text.Line) */
282     public static final String JavaDoc PROP_LINE = "line"; // NOI18N
283

284     public static final String JavaDoc PROP_DUE_DATE = "dueDate"; // NOI18N
285
public static final String JavaDoc PROP_CATEGORY = "category"; // NOI18N
286
public static final String JavaDoc PROP_PROGRESS = "progress"; // NOI18N
287
public static final String JavaDoc PROP_EFFORT = "effort"; // NOI18N
288
public static final String JavaDoc PROP_REMAINING_EFFORT = "remainingEffort"; // NOI18N
289
public static final String JavaDoc PROP_SPENT_TIME = "spentTime"; // NOI18N
290
public static final String JavaDoc PROP_OWNER = "owner"; // NOI18N
291
public static final String JavaDoc PROP_COMPLETED_DATE = "completedDate"; // NOI18N
292
public static final String JavaDoc PROP_WORK_PERIODS = "workPeriods"; // NOI18N
293
public static final String JavaDoc PROP_START = "start"; // NOI18N
294
public static final String JavaDoc PROP_SPENT_TIME_TODAY = "spentTimeToday"; // NOI18N
295

296     /** last modified. long as returned by System.currentTimeMillis() */
297     public static final String JavaDoc PROP_LAST_EDITED_DATE =
298             "lastEditedDate"; // NOI18N
299

300     /** see setValuesComputed */
301     public static final String JavaDoc PROP_VALUES_COMPUTED =
302             "valuesComputed"; // NOI18N
303

304     // ATTENTION: if you add new fields here do not forget to update copyFrom
305

306     /**
307      * When false, don't update the "last modified" date.
308      * Used by the restore code.
309      */

310     private boolean updateLastModified = true;
311
312     private UserTask parent;
313
314     /** If this item has subtasks, they are stored in this list */
315     private UserTaskObjectList subtasks;
316
317     /**
318      * Used to create uid's. May be expensive to compute, so only do
319      * it once.
320      */

321     private static String JavaDoc domain = null;
322     
323     /** Used to create uid's. Assign unique id's for this session. */
324     private static int unique = 0;
325     
326     /** A summary (one-line description) of the task */
327     private String JavaDoc summary = null;
328
329     /** A (possibly) multi-line summary of the task */
330     private String JavaDoc details = null;
331     
332     /** The priority of this suggestion, defaults to SuggestionPriority.MEDIUM */
333     private int priority = UserTask.MEDIUM;
334
335     /**
336      * this value is used by ICalImport/ExportFormat to store additional
337      * not editable parameters
338      */

339     public Object JavaDoc userObject;
340     
341     /** may be null if this task was removed from the list */
342     private UserTaskList list;
343     
344     private String JavaDoc uid;
345     
346     /**
347      * 0..100 - value in percents
348      */

349     private float progress = 0.0f;
350     
351     /**
352      * true means progress will be computed automatically as a weighted average
353      * of the subtasks. If a task has no children it means 0%
354      * true means that the effort will be computed automatically as them sum of the
355      * subtask efforts. If a task has no children it means 0
356      */

357     private boolean valuesComputed = false;
358     
359     private Date JavaDoc dueDate;
360     private boolean dueAlarmSent;
361     
362     private String JavaDoc category;
363     private long created;
364     private long lastEditedDate;
365
366     /** in minutes */
367     private int effort = 60;
368     
369     /** Time spent on this task */
370     private int spentTime = 0;
371     
372     private PropertyChangeListener JavaDoc lineListener;
373
374     // <editor-fold defaultstate="collapsed" desc="These 4 attributes should be used/updated together: url, annotation, line, linenumber">
375
/** annotation for this task. != null if line != null */
376     private UTAnnotation annotation = null;
377     
378     /** URL associated with this task. */
379     private URL JavaDoc url = null;
380     
381     /** The line position associated with the task */
382     private Line line = null;
383     
384     /**
385      * 0, 1, 2, 3, ...
386      * -1 = no line information
387      */

388     private int linenumber = -1;
389     // </editor-fold>
390

391     private ObjectList<Dependency> dependencies = new ObjectList<Dependency>();
392     private String JavaDoc owner = ""; // NOI18N
393
private long completedDate = 0;
394     
395     /**
396      * Start date/time for the task as returned by System.currentTimeMillis or -1
397      * if undefined
398      */

399     private long start = -1;
400     
401     // <WorkPeriod>
402
private ObjectList<WorkPeriod> workPeriods = new ObjectList<WorkPeriod>();
403     // ATTENTION: if you add new fields here do not forget to update copyFrom
404

405     /**
406      * Creates a task with the specified description
407      *
408      * @param summary description
409      * @param list task list that this task belongs to
410      */

411     public UserTask(String JavaDoc summary, UserTaskList list) {
412         this(summary, false, 3, "", "", null, list); // NOI18N
413
}
414     
415     /**
416      *
417      * Construct a new task with the given parameters.
418      *
419      * @param summary description
420      * @param done true = the task is done
421      * @param priority 1..5 (High..Low)
422      * @param details details
423      * @param category task's category ("" - no category)
424      * @param parent parent task
425      * @param list task list that this task belongs to
426      */

427     public UserTask(String JavaDoc summary, boolean done, int priority, String JavaDoc details,
428     String JavaDoc category, UserTask parent, UserTaskList list) {
429         this.summary = summary;
430         this.parent = parent;
431         
432         assert priority >= 1 && priority <= 5 : "priority ?"; // NOI18N
433
assert summary != null : "desc == null"; // NOI18N
434
assert details != null : "details == null"; // NOI18N
435
assert category != null : "category == null"; // NOI18N
436

437         workPeriods.addListener(new ObjectListListener() {
438             public void listChanged(ObjectListEvent event) {
439                 firePropertyChange(PROP_WORK_PERIODS, null, null);
440             }
441         });
442         
443         lineListener = new PropertyChangeListener JavaDoc() {
444             public void propertyChange(PropertyChangeEvent JavaDoc e) {
445                 if (e.getPropertyName() == Line.PROP_LINE_NUMBER) {
446                     firePropertyChange("lineNumber", // NOI18N
447
e.getOldValue(), e.getNewValue());
448                 }
449             }
450         };
451         
452         subtasks = new UserTaskObjectList(this);
453         subtasks.addListener(new ObjectListListener() {
454             public void listChanged(ObjectListEvent ev) {
455                 switch (ev.getType()) {
456                     case ObjectListEvent.EVENT_ADDED:
457                         structureChanged();
458                         break;
459                     case ObjectListEvent.EVENT_REMOVED: {
460                         structureChanged();
461                         break;
462                     }
463                     case ObjectListEvent.EVENT_REORDERED:
464                         structureChanged();
465                         break;
466                     case ObjectListEvent.EVENT_STRUCTURE_CHANGED:
467                         structureChanged();
468                         break;
469                     default:
470                         throw new InternalError JavaDoc("unexpected event type"); // NOI18N
471
}
472             }
473             public void structureChanged() {
474                 if (isValuesComputed()) {
475                     setProgress_(computeProgress());
476                     setEffort_(computeEffort());
477                     setSpentTime_(computeSpentTime());
478                 }
479                 if (UserTask.this.list != null) {
480                     UserTask.this.list.fireChange();
481                 }
482             }
483         });
484     
485         this.list = list;
486         
487         if (done)
488             setDone(true);
489         
490         setPriority(priority);
491
492         this.category = category;
493         
494         setDetails(details);
495
496         created = System.currentTimeMillis();
497         lastEditedDate = created;
498
499     if (domain == null) {
500             try {
501                 InetAddress JavaDoc address = InetAddress.getLocalHost();
502                 domain = address.toString();
503             } catch (UnknownHostException JavaDoc e) {
504                 domain = "unknown"; // NOI18N
505
}
506     }
507         
508         // XXX Later, come up with a better time stamp, e.g. use DateFormat
509
String JavaDoc timestamp = Long.toString(System.currentTimeMillis());
510         
511     // uid = "nb" + timestamp + "." + (unique++) + "@" + domain;
512
uid = new StringBuffer JavaDoc(50).append("nb").append(timestamp). // NOI18N
513
append('.').append(unique++).append('@').append(domain).toString();
514
515         addPropertyChangeListener(this);
516     }
517
518     /**
519      * Merges
520      *
521      * @param wps sorted array
522      */

523     private void merge(ObjectList<WorkPeriod> wps) {
524         for (int i = 0; i < wps.size() - 1; i++) {
525             WorkPeriod a = wps.get(i);
526             WorkPeriod b = wps.get(i + 1);
527             if (a.intersects(b)) {
528                 
529             }
530         }
531     }
532     
533     /**
534      * Returns work periods for this task.
535      *
536      * @return <WorkPeriod>
537      */

538     public ObjectList<WorkPeriod> getWorkPeriods() {
539         /*if (isValuesComputed()) {
540             ObjectList r = new ObjectList<WorkPeriod>();
541             UserTaskObjectList st = getSubtasks();
542             for (int i = 0; i < st.size(); i++) {
543                 r.addAll(st.getUserTask(i).getWorkPeriods());
544             }
545             Collections.sort(r);
546             merge(r);
547             return r;
548         } else { TODO: future implementation */

549             return workPeriods;
550         /*}*/
551     }
552     
553     /**
554      * Returns the list where this task is contained.
555      *
556      * @return the list or null if this task is not in a UserTaskList
557      */

558     public UserTaskObjectList getParentObjectList() {
559         if (getParent() != null)
560             return getParent().getSubtasks();
561         else if (getList() != null)
562             return getList().getSubtasks();
563         else
564             return null;
565     }
566     
567     /**
568      * Returns the path to this task starting with the user task list.
569      *
570      * @return path to this task
571      */

572     public TreePath JavaDoc getPathTo() {
573         List JavaDoc<Object JavaDoc> l = new ArrayList JavaDoc<Object JavaDoc>(10);
574         UserTask t = this;
575         while (t != null) {
576             l.add(0, t);
577             t = t.getParent();
578         }
579         l.add(0, getList());
580         return new TreePath JavaDoc(l.toArray());
581     }
582     
583     /**
584      * Moves this task up in the parent's list
585      */

586     public void moveUp() {
587         UserTaskObjectList list;
588         if (getParent() == null)
589             list = this.list.getSubtasks();
590         else
591             list = this.parent.getSubtasks();
592         int index = list.indexOf(this);
593         list.move(index, index - 1);
594     }
595     
596     /**
597      * Moves this task down in the parent's list
598      */

599     public void moveDown() {
600         UserTaskObjectList list;
601         if (getParent() == null)
602             list = this.list.getSubtasks();
603         else
604             list = this.parent.getSubtasks();
605         int index = list.indexOf(this);
606         list.move(index, index + 1);
607     }
608     
609     /**
610      * Returns children of this task
611      *
612      * @return children of this task
613      */

614     public UserTaskObjectList getSubtasks() {
615         return subtasks;
616     }
617     
618     /**
619      * Returns the list this task belongs to.
620      *
621      * @return task list or null if this task was removed from the list.
622      */

623     public UserTaskList getList() {
624         return list;
625     }
626     
627     /**
628      * Sets the list this task belongs to.
629      *
630      * @param list user task list
631      */

632     void setList(UserTaskList list) {
633         this.list = list;
634     }
635     
636     /**
637      * Goes up the hierarchy of tasks and checks whether this task is an
638      * ancestor of <code>another</code>. Returns true if
639      * <code>another == this</code>
640      *
641      * @param another the task that should be tested
642      * @return true = ancestor
643      */

644     public boolean isAncestorOf(UserTask another) {
645         while (another != null) {
646             if (another == this)
647                 return true;
648             another = another.getParent();
649         }
650         return false;
651     }
652     
653     /**
654      * Returns how long it took to complete this task.
655      *
656      * @return duration in minutes >= 0
657      */

658     public int getSpentTime() {
659         return spentTime;
660     }
661     
662     /**
663      * Changes the duration of this task
664      *
665      * @param duration new value in minutes
666      */

667     public void setSpentTime(int spentTime) {
668         assert spentTime >= 0;
669         if (valuesComputed)
670             setValuesComputed(false);
671     
672         setSpentTime_(spentTime);
673
674         if (isValuesComputed() && getSubtasks().size() == 0)
675             setProgress_(computeProgress());
676     }
677     
678     /**
679      * Setter for property spentTime. This method does not check the
680      * spentTimeComputed property and so it could be used from setSpentTimeComputed()
681      *
682      * @param spentTime New value of property spentTime in minutes.
683      */

684     private void setSpentTime_(int spentTime) {
685         int old = this.spentTime;
686         
687         if (this.spentTime != spentTime) {
688             this.spentTime = spentTime;
689             firePropertyChange("spentTime", // NOI18N
690
new Integer JavaDoc(old), new Integer JavaDoc(spentTime));
691             if (getParent() != null) {
692                 UserTask p = (UserTask) getParent();
693                 if (p.isValuesComputed())
694                     p.setSpentTime_(p.computeSpentTime());
695             }
696         }
697     }
698     
699     /**
700      * Sets whether the spent time, progress and effort of this task
701      * should be computed
702      *
703      * @param v true = the spent time, progress and effort will be computed
704      */

705     public void setValuesComputed(boolean v) {
706         if (this.valuesComputed != v) {
707             if (isStarted())
708                 stop();
709             this.valuesComputed = v;
710             firePropertyChange(PROP_VALUES_COMPUTED,
711                     Boolean.valueOf(!v), Boolean.valueOf(v));
712             if (v) {
713                 setSpentTime_(computeSpentTime());
714                 setProgress_(computeProgress());
715                 setEffort_(computeEffort());
716                 /* TODO: future implementation workPeriods.clear(); */
717             }
718         }
719     }
720     
721     /**
722      * Getter for property valuesComputed.
723      *
724      * @return true = the spent time, effort and progress
725      * will be computed as the sum of the subtask values
726      */

727     public boolean isValuesComputed() {
728         return valuesComputed;
729     }
730     
731     /**
732      * Computes "spentTime" property as the sum of the subtask times.
733      * This method should only be called if spentTimeComputed == true
734      *
735      * This method is used in tests that is why it's package private
736      *
737      * @return spent time in minutes
738      */

739     int computeSpentTime() {
740         assert valuesComputed;
741
742         int sum = 0;
743         Iterator JavaDoc it = getSubtasks().iterator();
744         while (it.hasNext()) {
745             UserTask child = (UserTask) it.next();
746             sum += child.getSpentTime();
747         }
748         return sum;
749     }
750
751     /**
752      * Start to work on this task
753      */

754     public void start() {
755         StartedUserTask.getInstance().start(this);
756     }
757     
758     /**
759      * Stops this task
760      */

761     public void stop() {
762         assert isStarted();
763         StartedUserTask.getInstance().start(null);
764     }
765     
766     /**
767      * Is this task currently running?
768      *
769      * @return true = this is the currently running task
770      */

771     public boolean isStarted() {
772         return StartedUserTask.getInstance().getStarted() == this;
773     }
774     
775     /**
776      * Test whether all tasks that this one depends on are done.
777      *
778      * @return all dependencies are done
779      */

780     public boolean areDependenciesDone() {
781         List JavaDoc deps = this.getDependencies();
782         for (int i = 0; i < deps.size(); i++) {
783             Dependency d = (Dependency) deps.get(i);
784             if (d.getType() == Dependency.END_BEGIN) {
785                 if (!d.getDependsOn().isDone())
786                     return false;
787             }
788         }
789         return true;
790     }
791     
792     /**
793      * Checks whether this task could be started. This method also could
794      * return true for a task that is currently running.
795      *
796      * @return true = this task could be started
797      */

798     public boolean isStartable() {
799         return !isValuesComputed() && !isDone() && areDependenciesDone();
800     }
801     
802     /**
803      * Was the due alarm for this task already sent?
804      *
805      * @return true = yes
806      */

807     public boolean isDueAlarmSent() {
808         return dueAlarmSent;
809     }
810     
811     /**
812      * Sets the "due alarm sent" flag.
813      *
814      * @param flag true = due alarm was already sent
815      */

816     public void setDueAlarmSent(boolean flag) {
817         boolean old = this.dueAlarmSent;
818         dueAlarmSent = flag;
819         firePropertyChange("dueAlarmSent", Boolean.valueOf(old), // NOI18N
820
Boolean.valueOf(dueAlarmSent));
821     }
822     
823     /**
824      * Get the "Deadline" for this task
825      *
826      * @return the "deadline" or null
827      */

828     public Date JavaDoc getDueDate() {
829         return dueDate;
830     }
831     
832     /**
833      * Set the "Deadline" for this task
834      *
835      * @param d the "deadline"
836      */

837     public void setDueDate(Date JavaDoc d) {
838         Date JavaDoc old = this.dueDate;
839         if (d != null) {
840             if (!d.equals(dueDate)) {
841                 dueAlarmSent = false;
842             }
843         } else {
844             if (dueDate != null) {
845                 dueAlarmSent = false;
846             }
847         }
848         dueDate = d;
849         firePropertyChange("dueDate", old, dueDate); // NOI18N
850
}
851     
852     /**
853      * get the "deadline" for this task
854      *
855      * @return "deadline" for this task, Long.MAX_VALUE == no due time
856      */

857     public long getDueTime() {
858         long ret = Long.MAX_VALUE;
859         if (dueDate != null) {
860             ret = dueDate.getTime(); // getNextDueDate();
861
}
862         return ret;
863     }
864
865     /**
866      * Check if this task is due. A task is considered
867      * due if its due time has already passed, or will
868      * occur in the next 36 hours (that will usually
869      * be roughly "today or tomorrow")
870      * <p>
871      * @param when Date when we want to know if the date is due.
872      * You'll probably want to pass in a date corresponding
873      * to now.
874      */

875     boolean isDue(Date JavaDoc when) {
876          if (dueDate == null) {
877              return false;
878          }
879          long due = dueDate.getTime();
880          long now = when.getTime();
881          return (due < (now + (36 * 60 * 60 * 1000)));
882          // number of milliseconds in 36 hours:
883
// 36 hours * 60 minutes * 60 * seconds * 1000 milliseconds
884
}
885     
886     /**
887      * The UID (Unique Identifier) for this item. See RFC 822 and RFC 2445.
888      *
889      * @return unique ID
890      */

891     public String JavaDoc getUID() {
892         return uid;
893     }
894     
895     public void setUID(String JavaDoc nuid) {
896         String JavaDoc old = this.uid;
897         uid = nuid;
898         
899         firePropertyChange("UID", old, this.uid); // NOI18N
900
}
901     
902     /**
903      * Indicate if the task is done
904      *
905      * @return true iff percents complete equals 100
906      */

907     public boolean isDone() {
908         return Math.abs(100.0f - getProgress()) < 1e-6;
909     }
910
911     /**
912      * Sets the percent complete to either 0 or 100.
913
914      * @param done Whether or not the task is done.
915      */

916     public void setDone(boolean done) {
917         if (done)
918             setPercentComplete(100);
919         else
920             setPercentComplete(0);
921     }
922
923     /**
924      * Returns the percentage complete for this task.
925      *
926      * @return 0..100 - value in percents
927      */

928     public int getPercentComplete() {
929         int p = Math.round(progress);
930         if (p == 100 && Math.abs(100.0f - progress) > 1e-6)
931             return 99;
932         else
933             return p;
934     }
935     
936     /**
937      * Returns the percentage complete for this task.
938      *
939      * @return 0..100 - value in percents
940      */

941     public float getProgress() {
942         return progress;
943     }
944     
945     /**
946      * Computes the "expected" (based on the spent time) progress for this task.
947      *
948      * @return "expected" progress 0..100 - value in percents
949      */

950     public float getExpectedProgress() {
951         return ((float) getSpentTime()) / getEffort() * 100.0f;
952     }
953     
954     /**
955      * Computes "percent complete" property as an average of the subtasks
956      * This method should only be called if percentComputed == true
957      *
958      * @return computed percentage
959      */

960     private float computeProgress() {
961         assert valuesComputed;
962         
963         if (getSubtasks().size() == 0) {
964             if (isValuesComputed())
965                 return 100.0f;
966             
967             float p = (((float) getSpentTime()) / getEffort()) * 100.0f;
968             if (p > 99.0)
969                 p = 99;
970             return p;
971         }
972         
973         Iterator JavaDoc it = getSubtasks().iterator();
974         int sum = 0;
975         int full = 0;
976         while (it.hasNext()) {
977             UserTask child = (UserTask) it.next();
978             sum += child.getProgress() * child.getEffort();
979             full += 100 * child.getEffort();
980         }
981         
982         if (full == sum)
983             return 100;
984         
985         float p = ((float) sum) / full * 100.0f;
986         return p;
987     }
988
989     /**
990      * Sets the percentage complete for this task. Will also
991      * update the done flag for this task.
992      *
993      * @param percent 0..100 - value in percents
994      */

995     public void setPercentComplete(int percent) {
996         setProgress(percent);
997     }
998     
999     /**
1000     * Sets the percentage complete for this task. Will also
1001     * update the done flag for this task.
1002     *
1003     * @param percent 0..100 - value in percents
1004     */

1005    public void setProgress(float progress) {
1006        assert progress >= 0 && progress <= 100;
1007        if (valuesComputed)
1008            setValuesComputed(false);
1009        
1010        setProgress_(progress);
1011        
1012        if (isDone() && isStarted())
1013            stop();
1014        
1015        if (isDone())
1016            setCompletedDate(System.currentTimeMillis());
1017        else
1018            setCompletedDate(0);
1019    
1020        if (!isDone()) {
1021            UserTask[] t = findTasksThatDependOnThisOne();
1022            for (int i = 0; i < t.length; i++) {
1023                Dependency d = t[i].findDependencyOn(this);
1024                if (d.getType() == Dependency.END_BEGIN) {
1025                    t[i].setDone(false);
1026                    t[i].setSpentTime(0);
1027                    t[i].getWorkPeriods().clear();
1028                }
1029            }
1030        }
1031        
1032        if (annotation != null) {
1033            annotation.setDone(isDone());
1034        }
1035    }
1036
1037    /**
1038     * Sets the percentage complete for this task. This method does not
1039     * check the percentComputed flag and could be used from
1040     * setProgressComputed()
1041     *
1042     * @param percent 0..100 - value in percents
1043     */

1044    private void setProgress_(float progress) {
1045        float old = this.progress;
1046        
1047        if (this.progress != progress) {
1048            this.progress = progress;
1049            firePropertyChange("progress", // NOI18N
1050
new Float JavaDoc(old), new Float JavaDoc(progress));
1051            if (getParent() != null) {
1052                UserTask p = (UserTask) getParent();
1053                if (p.isValuesComputed())
1054                    p.setProgress_(p.computeProgress());
1055            }
1056        }
1057    }
1058    
1059    /**
1060     * Return the URL associated with this
1061     * task, or null if none.
1062     *
1063     * @return URL or null
1064     */

1065    public URL JavaDoc getUrl() {
1066        if (line == null)
1067            return url;
1068        
1069        return UTUtils.getExternalURLForLine(line);
1070    }
1071    
1072    /**
1073     * Associates an URL with this task. This method sets the line
1074     * number to 0.
1075     *
1076     * @param url an url or null
1077     */

1078    public void setUrl(URL JavaDoc url) {
1079        URL JavaDoc old = this.url;
1080        int oldn = this.linenumber;
1081        
1082        this.url = url;
1083        this.linenumber = 0;
1084        
1085        firePropertyChange(PROP_URL, old, url);
1086        firePropertyChange(PROP_LINE_NUMBER, new Integer JavaDoc(oldn), new Integer JavaDoc(linenumber));
1087
1088        updateLine();
1089        updateAnnotation();
1090    }
1091
1092    /**
1093     * Return line number associated with the task.
1094     *
1095     * @return Line number, or -1 if no particular line is
1096     * associated.
1097     */

1098    public int getLineNumber() {
1099        Line line = getLine();
1100        if(line != null)
1101            return line.getLineNumber();
1102        return linenumber;
1103    }
1104    
1105    /**
1106     * Sets new line number.
1107     *
1108     * @param n new line number
1109     */

1110    public void setLineNumber(int n) {
1111        int old = this.linenumber;
1112        this.linenumber = n;
1113        
1114        firePropertyChange(PROP_LINE_NUMBER, new Integer JavaDoc(old),
1115            new Integer JavaDoc(this.linenumber));
1116
1117        updateLine();
1118        updateAnnotation();
1119    }
1120    
1121    /**
1122     * Get the line position for the task.
1123     *
1124     * @return The line position for the task.
1125     */

1126    public Line getLine() {
1127        return line;
1128    }
1129
1130    /**
1131     * Set the line (file position) associated with the task.
1132     *
1133     * @param line The line associated with the task.
1134     */

1135    public void setLine(final Line line) {
1136        Line old = this.line;
1137        
1138        if (this.line != null) {
1139            this.line.removePropertyChangeListener(lineListener);
1140        }
1141        this.line = line;
1142        if (this.line != null) {
1143            this.line.addPropertyChangeListener(lineListener);
1144        }
1145        firePropertyChange(PROP_LINE, old, this.line);
1146        
1147        if (line != null) {
1148            URL JavaDoc oldUrl = this.url;
1149            this.url = UTUtils.getExternalURLForLine(line);
1150            firePropertyChange(PROP_URL, oldUrl, this.url);
1151            
1152            int oldLineNumber = this.linenumber;
1153            this.linenumber = line.getLineNumber();
1154            firePropertyChange(PROP_LINE_NUMBER, new Integer JavaDoc(oldLineNumber),
1155                new Integer JavaDoc(this.linenumber));
1156        } else {
1157            URL JavaDoc oldUrl = this.url;
1158            this.url = null;
1159            firePropertyChange(PROP_URL, oldUrl, this.url);
1160            
1161            int oldLineNumber = this.linenumber;
1162            this.linenumber = -1;
1163            firePropertyChange(PROP_LINE_NUMBER, new Integer JavaDoc(oldLineNumber),
1164                new Integer JavaDoc(this.linenumber));
1165        }
1166        
1167        updateAnnotation();
1168    }
1169
1170    /**
1171     * Computes line property from the url and lineNumber
1172     */

1173    private void updateLine() {
1174        if (this.line != null)
1175            this.line.removePropertyChangeListener(lineListener);
1176            
1177        Line oldLine = this.line;
1178        this.line = null;
1179        
1180        if (url != null) {
1181            FileObject fo = URLMapper.findFileObject(url);
1182            if (fo != null)
1183                this.line = UTUtils.getLineByFile(fo, linenumber);
1184        }
1185        
1186        if (this.line != null)
1187            this.line.addPropertyChangeListener(lineListener);
1188            
1189        if (this.line != oldLine)
1190            firePropertyChange(PROP_LINE, oldLine, this.line);
1191    }
1192    
1193    /**
1194     * Return category of the task, or "" if no category
1195     * has been selected.
1196     *
1197     * @return Category name
1198     */

1199    public java.lang.String JavaDoc getCategory() {
1200    if (category == null) {
1201            return ""; // NOI18N
1202
}
1203        return category;
1204    }
1205    
1206    /**
1207     * Set category associated with the task.
1208     *
1209     * @param category New category
1210     */

1211    public void setCategory(java.lang.String JavaDoc category) {
1212        String JavaDoc old = this.category;
1213        this.category = category;
1214        firePropertyChange("category", old, this.category); // NOI18N
1215
}
1216    
1217    /**
1218     * Return the date when the item was created
1219     *
1220     * @return Date when the item was created
1221     */

1222    public long getCreatedDate() {
1223        return created;
1224    }
1225
1226    /**
1227     * Get annotation associated with this task
1228     *
1229     * @return annotation
1230     */

1231    public UTAnnotation getAnnotation() {
1232        return annotation;
1233    }
1234    
1235    /**
1236     * Creates new annotation object for the current line property.
1237     */

1238    private void updateAnnotation() {
1239        if (this.annotation != null) {
1240            this.annotation.detach();
1241            this.annotation = null;
1242        }
1243        
1244        if (this.line != null) {
1245            this.annotation = createAnnotation();
1246            this.annotation.attach(this.line);
1247        }
1248    }
1249    
1250    /**
1251     * Set the date when the item was created
1252     */

1253    public void setCreatedDate(long cr) {
1254        long old = this.created;
1255    created = cr;
1256        firePropertyChange("createdDate", new Long JavaDoc(old), new Long JavaDoc(this.created)); // NOI18N
1257
}
1258
1259    /**
1260     * Get the date when the item was last edited.
1261     * If the item has not been edited since it was created,
1262     * this returns the date of creation. Note also that
1263     * adding subtasks or removing subtasks is not considered
1264     * an edit of this item.
1265     *
1266     * @return the date when the item was last edited.
1267     */

1268    public long getLastEditedDate() {
1269    return lastEditedDate;
1270    }
1271
1272    /**
1273     * Set the date when the item was last edited
1274     *
1275     * @param ed new date as returned by System.currentTimeMillis
1276     */

1277    public void setLastEditedDate(long ed) {
1278        lastEditedDate = ed;
1279    }
1280    
1281    public int hashCode() {
1282        return summary.hashCode() + details.hashCode() + priority;
1283    }
1284    
1285    /**
1286     * Create an identical copy of a task (a deep copy, e.g. the
1287     * list of subtasks will be cloned as well
1288     */

1289    public Object JavaDoc clone() {
1290        UserTask t = new UserTask("", null); // NOI18N
1291
t.copyFrom(this);
1292        return t;
1293    }
1294
1295    /**
1296     * Returns dependencies of this task. No copy of the dependencies will
1297     * be made.
1298     *
1299     * @return dependencies. List<Dependency>
1300     */

1301    public ObjectList<Dependency> getDependencies() {
1302        return dependencies;
1303    }
1304    
1305    /**
1306     * Finds a dependency on another task.
1307     *
1308     * @param ut another task
1309     * @return found dependency of this task on <code>ut</code> or null
1310     */

1311    public Dependency findDependencyOn(UserTask ut) {
1312        for (int i = 0; i < dependencies.size(); i++) {
1313            Dependency d = (Dependency) dependencies.get(i);
1314            if (d.getDependsOn() == ut)
1315                return d;
1316        }
1317        return null;
1318    }
1319    
1320    /**
1321     * Searches in the whole task list for tasks that depend on this one.
1322     *
1323     * @return found tasks
1324     */

1325    public UserTask[] findTasksThatDependOnThisOne() {
1326        List JavaDoc<Object JavaDoc> t = UTUtils.filter(
1327                new UTListTreeAbstraction(getList()), new UnaryFunction() {
1328            public Object JavaDoc compute(Object JavaDoc obj) {
1329                if (obj instanceof UserTask) {
1330                    UserTask ut = (UserTask) obj;
1331                    Boolean JavaDoc b = Boolean.valueOf(
1332                            ut.findDependencyOn(UserTask.this) != null);
1333                    return b;
1334                } else {
1335                    return Boolean.FALSE;
1336                }
1337            }
1338        });
1339        return (UserTask[]) t.toArray(new UserTask[t.size()]);
1340    }
1341    
1342    /**
1343     * Copy all the fields in the given task into this object.
1344     * Should only be called on an object of the EXACT same type.
1345     * Thus, if you're implementing a subclass of Task, say
1346     * UserTask, you can implement copy assuming that the passed
1347     * in Task parameter is of type UserTask. When overriding,
1348     * remember to call super.copyFrom.
1349     * <p>
1350     * Make a deep copy - except when that doesn't make sense.
1351     * For example, you can share the same icon reference.
1352     * And in particular, the tasklist reference should be the same.
1353     * But the list of subitems should be unique. You get the idea.
1354     *
1355     * @param from another task
1356     */

1357    protected void copyFrom(UserTask from) {
1358        // Copy fields from the parent implementation
1359
if (this.line != null)
1360            this.line.removePropertyChangeListener(lineListener);
1361        this.line = from.line;
1362        if (this.line != null)
1363            this.line.addPropertyChangeListener(lineListener);
1364        
1365        this.url = from.url;
1366        this.linenumber = from.linenumber;
1367        if (this.line == null)
1368            this.annotation = null;
1369        else
1370            this.annotation = createAnnotation();
1371        
1372        setSummary(from.getSummary());
1373        setPriority(from.getPriority());
1374
1375        setDetails(from.getDetails());
1376        setDueDate(from.getDueDate());
1377        setDueAlarmSent(from.isDueAlarmSent());
1378
1379        // Copying the parent reference may seem odd, since for children
1380
// it should be changed - but this only affects the root node.
1381
// For children nodes, we override the parent reference after
1382
// cloning the child.
1383
parent = from.parent;
1384
1385        // Copy the subtasks reference
1386

1387    // Please note -- I'm NOT copying the universal id, these have to
1388
// be unique, even for copies
1389
Iterator JavaDoc it = from.subtasks.iterator();
1390        subtasks = new UserTaskObjectList(this);
1391        while (it.hasNext()) {
1392            UserTask task = (UserTask)it.next();
1393            UserTask mycopy = (UserTask)task.clone();
1394            mycopy.list = list;
1395            mycopy.parent = this;
1396            subtasks.add(mycopy);
1397        }
1398
1399        progress = from.progress;
1400        valuesComputed = from.valuesComputed;
1401        category = from.category;
1402        created = from.created;
1403        effort = from.effort;
1404        spentTime = from.spentTime;
1405        dependencies.clear();
1406        dependencies.addAll(from.dependencies);
1407        owner = from.owner;
1408        completedDate = from.completedDate;
1409        start = from.start;
1410        lastEditedDate = from.lastEditedDate;
1411        
1412        workPeriods.clear();
1413        for (int i = 0; i < from.workPeriods.size(); i++) {
1414            WorkPeriod wp = (WorkPeriod) from.workPeriods.get(i);
1415            try {
1416                workPeriods.add((WorkPeriod) wp.clone());
1417            } catch (CloneNotSupportedException JavaDoc e) {
1418                throw new InternalError JavaDoc("unexpected"); // NOI18N
1419
}
1420        }
1421    }
1422    
1423    /**
1424     * Returns the remaining effort in minutes
1425     *
1426     * @return remaining effort in minutes >= 0
1427     */

1428    public int getRemainingEffort() {
1429        return Math.round(getEffort() * (1 - getProgress() / 100));
1430    }
1431    
1432    /**
1433     * Getter for property effort.
1434     *
1435     * @return effort in minutes > 0
1436     */

1437    public int getEffort() {
1438        return effort;
1439    }
1440    
1441    /**
1442     * Computes "effort" property as the sum of the subtask efforts.
1443     * This method should only be called if effortComputed == true
1444     *
1445     * This method is used in tests that is why it's package private
1446     *
1447     * @return effort in minutes
1448     */

1449    int computeEffort() {
1450        assert valuesComputed;
1451        
1452        Iterator JavaDoc it = getSubtasks().iterator();
1453        int sum = 0;
1454        while (it.hasNext()) {
1455            UserTask child = (UserTask) it.next();
1456            sum += child.getEffort();
1457        }
1458        return sum;
1459    }
1460
1461    /**
1462     * Setter for property effort.
1463     *
1464     * @param effort New value of property effort in minutes.
1465     */

1466    public void setEffort(int effort) {
1467        assert effort >= 0;
1468        if (valuesComputed)
1469            setValuesComputed(false);
1470    
1471        setEffort_(effort);
1472
1473        if (isValuesComputed() && getSubtasks().size() == 0)
1474            setProgress_(computeProgress());
1475    }
1476
1477    /**
1478     * Setter for property effort. This method does not check the
1479     * effortComputed property and so it could be used from setEffortComputed()
1480     *
1481     * @param effort New value of property effort in minutes.
1482     */

1483    private void setEffort_(int effort) {
1484        int old = this.effort;
1485        
1486        if (this.effort != effort) {
1487            int oldre = getRemainingEffort();
1488            this.effort = effort;
1489            firePropertyChange("effort", // NOI18N
1490
new Integer JavaDoc(old), new Integer JavaDoc(effort));
1491            firePropertyChange("remainingEffort",
1492                    oldre, getRemainingEffort());
1493            if (getParent() != null) {
1494                UserTask p = (UserTask) getParent();
1495                if (p.isValuesComputed())
1496                    p.setEffort_(p.computeEffort());
1497                if (p.isValuesComputed())
1498                    p.setProgress_(p.computeProgress());
1499            }
1500        }
1501    }
1502    
1503    /**
1504     * Generate a string summary of the task; only used
1505     * for debugging. DO NOT depend on this format for anything!
1506     * Use generate() instead.
1507     *
1508     * @return summary string
1509     */

1510    public String JavaDoc toString() {
1511        return "UserTask[" + getSummary() + ", " +
1512                getDetails() + "]"; // NOI18N
1513
}
1514
1515    public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1516        if (updateLastModified)
1517            setLastEditedDate(System.currentTimeMillis());
1518    }
1519
1520    /**
1521     * Deletes all completed subtasks of this task (recursively)
1522     */

1523    public void purgeCompleted() {
1524        getSubtasks().purgeCompletedItems();
1525    }
1526    
1527    /**
1528     * Fires a PropertyChangeEvent. Also fires an event for PROP_DEEP_CHANGE
1529     *
1530     * @param propertyName changed property
1531     * @param oldValue old value (may be null)
1532     * @param newValue new value (may be null)
1533     */

1534    protected void firePropertyChange(String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
1535        supp.firePropertyChange(propertyName, oldValue, newValue);
1536        if (list != null)
1537            list.fireChange();
1538    }
1539    
1540    /**
1541     * Searches for a task
1542     *
1543     * @param t task to be found
1544     * @return index of the task or -1
1545     */

1546    public int indexOf(UserTask t) {
1547        return subtasks.indexOf(t);
1548    }
1549    
1550    /**
1551     * Returns indent level for this task. If parent == null returns 0
1552     *
1553     * @return indent level for this task
1554     */

1555    public int getLevel() {
1556        UserTask t = getParent();
1557        int level = 0;
1558        while (t != null) {
1559            level++;
1560            t = t.getParent();
1561        }
1562        return level;
1563    }
1564
1565    /**
1566     * Set the summary of the task. This is a one-line description
1567     * of the task. The summary should not be null.
1568     *
1569     * @param summary The summary of the task.
1570     */

1571    public void setSummary(final String JavaDoc summary) {
1572        if (summary == null) {
1573            throw new NullPointerException JavaDoc();
1574        }
1575        String JavaDoc old = getSummary();
1576        if (old.equals(summary)) return;
1577        this.summary = summary;
1578        firePropertyChange(PROP_SUMMARY, old, summary);
1579    }
1580
1581    /**
1582     * Set the details of the task. This could be multiple lines
1583     * of description of the task. Can be null.
1584     *
1585     * @param details The details of the task
1586     */

1587    public void setDetails(final String JavaDoc details) {
1588        String JavaDoc old = getDetails();
1589        if (old.equals(details)) return;
1590        this.details = details;
1591        firePropertyChange(PROP_DETAILS, old, details);
1592    }
1593
1594    /**
1595     * Set the priority of the task.
1596     * <p>
1597     *
1598     * @param priority The priority of the task.
1599     */

1600    public void setPriority(int priority) {
1601        assert priority == HIGH || priority == MEDIUM_HIGH ||
1602            priority == MEDIUM || priority == MEDIUM_LOW || priority == LOW;
1603        int old = getPriority();
1604        if (old == priority) return;
1605        this.priority = priority;
1606        firePropertyChange(PROP_PRIORITY, new Integer JavaDoc(old), new Integer JavaDoc(priority));
1607    }
1608
1609    /**
1610     * Returns the parent of this task
1611     *
1612     * @return parent or null for top-level tasks
1613     */

1614    public final UserTask getParent() {
1615        return parent;
1616    }
1617    
1618    /**
1619     * Sets the parent attribute.
1620     *
1621     * @param parent new parent or null for top-level tasks
1622     */

1623    void setParent(UserTask parent) {
1624        this.parent = parent;
1625    }
1626
1627    /**
1628     * Write a TodoItem to a text stream. NOT DONE.
1629     * @param item The task to write out
1630     * @param w The writer to write the string to
1631     * @throws IOException Not thrown explicitly by this code, but perhaps
1632     * by the call it makes to w's write() method
1633     *
1634     * @todo Finish the implementation here such that it
1635     * writes out all the fields, not just the
1636     * description.
1637     */

1638    public static void generate(UserTask item, Writer JavaDoc w) throws IOException JavaDoc {
1639    w.write(item.getSummary());
1640    }
1641
1642    /**
1643     * Parse a task from a text stream.
1644     *
1645     * @param r The reader to read the task from
1646     * @throws IOException Not thrown directly by this method, but
1647     * possibly by r's read() method which it calls
1648     * @return A new task object which represents the
1649     * data read from the reader
1650     * @see generate
1651     */

1652    public static UserTask[] parse(Reader JavaDoc r) throws IOException JavaDoc {
1653        UserTaskList utl = new UserTaskList();
1654        BufferedReader JavaDoc reader = new BufferedReader JavaDoc(r);
1655        String JavaDoc line;
1656        List JavaDoc<UserTask> res = new ArrayList JavaDoc<UserTask>();
1657        while ((line = reader.readLine()) != null) {
1658            res.add(new UserTask(line, utl));
1659        }
1660        return res.toArray(new UserTask[res.size()]);
1661    }
1662
1663    /**
1664     * Counts all subtasks of this task recursively.
1665     *
1666     * @return number of subtasks
1667     */

1668    public int getSubtaskCountRecursively() {
1669        int n = 0;
1670        Iterator JavaDoc it = subtasks.iterator();
1671        while(it.hasNext()) {
1672            UserTask t = (UserTask) it.next();
1673            n += t.getSubtaskCountRecursively() + 1;
1674        }
1675        return n;
1676    }
1677
1678    /**
1679     * Clones task's properies without its
1680     * membership relations (parent).
1681     */

1682    public UserTask cloneTask() {
1683        UserTask clone = (UserTask) clone();
1684        clone.parent = null;
1685        return clone;
1686    }
1687
1688    /**
1689     * Get the summary of the task.
1690     *
1691     * @return The summary of the task.
1692     */

1693    public String JavaDoc getSummary() {
1694        if (summary == null) {
1695            summary = ""; // NOI18N
1696
}
1697        return summary;
1698    }
1699
1700
1701    /**
1702     * Get the details of the task. Will never be null (but may
1703     * be an empty string.)
1704     * <p>
1705     *
1706     * @return The details of the task
1707     */

1708    public String JavaDoc getDetails() {
1709        if (details == null) {
1710            details = ""; // NOI18N
1711
}
1712        return details;
1713    }
1714    
1715    /**
1716     * Get the priority of the task.
1717     * <p>
1718     *
1719     * @return The priority of the task.
1720     */

1721    public int getPriority() {
1722        return priority;
1723    }
1724
1725    /**
1726     * Listen to changes in bean properties.
1727     * @param l listener to be notified of changes
1728     */

1729    public final void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
1730        supp.removePropertyChangeListener(l);
1731        supp.addPropertyChangeListener(l);
1732    }
1733
1734    /**
1735     * Stop listening to changes in bean properties.
1736     *
1737     * @param l listener who will no longer be notified of changes
1738     */

1739    public final void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
1740        supp.removePropertyChangeListener(l);
1741    }
1742
1743    /**
1744     * Will be called from UserTaskList.destroy()
1745     */

1746    public void destroy() {
1747        if (this.annotation != null) {
1748            this.annotation.detach();
1749            this.annotation = null;
1750        }
1751        
1752        Iterator JavaDoc it = getSubtasks().iterator();
1753        while (it.hasNext()) {
1754            UserTask ut = (UserTask) it.next();
1755            ut.destroy();
1756        }
1757    }
1758    
1759    /**
1760     * Creates an annotation for this object.
1761     *
1762     * @return created annotation
1763     */

1764    private UTAnnotation createAnnotation() {
1765        UTAnnotation ann = new UTAnnotation(this, false);
1766        return ann;
1767    }
1768
1769    /**
1770     * Returns the owner (a person) of this task
1771     *
1772     * @return owner
1773     */

1774    public String JavaDoc getOwner() {
1775        return owner;
1776    }
1777
1778    /**
1779     * Sets a new owner of this task
1780     *
1781     * @param owner a person
1782     */

1783    public void setOwner(String JavaDoc owner) {
1784        String JavaDoc old = this.owner;
1785        this.owner = owner;
1786        firePropertyChange("owner", old, this.owner); // NOI18N
1787
}
1788
1789    /**
1790     * Returns the date when this task was completed.
1791     *
1792     * @return date when this task was completed or 0
1793     */

1794    public long getCompletedDate() {
1795        return completedDate;
1796    }
1797
1798    /**
1799     * Sets the date when this task was completed.
1800     *
1801     * @param completed date when this task was completed or 0
1802     */

1803    public void setCompletedDate(long completed) {
1804        long old = this.completedDate;
1805        this.completedDate = completed;
1806        firePropertyChange("completedDate", new Long JavaDoc(old), new Long JavaDoc(completed)); // NOI18N
1807
}
1808    
1809    /**
1810     * Computes the time spent on this task today.
1811     *
1812     * @return time in minutes
1813     */

1814    public int getSpentTimeToday() {
1815        int sum = 0;
1816        if (isValuesComputed()) {
1817            for (int i = 0; i < getSubtasks().size(); i++) {
1818                UserTask ut = (UserTask) getSubtasks().get(i);
1819                sum += ut.getSpentTimeToday();
1820            }
1821        } else {
1822            for (int i = workPeriods.size() - 1; i >= 0; i--) {
1823                WorkPeriod wp = (WorkPeriod) workPeriods.get(i);
1824                if (wp.isToday())
1825                    sum += wp.getDuration();
1826                else
1827                    break;
1828            }
1829        }
1830        return sum;
1831    }
1832
1833    /**
1834     * Returns the start time for this task.
1835     *
1836     * @return start time as returned by System.currentTimeMillis or -1 if
1837     * undefined
1838     */

1839    public long getStart() {
1840        return start;
1841    }
1842
1843    /**
1844     * Returns the start time for this task.
1845     *
1846     * @return start time or null if undefined
1847     */

1848    public Date JavaDoc getStartDate() {
1849        if (start == -1)
1850            return null;
1851        else
1852            return new Date JavaDoc(start);
1853    }
1854    
1855    /**
1856     * Sets the start time for this task.
1857     *
1858     * @param start time as returned by System.currentTimeMillis or -1 if
1859     * undefined
1860     */

1861    public void setStart(long start) {
1862        long old = this.start;
1863        this.start = start;
1864        firePropertyChange("start", new Long JavaDoc(old), new Long JavaDoc(start)); // NOI18N
1865
}
1866
1867    /**
1868     * Sets the start time for this task.
1869     *
1870     * @param start time or null if undefined
1871     */

1872    public void setStartDate(Date JavaDoc start) {
1873        if (start == null)
1874            setStart(-1);
1875        else
1876            setStart(start.getTime());
1877    }
1878    
1879    /**
1880     * Clears all work periods with duration = 0.
1881     */

1882    public void clearEmptyWorkPeriods() {
1883        Iterator JavaDoc it = workPeriods.iterator();
1884        while (it.hasNext()) {
1885            WorkPeriod wp = (WorkPeriod) it.next();
1886            if (wp.getDuration() == 0)
1887                it.remove();
1888        }
1889    }
1890
1891    /**
1892     * Sets whether the "last modified" date should be updated.
1893     *
1894     * @param b true = last modified date will be updated
1895     */

1896    public void setUpdateLastModified(boolean b) {
1897        this.updateLastModified = b;
1898    }
1899}
1900
Popular Tags