001: package com.technoetic.xplanner.domain;
002:
003: import java.util.Collection;
004: import java.util.Date;
005: import java.util.HashSet;
006: import java.util.Iterator;
007:
008: // TODO management of original/current estimate should be done through a status
009: public class Task extends DomainObject implements Nameable,
010: NoteAttachable, Describable {
011: // ------------------------------ FIELDS ------------------------------
012:
013: private int acceptorId;
014: private String name;
015: private String description;
016: private int completionFlag;
017: private String type;
018: private TaskDisposition disposition = TaskDisposition.PLANNED;
019: private double estimatedOriginalHours;
020: private double estimatedHours;
021: private double postponedHours;
022: private Date createdDate;
023: private UserStory story;
024: private Collection timeEntries = new HashSet();
025: public static final String ADDED_ORIGINAL_HOURS = getValidProperty("addedOriginalHours");
026: public static final String ESTIMATED_ORIGINAL_HOURS = getValidProperty("estimatedOriginalHours");
027: public static final String ITERATION_START_ESTIMATED_HOURS = getValidProperty("iterationStartEstimatedHours");
028:
029: private static String getValidProperty(String property) {
030: return getValidProperty(Task.class, property);
031: }
032:
033: // --------------------- GETTER / SETTER METHODS ---------------------
034:
035: public int getAcceptorId() {
036: return acceptorId;
037: }
038:
039: public void setAcceptorId(int acceptorId) {
040: this .acceptorId = acceptorId;
041: }
042:
043: public double getActualHours() {
044: double actualHours = 0.0;
045: if (timeEntries != null && timeEntries.size() > 0) {
046: Iterator itr = timeEntries.iterator();
047: while (itr.hasNext()) {
048: TimeEntry entry = (TimeEntry) itr.next();
049: actualHours += entry.getEffort();
050: }
051: }
052: return actualHours;
053: }
054:
055: public Date getCreatedDate() {
056: return createdDate;
057: }
058:
059: public void setCreatedDate(java.util.Date createdDate) {
060: this .createdDate = createdDate;
061: }
062:
063: public String getDescription() {
064: return description;
065: }
066:
067: public void setDescription(String description) {
068: this .description = description;
069: }
070:
071: public TaskDisposition getDisposition() {
072: return disposition;
073: }
074:
075: public void setDisposition(TaskDisposition disposition) {
076: this .disposition = disposition;
077: }
078:
079: public double getEstimatedHours() {
080: return estimatedHours;
081: }
082:
083: public void setEstimatedHours(double estimatedHours) {
084: this .estimatedHours = estimatedHours;
085: }
086:
087: public String getName() {
088: return name;
089: }
090:
091: public void setName(String name) {
092: this .name = name;
093: }
094:
095: public TaskStatus getStatus() {
096: TaskStatus status;
097: if (isCompleted()) {
098: status = TaskStatus.COMPLETED;
099: } else if (getActualHours() > 0.0) {
100: status = TaskStatus.STARTED;
101: } else {
102: status = TaskStatus.NON_STARTED;
103: }
104: return status;
105: }
106:
107: public boolean isCompleted() {
108: return completionFlag == 1;
109: }
110:
111: public UserStory getStory() {
112: return story;
113: }
114:
115: // TODO: add management of the inverse relationship tasks
116: public void setStory(UserStory story) {
117: this .story = story;
118: }
119:
120: public Collection getTimeEntries() {
121: return timeEntries;
122: }
123:
124: public void setTimeEntries(Collection timeEntries) {
125: this .timeEntries = timeEntries;
126: }
127:
128: public String getType() {
129: return type;
130: }
131:
132: public void setType(String type) {
133: this .type = type;
134: }
135:
136: public void setEstimatedOriginalHours(double estimatedOriginalHours) {
137: this .estimatedOriginalHours = estimatedOriginalHours;
138: }
139:
140: // ------------------------ CANONICAL METHODS ------------------------
141:
142: public String toString() {
143: return "Task(id="
144: + this .getId()
145: + ", userStoryId="
146: + (this .getStory() == null ? "null" : ""
147: + this .getStory().getId()) + ", name="
148: + this .getName() + ", acceptorId="
149: + this .getAcceptorId() + ", createDate="
150: + this .getCreatedDate() + ", desc="
151: + this .getDescription() + ", title=" + this .getName();
152: }
153:
154: // -------------------------- OTHER METHODS --------------------------
155:
156: public String getDispositionName() {
157: return disposition != null ? disposition.getName() : null;
158: }
159:
160: public void setDispositionName(String dispositionName) {
161: this .disposition = TaskDisposition.fromName(dispositionName);
162: }
163:
164: public double getAddedHours() {
165: return isAdded() ? getEstimatedHours() : 0;
166: }
167:
168: private boolean isAdded() {
169: return getDisposition().equals(TaskDisposition.ADDED);
170: }
171:
172: public double getAdjustedEstimatedHours() {
173: if (isCompleted()) {
174: return getActualHours();
175: } else {
176: return Math.max(getEstimatedHours(), getActualHours());
177: }
178: }
179:
180: public double getCompletedHours() {
181: return isCompleted() ? getActualHours() : 0;
182: }
183:
184: public double getEstimatedHoursBasedOnActuals() {
185: return getActualHours() + getRemainingHours();
186: }
187:
188: public double getCompletedRemainingHours() {
189: return isCompleted() ? 0.0 : getEstimatedOriginalHours();
190: }
191:
192: public double getRemainingHours() {
193: return isCompleted() ? 0.0 : Math.max(getEstimatedHours()
194: - getActualHours() - getPostponedHours(), 0.0);
195: }
196:
197: public double getAddedOriginalHours() {
198: return getDisposition().isOriginal() ? 0
199: : getEstimatedOriginalHours();
200: }
201:
202: public double getEstimatedOriginalHours() {
203: if (isStarted())
204: return estimatedOriginalHours;
205: else
206: return getEstimatedHours();
207: }
208:
209: public void setEstimatedOriginalHoursField(
210: double estimatedOriginalHours) {
211: this .estimatedOriginalHours = estimatedOriginalHours;
212: }
213:
214: public double getEstimatedOriginalHoursField() {
215: return estimatedOriginalHours;
216: }
217:
218: public double getCompletedOriginalHours() {
219: return isCompleted() ? getEstimatedOriginalHours() : 0;
220: }
221:
222: public double getOverestimatedOriginalHours() {
223: if (isOverestimated(getEstimatedOriginalHours())) {
224: return getEstimatedOriginalHours() - getActualHours();
225: }
226: return 0.0;
227: }
228:
229: private boolean isOverestimated(double estimatedHours) {
230: return isCompleted() && getActualHours() < estimatedHours;
231: }
232:
233: public double getUnderestimatedOriginalHours() {
234: return isUnderestimated(getEstimatedOriginalHours()) ? getActualHours()
235: - getEstimatedOriginalHours()
236: : 0.0;
237: }
238:
239: private boolean isUnderestimated(double estimatedHours) {
240: return getActualHours() > estimatedHours;
241: }
242:
243: public double getOverestimatedHours() {
244: return isOverestimated(getEstimatedHours()) ? getEstimatedHours()
245: - getActualHours()
246: : 0.0;
247: }
248:
249: public double getUnderestimatedHours() {
250: if (isDiscovered()) {
251: double result = isCompleted() ? 0.0 : Math.max(
252: getEstimatedHours() - getActualHours(), 0.0);
253: return getActualHours() + result;
254: }
255: return isUnderestimated(getEstimatedHours()) ? getActualHours()
256: - getEstimatedHours() : 0.0;
257: }
258:
259: public double getIterationStartEstimatedHours() {
260: if (!disposition.isOriginal())
261: return 0;
262: return getEstimatedOriginalHours();
263: }
264:
265: private boolean isDiscovered() {
266: return TaskDisposition.DISCOVERED.equals(getDisposition());
267: }
268:
269: public boolean isCurrentlyActive(int personId) {
270: if (timeEntries != null && timeEntries.size() > 0) {
271: Iterator itr = timeEntries.iterator();
272: while (itr.hasNext()) {
273: TimeEntry entry = (TimeEntry) itr.next();
274: if (entry.isCurrentlyActive(personId)) {
275: return true;
276: }
277: }
278: }
279: return false;
280: }
281:
282: public void setCompleted(boolean flag) {
283: completionFlag = flag ? 1 : 0;
284: }
285:
286: public double getPostponedHours() {
287: return postponedHours;
288: }
289:
290: public void setPostponedHours(double postponedHours) {
291: this .postponedHours = postponedHours;
292: }
293:
294: public void postponeRemainingHours() {
295: setPostponedHours(getRemainingHours());
296: }
297:
298: public void postpone() {
299: postponeRemainingHours();
300: setCompleted(false);
301: }
302:
303: public void start() {
304: if (!isStarted()) {
305: setEstimatedOriginalHours(getEstimatedHours());
306: }
307: }
308:
309: public boolean isStarted() {
310: return estimatedOriginalHours > 0;
311: }
312: }
|