001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.pm.criticalpath;
051:
052: import java.io.IOException;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.util.Collection;
056: import java.util.Date;
057: import java.util.Iterator;
058:
059: import com.projity.datatype.Duration;
060: import com.projity.grouping.core.Node;
061: import com.projity.pm.dependency.Dependency;
062: import com.projity.pm.task.NormalTask;
063: import com.projity.pm.task.SubProj;
064: import com.projity.pm.task.Task;
065:
066: public final class TaskSchedule implements Cloneable {
067: public static final int CURRENT = 0;
068: public static final int EARLY = -1;
069: public static final int LATE = 1;
070:
071: //Persisted fields
072: private double percentComplete = 0D;
073:
074: // these are calculated, but are persisted anyway for reporting
075: private long rawDuration;
076: private long start;
077: private long finish;
078:
079: // Calculated fields that are transient
080: //TODO don't bother serializing these. When I make them transient, the program hangs
081: private Task task;
082: private int type;
083: private boolean forward = true;
084: private long dependencyDate = Dependency.NEEDS_CALCULATION;
085: private long remainingDependencyDate = 0;
086:
087: public TaskSchedule() {
088:
089: }
090:
091: public TaskSchedule(Task task, int type) {
092: init(task, type);
093: start = 0;
094: finish = 0;
095: }
096:
097: public void init(Task task, int type) {
098: this .task = task;
099: this .type = type;
100: if (type == EARLY)
101: forward = true;
102: else if (type == LATE)
103: forward = false;
104: dependencyDate = Dependency.NEEDS_CALCULATION;
105: invalidate();
106: }
107:
108: public void initSerialized(Task task, int type) {
109: this .task = task;
110: this .type = type;
111: if (type == EARLY)
112: forward = true;
113: else if (type == LATE)
114: forward = false;
115: }
116:
117: public void setTask(Task task) {
118: this .task = task;
119: }
120:
121: public void serialize(ObjectOutputStream s) throws IOException {
122: s.writeDouble(percentComplete);
123: s.writeLong(rawDuration);
124: s.writeLong(start);
125: s.writeLong(finish);
126: }
127:
128: //call init to complete initialization
129: public static TaskSchedule deserialize(ObjectInputStream s)
130: throws IOException, ClassNotFoundException {
131: TaskSchedule t = new TaskSchedule();
132: t.setPercentComplete(s.readDouble());
133: t.setRawDuration(s.readLong());
134: t.setStart(s.readLong());
135: t.setFinish(s.readLong());
136: return t;
137: }
138:
139: public Object clone() {
140: try {
141: return super .clone();
142: } catch (CloneNotSupportedException e) {
143: throw new InternalError();
144: }
145: }
146:
147: public Object cloneWithTask(Task task) {
148: TaskSchedule ts = (TaskSchedule) clone();
149: ts.setTask(task);
150: ts.invalidate();
151: return ts;
152: }
153:
154: //
155: // void copyDatesFrom(TaskSchedule from) {
156: // start = from.start;
157: // finish = from.finish;
158: // if (task.isWbsParent())
159: // rawDuration = from.rawDuration;
160: // dependencyDate = from.dependencyDate;
161: // remainingDependencyDate = from.remainingDependencyDate;
162: // }
163:
164: public long getBegin() {
165: return forward ? start : -finish;
166: }
167:
168: public long getEnd() {
169: return forward ? finish : -start;
170: }
171:
172: public long getWindowBegin() {
173: return forward ? task.getWindowEarlyStart() : -task
174: .getWindowLateFinish();
175: }
176:
177: public long getWindowEnd() {
178: return forward ? task.getWindowEarlyFinish() : -task
179: .getWindowLateStart();
180: }
181:
182: public void setBegin(long begin) {
183: if (forward)
184: start = begin;
185: else
186: finish = -begin;
187: }
188:
189: public void setEnd(long end) {
190: if (forward)
191: finish = end;
192: else
193: start = -end;
194: }
195:
196: public long getBeginDependency() {
197: if (dependencyDate == Dependency.NEEDS_CALCULATION)
198: dependencyDate = calcDependencyDate();
199:
200: return dependencyDate;
201: }
202:
203: public final void invalidate() {
204: start = 0;
205: finish = 0;
206: dependencyDate = Dependency.NEEDS_CALCULATION;
207: }
208:
209: public void copyDatesAfterClone(TaskSchedule from) {
210: start = from.start;
211: finish = from.finish;
212: dependencyDate = from.dependencyDate;
213: }
214:
215: final void invalidateDependencyDate() {
216: dependencyDate = Dependency.NEEDS_CALCULATION;
217: }
218:
219: /**
220: * @return Returns the percentComplete.
221: */
222: public final double getPercentComplete() {
223: return percentComplete;
224: }
225:
226: /**
227: * @param percentComplete The percentComplete to set.
228: */
229: public final void setPercentComplete(double percentComplete) {
230: this .percentComplete = percentComplete;
231: }
232:
233: /**
234: * @return Returns the rawDuration.
235: */
236: public final long getRawDuration() {
237: return rawDuration;
238: }
239:
240: /**
241: * @param rawDuration The rawDuration to set.
242: */
243: public final void setRawDuration(long rawDuration) {
244: this .rawDuration = rawDuration;
245:
246: }
247:
248: private final boolean isLate() {
249: return type == LATE;
250: }
251:
252: public final long getDependencyDate() {
253: return dependencyDate;
254: }
255:
256: public final void setDependencyDate(long dependencyDate) {
257: this .dependencyDate = dependencyDate;
258: }
259:
260: public final long getFinish() {
261: return finish;
262: }
263:
264: public final void setFinish(long finish) {
265: this .finish = finish;
266: }
267:
268: public final long getStart() {
269: return start;
270: }
271:
272: public final void setStart(long start) {
273: this .start = start;
274: }
275:
276: public TaskSchedule getOppositeSchedule() {
277: return task.getSchedule(forward ? LATE : EARLY);
278: }
279:
280: /**
281: * @return Returns the forward.
282: */
283: public final boolean isForward() {
284: return forward;
285: }
286:
287: public void setForward(boolean forward) {
288: if (this .forward != forward) {
289: this .forward = forward;
290: long s = start;
291: start = -finish;
292: finish = -s;
293: dependencyDate = -dependencyDate;
294: remainingDependencyDate = -remainingDependencyDate;
295:
296: }
297: }
298:
299: /**
300: * @param begin
301: */
302: public final void setRemainingDependencyDate(
303: long remainingDependencyDate) {
304: this .remainingDependencyDate = remainingDependencyDate;
305: }
306:
307: /**
308: * @return Returns the remainingDependencyDate.
309: */
310: public final long getRemainingDependencyDate() {
311: return remainingDependencyDate;
312: }
313:
314: /**
315: * Calculate a task's dates and see if the critical path changes. This means either: The task is currently critical, or the task becomes critical.
316: * This function is useful in seeing whether a backward pass is necessary. The backward pass is only necessary when the CP is modified.
317: * @param honorRequiredDates
318: * @param boundary
319: * @return
320: */
321: final boolean affectsCriticalPath(CalculationContext context) {
322: if (task.isOrWasCritical())
323: return true;
324:
325: calcStartAndFinish(context); // for parents, it will examine all children
326: long newEnd = getEnd();
327: long oppositeEnd = -getOppositeSchedule().getBegin();
328:
329: // System.out.println("Affects" + (oppositeEnd < newEnd) + " opposite " + new Date(oppositeEnd) + " new" + new Date(newEnd));
330: return (oppositeEnd < newEnd);
331: }
332:
333: final void calcDates(CalculationContext context) {
334: long oldBegin = getBegin();
335: long oldEnd = getEnd();
336: long newBegin = 0L;
337: long newEnd = 0L;
338: final long needsCalculation = Dependency.NEEDS_CALCULATION;
339: boolean unopenedSubproject = task.isSubproject()
340: && !((SubProj) task).isValidAndOpen();
341: boolean external = task.isExternal();
342: if (!external && !unopenedSubproject) {
343: if (context.taskReferenceType == PredecessorTaskList.TaskReference.PARENT_END) {
344: assignDatesFromChildren();
345: } else {
346: calcStartAndFinish(context); // for parents, it will examine all children
347: }
348: newBegin = getBegin();
349: newEnd = getEnd();
350: boolean reverseScheduled = task.isReverseScheduled();
351:
352: // if not just calculating early dates, check if reverse scheduled
353: if (!context.earlyOnly && reverseScheduled) {
354: TaskSchedule oppositeSchedule = getOppositeSchedule();
355: newBegin = -oppositeSchedule.getEnd();
356: newEnd = -oppositeSchedule.getBegin();
357: }
358:
359: if (context.assign && !unopenedSubproject) {
360: TaskSchedule currentSchedule = task
361: .getCurrentSchedule();
362: if (newBegin < 0) {
363: currentSchedule.setStart(-newEnd);
364: currentSchedule.setFinish(-newBegin);
365: currentSchedule
366: .setRemainingDependencyDate(-remainingDependencyDate);
367: } else {
368: currentSchedule.setStart(newBegin);
369: currentSchedule.setFinish(newEnd);
370: currentSchedule
371: .setRemainingDependencyDate(remainingDependencyDate);
372:
373: }
374: currentSchedule.setDependencyDate(dependencyDate);
375: // for parents, set current schedule's duration
376: if (context.taskReferenceType == PredecessorTaskList.TaskReference.PARENT_END) {
377: //TODO this only needs to be done if advancement changed on a task
378: currentSchedule.updateDurationFromDates(); // calculate duration based on parent start/end
379: ((NormalTask) (currentSchedule.task))
380: .assignActualDatesFromChildren();
381: }
382: // System.out.println(task.getName() + " Set current " + new Date(currentSchedule.getStart()) + " " + new Date(currentSchedule.getFinish()));
383:
384: }
385: } else if (external) { // external
386: TaskSchedule currentSchedule = task.getCurrentSchedule();
387: newBegin = currentSchedule.getBegin();
388: newEnd = currentSchedule.getEnd();
389: oldBegin = 0;
390:
391: }
392:
393: if (oldBegin == newBegin && oldEnd == newEnd) {
394: // System.out.println("no change");
395: if (!unopenedSubproject)
396: return;
397: }
398:
399: if (unopenedSubproject) { // need to put back old dates because we want the reverse pass to work right
400: newBegin = oldBegin;
401: newEnd = oldEnd;
402: }
403: Collection list = task.getDependencyList(!forward);
404: Task parent = task.getWbsParentTask();
405: TaskSchedule parentSchedule = null;
406: long parentEnd = 0;
407: if (parent != null) {
408: parentSchedule = parent.getSchedule(type);
409: parentEnd = parentSchedule.getEnd();
410: }
411:
412: if (context.taskReferenceType == PredecessorTaskList.TaskReference.PARENT_BEGIN) {
413: if (oldBegin != newBegin) { // if parent start (finish) changed, then all of its children need to me marked
414:
415: flagChildren();
416: setDependencyDate(newBegin); //This fixes a problem in incorrect propagation of constraints to children hk 16/8/05
417:
418: // make sure that in second pass over this, the schedule will change so it will be marked in backward pass. However, we don't want to lose
419: // information - specificially whether this task affects its parent's task. In case it does, it is marked with a special value (Dependency.NEEDS_CALCULATION)
420: // This is a bit of a hack, but it's for optimization purposes.
421:
422: if (parentEnd == oldEnd)
423: setEnd(needsCalculation);
424: else
425: setEnd(0);
426: }
427: return;
428: }
429:
430: Dependency dependency;
431:
432: if (list.isEmpty()) {
433: if (!task.isExternal() && task != context.sentinel) { // When the task is the sentinel, do nothing, otherwise find dependency and update it
434: dependency = (Dependency) context.sentinel
435: .getDependencyList(forward).find(forward, task); // find sentinel's dependency concerning this task
436:
437: //debug
438: if (dependency == null) {
439: dependency = (Dependency) context.sentinel
440: .getDependencyList(forward).find(forward,
441: task); // find sentinel's dependency concerning this task
442: }
443: if (dependency != null) { // tasks in a subproject won't have a sentinel dependency
444: dependency.calcDependencyDate(forward, newBegin,
445: newEnd, false); // calculate it to store off value
446: context.sentinel
447: .setCalculationStateCount(context.stateCount); // need to process successor(predecessor) later on in pass
448: context.sentinel.getSchedule(type)
449: .setDependencyDate(needsCalculation); //sentinel needs dependencies calculated - I assume more than one
450: }
451: }
452: } else {
453: // Go Thru Successors (Predecessors) and calculate a dependency date for them and mark them for further treatment. There is an optimization here:
454: // If the successor(pred) task only has one predecessor(succ), then just set its dependency date instead of calculating it. This avoids reprocessing
455: // the predecessor(successor) list of that task later on. Since in most cases, a task has only one predecessor, this saves time.
456: for (Iterator d = list.iterator(); d.hasNext();) {
457: Task dependencyTask;
458: TaskSchedule dependencyTaskSchedule;
459:
460: dependency = (Dependency) d.next();
461: if (dependency.isDisabled())
462: continue;
463: dependencyTask = (Task) dependency.getTask(!forward); // get the successor(pred) task
464: dependencyTaskSchedule = dependencyTask
465: .getSchedule(type);
466: // if this task is the only predecessor(successor) for the successor(predecessor) task, avoid long calculation and just calculate the date, otherwise
467: // flag the task for later calculation
468: long dependencyCount = dependencyTask
469: .getDependencyList(forward).size();
470:
471: long dep = newBegin; // by default (if no preds for example)
472: if (dependencyCount > 0) {
473: boolean useSooner = !dependencyTask.isWbsParent()
474: && dependencyTask.hasDuration();
475: dep = dependency.calcDependencyDate(forward,
476: newBegin, newEnd, useSooner); // calculate it and store off value
477: if (dependencyCount > 1) // can't just set date directly because more than one
478: dep = needsCalculation; // it will need to be calculated later
479: }
480: dependencyTaskSchedule.setDependencyDate(dep);
481: dependencyTask
482: .setCalculationStateCount(context.stateCount); // need to process successor(predecessor) later on in pass
483: }
484: }
485:
486: // mark parent also if it is affected and isn't marked already
487: if (parent != null
488: && parent.getCalculationStateCount() != context.stateCount) {
489: long parentBegin = parentSchedule.getBegin();
490: if (oldEnd == 0 || oldEnd == Dependency.NEEDS_CALCULATION) // in case a parent itself modifies its parent or this task has been invalidated (if it is the task modified)
491: parent.setCalculationStateCount(context.stateCount); // mark its parent
492: else if ((oldEnd != newEnd && (newEnd > parentEnd || oldEnd == parentEnd)) // if this task previously determined the parent end date, the parent will need to be recalculated
493: || (oldBegin != newBegin && (newBegin < parentBegin || oldBegin == parentBegin))) { // if this task previously determined parent start date
494: parent.setCalculationStateCount(context.stateCount); // mark parent
495: }
496: }
497: if (context.pass == 1) // only mark during first pass.
498: task.setCalculationStateCount(context.stateCount + 1); // signal that backward pass needs to be done on this
499: }
500:
501: private void flagChildren() {
502: int stateCount = task.getCalculationStateCount();
503: Collection children = task.getWbsChildrenNodes();
504: if (children == null)
505: return;
506: Object current;
507: Iterator i = children.iterator();
508: while (i.hasNext()) {
509: current = ((Node) i.next()).getImpl();
510: if (!(current instanceof Task))
511: continue;
512: ((Task) current).setCalculationStateCount(stateCount); // mark parent
513: }
514:
515: }
516:
517: private void updateDurationFromDates() {
518: setRawDuration(task.getEffectiveWorkCalendar().compare(
519: getFinish(), getStart(), false));
520:
521: }
522:
523: public void assignDatesFromChildren() {
524: Collection children = task.getWbsChildrenNodes();
525: if (children == null)
526: return;
527: long begin = Long.MAX_VALUE;
528: long end = Long.MIN_VALUE;
529:
530: Iterator i = children.iterator();
531: NormalTask child;
532: Object current;
533: TaskSchedule childSchedule;
534: boolean estimated = false;
535: while (i.hasNext()) {
536: current = ((Node) i.next()).getImpl();
537: if (!(current instanceof NormalTask))
538: continue;
539:
540: child = (NormalTask) current;
541: estimated |= child.isEstimated();
542: childSchedule = child.getSchedule(type);
543: long childBegin = childSchedule.getBegin();
544: if (childBegin != 0)
545: begin = Math.min(begin, childBegin);
546: long childEnd = childSchedule.getEnd();
547: if (childEnd != 0)
548: end = Math.max(end, childEnd);
549: }
550:
551: if (begin != Long.MAX_VALUE && begin != 0) // in case of invalid subproject having no children
552: setBegin(begin);
553: else
554: return;
555: if (end != Long.MIN_VALUE && end != 0)
556: setEnd(end);
557: else
558: return;// System.out.println("begin is " + new Date(begin) + " end " + new Date(end));
559: long duration = task.getEffectiveWorkCalendar().compare(end,
560: begin, false);
561: duration = Duration.setAsEstimated(duration, estimated);
562: ((NormalTask) task).setEstimated(estimated);
563: setRawDuration(duration);
564: // System.out.println("parent " + task + " begin " + new Date(start) + " " +start+ " end " + new Date(finish) + " " + finish);
565: // if (begin == 0)
566: // System.out.println("bad date 0");//assignDatesFromChildren();
567: }
568:
569: /**
570: * Calculate the date which predecessors(successors) push this task to start by looping thru all of its predecesors(succ) and choosing the max value
571: * @return max date
572: */
573: long calcDependencyDate() {
574: long result = 0;
575: Dependency dependency;
576: long current;
577: Collection list = task.getDependencyList(forward);
578: for (Iterator i = list.iterator(); i.hasNext();) {
579: dependency = (Dependency) i.next();
580: if (dependency.isDisabled())
581: continue;
582: current = dependency.getDate(forward);
583: if (result == 0)
584: result = current;
585: else
586: result = Math.max(result, current);
587: }
588: setDependencyDate(result);
589: return result;
590: }
591:
592: /**
593: * During the forward pass, begin is early dates, during backward pass, it is late dates.
594: * When reverse scheduling, the backward pass is executed first, then the forward.
595: * The late schedule uses a trick: dates are returned as negative values. This lets me to use the same min/max code. Also
596: * the calendar code knows to reverse durations when the date is negative.
597: * @param boundary
598: * @param honorRequiredDates
599: * @param early
600: * @return
601: */
602: private void calcStartAndFinish(CalculationContext context) {
603: if ("test".equals(task.getName()))
604: System.out.println("test");
605:
606: long begin = getBeginDependency();
607:
608: Task parent = task.getWbsParentTask();
609:
610: //boolean useSooner = (forward != task.hasDuration()); // shortcut: if forward and is milestone, use sooner, otherwise later. And conversely, if reverse and isn't milestone, use sooner, othewise later
611: boolean useSooner = !task.hasDuration();
612:
613: if (parent != null) {
614: TaskSchedule parentSchedule = parent.getSchedule(type);
615: long parentDependency = parentSchedule.getBeginDependency();
616: long parentWindow = parentSchedule.getWindowBegin();
617: if (parentDependency == 0
618: || (parentWindow != 0 && parentWindow > parentDependency))
619: parentDependency = parentWindow;
620: // in case where parent determines start time, make sure that this
621: // task starts either at day end if milesetone, or at next working
622: // day otherwise if parent is at day end
623: if (parentDependency != 0
624: && (begin == 0 || parentDependency > begin)) {
625: begin = task.getEffectiveWorkCalendar().add(
626: parentDependency, 0, useSooner);
627: }
628: }
629: if (task.isInSubproject())
630: begin = Math.max(begin, context.forward ? task
631: .getOwningProject().getStartConstraint() : -task
632: .getOwningProject().getEnd());
633:
634: // Soft constraints
635: long windowBegin = getWindowBegin();
636:
637: // Make sure the task starts after its early start window date. This
638: // is a soft constraint during forward pass.
639:
640: // For SNET
641: if (windowBegin != 0) {
642: if (begin == 0)
643: begin = windowBegin;
644: else if (windowBegin < begin) {
645: if (task.startsBeforeProject()) // case of task starting before project start but has SNET constraint
646: begin = windowBegin;
647: } else
648: begin = windowBegin;
649: }
650:
651: // For FNET
652: long windowEnd = getWindowEnd();
653: if (windowEnd != 0) {
654: if (begin == 0)
655: begin = Long.MIN_VALUE;
656: begin = Math.max(begin, task.calcOffsetFrom(windowEnd,
657: windowEnd, false, false, useSooner));
658: // System.out.println("Applying FNET " + task + " " + d(windowEnd) + " begin is now " + d(begin));
659: }
660:
661: // Hard constraints
662: if (context.honorRequiredDates) {
663: // If honoring required dates, check the hard constraint that the
664: // task is finished by its late finish date.
665: // Note that currently late finish has priority over early start.
666: long oppositeEnd = -getOppositeSchedule().getWindowBegin(); // For FNLT
667: if (oppositeEnd != 0) {
668: if (begin == 0)
669: begin = Long.MAX_VALUE;
670: begin = Math.min(begin, task.calcOffsetFrom(
671: oppositeEnd, dependencyDate, false, false,
672: useSooner));
673: // System.out.println("Applying FNLT " + task + " " + d(oppositeEnd) + " begin is now " + d(begin));
674: }
675: // For SNLT
676: long oppositeBegin = -getOppositeSchedule().getWindowEnd();
677: if (oppositeBegin != 0) {
678: if (begin == 0)
679: begin = Long.MAX_VALUE;
680: begin = Math.min(begin, oppositeBegin);
681: // System.out.println("Applying SNLT " + task + " " + d(oppositeBegin) + " begin is now " + d(begin));
682: }
683: }
684:
685: if (begin == 0) {
686: if (!task.isWbsParent()) // if no constraints at all
687: begin = context.boundary;
688: }
689: if (task.isSubproject()) {// subprojects can't start before their project start
690: SubProj subProj = (SubProj) task;
691: if (!subProj.isValidAndOpen())
692: return;
693: if (task.getPredecessorList().size() == 0
694: && task.getConstraintDate() == 0)
695: return;
696: begin = Math.max(begin, context.forward ? subProj
697: .getSubproject().getStartConstraint() : -subProj
698: .getSubproject().getEnd());
699: }
700:
701: long levelingDelay = task.getLevelingDelay();
702:
703: if (Duration.millis(levelingDelay) != 0)
704: begin = task.getEffectiveWorkCalendar().add(begin,
705: levelingDelay, useSooner);
706:
707: long remainingBegin = begin;
708:
709: if (forward == context.forward)
710: setRemainingDependencyDate(remainingBegin); // the date which predecessors push the task to start at. Actuals can override this
711:
712: if (context.forward) {
713: long actualStart = task.getActualStart();
714: if (actualStart != 0)
715: begin = actualStart;
716: }
717: setBegin(begin);
718: long end = ((Task) task).calcOffsetFrom(begin, remainingBegin,
719: true, true, true);
720: setEnd(end);
721: }
722:
723: public void dump() {
724: System.out.println("Task " + task + " schedule " + type
725: + " start " + new Date(start) + " finish "
726: + new Date(finish));
727: }
728:
729: /**
730: * Function used for tracing dates when debugging
731: * @param l date either postive or negative
732: * @return a date using the absolute value of the input
733: */
734: public static Date d(long l) {
735: return new Date(Math.abs(l));
736: }
737:
738: /**
739: * Structure used to store variables related to the pass
740: */
741: static class CalculationContext {
742: int stateCount;
743: boolean forward;
744: boolean honorRequiredDates;
745: Task sentinel;
746: int taskReferenceType;
747: long boundary;
748: boolean earlyOnly;
749: boolean assign;
750: int scheduleType;
751: int pass;
752: }
753: }
|