001: /**
002: * TaskListImpl.java
003: * Created on 21.02.2003, 12:29:54 Alex
004: * Package: net.sf.memoranda
005: *
006: * @author Alex V. Alishevskikh, alex@openmechanics.net
007: * Copyright (c) 2003 Memoranda Team. http://memoranda.sf.net
008: */package net.sf.memoranda;
009:
010: import java.util.Collection;
011: import java.util.Hashtable;
012: import java.util.Iterator;
013: import java.util.Vector;
014:
015: import net.sf.memoranda.date.CalendarDate;
016: import net.sf.memoranda.util.Util;
017: import nu.xom.Attribute;
018: import nu.xom.Document;
019: import nu.xom.Element;
020: import nu.xom.Elements;
021: import nu.xom.Node;
022: import nu.xom.Nodes;
023:
024: //import nu.xom.converters.*;
025: //import org.apache.xerces.dom.*;
026: //import nux.xom.xquery.XQueryUtil;
027:
028: /**
029: *
030: */
031: /*$Id: TaskListImpl.java,v 1.14 2006/07/03 11:59:19 alexeya Exp $*/
032: public class TaskListImpl implements TaskList {
033:
034: private Project _project = null;
035: private Document _doc = null;
036: private Element _root = null;
037:
038: /*
039: * Hastable of "task" XOM elements for quick searching them by ID's
040: * (ID => element)
041: */
042: private Hashtable elements = new Hashtable();
043:
044: /**
045: * Constructor for TaskListImpl.
046: */
047: public TaskListImpl(Document doc, Project prj) {
048: _doc = doc;
049: _root = _doc.getRootElement();
050: _project = prj;
051: buildElements(_root);
052: }
053:
054: public TaskListImpl(Project prj) {
055: _root = new Element("tasklist");
056: _doc = new Document(_root);
057: _project = prj;
058: }
059:
060: public Project getProject() {
061: return _project;
062: }
063:
064: /*
065: * Build the hashtable recursively
066: */
067: private void buildElements(Element parent) {
068: Elements els = parent.getChildElements("task");
069: for (int i = 0; i < els.size(); i++) {
070: Element el = els.get(i);
071: elements.put(el.getAttribute("id").getValue(), el);
072: buildElements(el);
073: }
074: }
075:
076: /**
077: * All methods to obtain list of tasks are consolidated under getAllSubTasks and getActiveSubTasks.
078: * If a root task is required, just send a null taskId
079: */
080: public Collection getAllSubTasks(String taskId) {
081: if ((taskId == null) || (taskId.length() == 0)) {
082: return getAllRootTasks();
083: } else {
084: Element task = getTaskElement(taskId);
085: if (task == null)
086: return new Vector();
087: Elements subTasks = task.getChildElements("task");
088: return convertToTaskObjects(subTasks);
089: }
090: }
091:
092: public Collection getTopLevelTasks() {
093: return getAllRootTasks();
094: }
095:
096: /**
097: * All methods to obtain list of tasks are consolidated under getAllSubTasks and getActiveSubTasks.
098: * If a root task is required, just send a null taskId
099: */
100: public Collection getActiveSubTasks(String taskId, CalendarDate date) {
101: Collection allTasks = getAllSubTasks(taskId);
102: return filterActiveTasks(allTasks, date);
103: }
104:
105: public Task createTask(CalendarDate startDate,
106: CalendarDate endDate, String text, int priority,
107: long effort, String description, String parentTaskId) {
108: Element el = new Element("task");
109: el
110: .addAttribute(new Attribute("startDate", startDate
111: .toString()));
112: el.addAttribute(new Attribute("endDate",
113: endDate != null ? endDate.toString() : ""));
114: String id = Util.generateId();
115: el.addAttribute(new Attribute("id", id));
116: el.addAttribute(new Attribute("progress", "0"));
117: el
118: .addAttribute(new Attribute("effort", String
119: .valueOf(effort)));
120: el.addAttribute(new Attribute("priority", String
121: .valueOf(priority)));
122:
123: Element txt = new Element("text");
124: txt.appendChild(text);
125: el.appendChild(txt);
126:
127: Element desc = new Element("description");
128: desc.appendChild(description);
129: el.appendChild(desc);
130:
131: if (parentTaskId == null) {
132: _root.appendChild(el);
133: } else {
134: Element parent = getTaskElement(parentTaskId);
135: parent.appendChild(el);
136: }
137:
138: elements.put(id, el);
139:
140: Util.debug("Created task with parent " + parentTaskId);
141:
142: return new TaskImpl(el, this );
143: }
144:
145: /**
146: * @see net.sf.memoranda.TaskList#removeTask(import net.sf.memoranda.Task)
147: */
148:
149: public void removeTask(Task task) {
150: String parentTaskId = task.getParentId();
151: if (parentTaskId == null) {
152: _root.removeChild(task.getContent());
153: } else {
154: Element parentNode = getTaskElement(parentTaskId);
155: parentNode.removeChild(task.getContent());
156: }
157: elements.remove(task.getID());
158: }
159:
160: public boolean hasSubTasks(String id) {
161: Element task = getTaskElement(id);
162: if (task == null)
163: return false;
164: if (task.getChildElements("task").size() > 0) {
165: return true;
166: } else {
167: return false;
168: }
169: }
170:
171: public Task getTask(String id) {
172: Util.debug("Getting task " + id);
173: return new TaskImpl(getTaskElement(id), this );
174: }
175:
176: public boolean hasParentTask(String id) {
177: Element t = getTaskElement(id);
178:
179: Node parentNode = t.getParent();
180: if (parentNode instanceof Element) {
181: Element parent = (Element) parentNode;
182: if (parent.getLocalName().equalsIgnoreCase("task")) {
183: return true;
184: } else {
185: return false;
186: }
187: } else {
188: return false;
189: }
190: }
191:
192: /**
193: * @see net.sf.memoranda.TaskList#getXMLContent()
194: */
195: public Document getXMLContent() {
196: return _doc;
197: }
198:
199: /**
200: * Recursively calculate total effort based on subtasks for every node in the task tree
201: * The values are saved as they are calculated as well
202: *
203: * @param t
204: * @return
205: */
206: public long calculateTotalEffortFromSubTasks(Task t) {
207: long totalEffort = 0;
208: if (hasSubTasks(t.getID())) {
209: Collection subTasks = getAllSubTasks(t.getID());
210: for (Iterator iter = subTasks.iterator(); iter.hasNext();) {
211: Task e = (Task) iter.next();
212: totalEffort = totalEffort
213: + calculateTotalEffortFromSubTasks(e);
214: }
215: t.setEffort(totalEffort);
216: return totalEffort;
217: } else {
218: return t.getEffort();
219: }
220: }
221:
222: /**
223: * Looks through the entire sub task tree and corrects any inconsistencies in start dates
224: *
225: * @param t
226: * @return
227: */
228: public CalendarDate getEarliestStartDateFromSubTasks(Task t) {
229: CalendarDate d = t.getStartDate();
230: if (hasSubTasks(t.getID())) {
231: Collection subTasks = getAllSubTasks(t.getID());
232: for (Iterator iter = subTasks.iterator(); iter.hasNext();) {
233: Task e = (Task) iter.next();
234: CalendarDate dd = getEarliestStartDateFromSubTasks(e);
235: if (dd.before(d)) {
236: d = dd;
237: }
238: }
239: t.setStartDate(d);
240: return d;
241: } else {
242: return t.getStartDate();
243: }
244: }
245:
246: /**
247: * Looks through the entire sub task tree and corrects any inconsistencies in start dates
248: *
249: * @param t
250: * @return
251: */
252: public CalendarDate getLatestEndDateFromSubTasks(Task t) {
253: CalendarDate d = t.getEndDate();
254: if (hasSubTasks(t.getID())) {
255: Collection subTasks = getAllSubTasks(t.getID());
256: for (Iterator iter = subTasks.iterator(); iter.hasNext();) {
257: Task e = (Task) iter.next();
258: CalendarDate dd = getLatestEndDateFromSubTasks(e);
259: if (dd.after(d)) {
260: d = dd;
261: }
262: }
263: t.setEndDate(d);
264: return d;
265: } else {
266: return t.getEndDate();
267: }
268: }
269:
270: /**
271: * Looks through the entire sub task tree and calculates progress on all parent task nodes
272: *
273: * @param t
274: * @return long[] of size 2. First long is expended effort in milliseconds, 2nd long is total effort in milliseconds
275: */
276: public long[] calculateCompletionFromSubTasks(Task t) {
277: // Util.debug("Task " + t.getText());
278:
279: long[] res = new long[2];
280: long expendedEffort = 0; // milliseconds
281: long totalEffort = 0; // milliseconds
282: if (hasSubTasks(t.getID())) {
283: Collection subTasks = getAllSubTasks(t.getID());
284: for (Iterator iter = subTasks.iterator(); iter.hasNext();) {
285: Task e = (Task) iter.next();
286: long[] subTaskCompletion = calculateCompletionFromSubTasks(e);
287: expendedEffort = expendedEffort + subTaskCompletion[0];
288: totalEffort = totalEffort + subTaskCompletion[1];
289: }
290:
291: int this Progress = (int) Math
292: .round((((double) expendedEffort / (double) totalEffort) * 100));
293: t.setProgress(this Progress);
294:
295: // Util.debug("Expended Effort: "+ expendedEffort);
296: // Util.debug("Total Effort: "+ totalEffort);
297: // Util.debug("Progress: "+ t.getProgress());
298:
299: res[0] = expendedEffort;
300: res[1] = totalEffort;
301: return res;
302: } else {
303: long eff = t.getEffort();
304: // if effort was not filled in, it is assumed to be "1 hr" for the purpose of calculation
305: if (eff == 0) {
306: eff = 1;
307: }
308: res[0] = Math
309: .round((double) (t.getProgress() * eff) / 100d);
310: res[1] = eff;
311: return res;
312: }
313: }
314:
315: /*
316: * private methods below this line
317: */
318: private Element getTaskElement(String id) {
319:
320: /*Nodes nodes = XQueryUtil.xquery(_doc, "//task[@id='" + id + "']");
321: if (nodes.size() > 0) {
322: Element el = (Element) nodes.get(0);
323: return el;
324: }
325: else {
326: Util.debug("Task " + id + " cannot be found in project " + _project.getTitle());
327: return null;
328: } */
329: Element el = (Element) elements.get(id);
330: if (el == null) {
331: Util.debug("Task " + id + " cannot be found in project "
332: + _project.getTitle());
333: }
334: return el;
335: }
336:
337: private Collection getAllRootTasks() {
338: Elements tasks = _root.getChildElements("task");
339: return convertToTaskObjects(tasks);
340: }
341:
342: private Collection convertToTaskObjects(Elements tasks) {
343: Vector v = new Vector();
344:
345: for (int i = 0; i < tasks.size(); i++) {
346: Task t = new TaskImpl(tasks.get(i), this );
347: v.add(t);
348: }
349: return v;
350: }
351:
352: private Collection filterActiveTasks(Collection tasks,
353: CalendarDate date) {
354: Vector v = new Vector();
355: for (Iterator iter = tasks.iterator(); iter.hasNext();) {
356: Task t = (Task) iter.next();
357: if (isActive(t, date)) {
358: v.add(t);
359: }
360: }
361: return v;
362: }
363:
364: private boolean isActive(Task t, CalendarDate date) {
365: if ((t.getStatus(date) == Task.ACTIVE)
366: || (t.getStatus(date) == Task.DEADLINE)
367: || (t.getStatus(date) == Task.FAILED)) {
368: return true;
369: } else {
370: return false;
371: }
372: }
373:
374: /*
375: * deprecated methods below
376: *
377: */
378:
379: // public void adjustParentTasks(Task t) {
380: // if ((t.getParent() == null) || (t.getParent().equals(""))){
381: // return;
382: // }
383: // else {
384: // Task p = getTask(t.getParent());
385: //
386: // long totalEffort = calculateTotalEffortFromSubTasks(p);
387: //
388: // if(totalEffort > p.getEffort()) {
389: // p.setEffort(totalEffort);
390: // }
391: // if(t.getStartDate().before(p.getStartDate())) {
392: // p.setStartDate(t.getStartDate());
393: // }
394: // if(t.getEndDate().after(p.getEndDate())) {
395: // p.setEndDate(t.getEndDate());
396: // }
397: //
398: // if (!((p.getParent() == null) || (p.getParent().equals("")))){
399: // // still has parent, go up the tree
400: // adjustParentTasks(p);
401: // }
402: // }
403: // }
404: }
|