0001: /*
0002: The contents of this file are subject to the Common Public Attribution License
0003: Version 1.0 (the "License"); you may not use this file except in compliance with
0004: the License. You may obtain a copy of the License at
0005: http://www.projity.com/license . The License is based on the Mozilla Public
0006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
0007: software over a computer network and provide for limited attribution for the
0008: Original Developer. In addition, Exhibit A has been modified to be consistent
0009: with Exhibit B.
0010:
0011: Software distributed under the License is distributed on an "AS IS" basis,
0012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
0013: specific language governing rights and limitations under the License. The
0014: Original Code is OpenProj. The Original Developer is the Initial Developer and
0015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
0016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
0017:
0018: Alternatively, the contents of this file may be used under the terms of the
0019: Projity End-User License Agreeement (the Projity License), in which case the
0020: provisions of the Projity License are applicable instead of those above. If you
0021: wish to allow use of your version of this file only under the terms of the
0022: Projity License and not to allow others to use your version of this file under
0023: the CPAL, indicate your decision by deleting the provisions above and replace
0024: them with the notice and other provisions required by the Projity License. If
0025: you do not delete the provisions above, a recipient may use your version of this
0026: file under either the CPAL or the Projity License.
0027:
0028: [NOTE: The text of this license may differ slightly from the text of the notices
0029: in Exhibits A and B of the license at http://www.projity.com/license. You should
0030: use the latest text at http://www.projity.com/license for your modifications.
0031: You may not remove this license text from the source files.]
0032:
0033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
0034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
0035: an open source solution from Projity. Attribution URL: http://www.projity.com
0036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
0037: alternatives listed on http://www.projity.com/logo
0038:
0039: Display of Attribution Information is required in Larger Works which are defined
0040: in the CPAL as a work which combines Covered Code or portions thereof with code
0041: not governed by the terms of the CPAL. However, in addition to the other notice
0042: obligations, all copies of the Covered Code in Executable and Source Code form
0043: distributed must, as a form of attribution of the original author, include on
0044: each user interface screen the "OpenProj" logo visible to all users. The
0045: OpenProj logo should be located horizontally aligned with the menu bar and left
0046: justified on the top left of the screen adjacent to the File menu. The logo
0047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
0048: must direct them back to http://www.projity.com.
0049: */
0050: package com.projity.pm.task;
0051:
0052: import java.util.Collection;
0053: import java.util.Date;
0054: import java.util.HashSet;
0055: import java.util.Iterator;
0056: import java.util.LinkedList;
0057: import java.util.List;
0058:
0059: import javax.swing.SwingUtilities;
0060:
0061: import org.apache.commons.collections.Closure;
0062: import org.apache.commons.collections.CollectionUtils;
0063: import org.apache.commons.collections.Predicate;
0064: import org.apache.commons.collections.functors.TruePredicate;
0065:
0066: import com.projity.association.AssociationFormatParameters;
0067: import com.projity.association.AssociationList;
0068: import com.projity.association.AssociationListFormat;
0069: import com.projity.configuration.CircularDependencyException;
0070: import com.projity.configuration.Configuration;
0071: import com.projity.configuration.Settings;
0072: import com.projity.datatype.Duration;
0073: import com.projity.document.Document;
0074: import com.projity.document.ObjectEvent;
0075: import com.projity.field.CustomFields;
0076: import com.projity.field.CustomFieldsImpl;
0077: import com.projity.field.Field;
0078: import com.projity.field.FieldContext;
0079: import com.projity.field.FieldParseException;
0080: import com.projity.functor.CollectionVisitor;
0081: import com.projity.functor.ObjectVisitor;
0082: import com.projity.grouping.core.Node;
0083: import com.projity.grouping.core.NodeList;
0084: import com.projity.grouping.core.OutlineCollection;
0085: import com.projity.grouping.core.hierarchy.BelongsToHierarchy;
0086: import com.projity.grouping.core.model.NodeModel;
0087: import com.projity.grouping.core.summaries.DivisionSummaryVisitor;
0088: import com.projity.grouping.core.summaries.LeafWalker;
0089: import com.projity.options.CalculationOption;
0090: import com.projity.options.CalendarOption;
0091: import com.projity.pm.assignment.Assignment;
0092: import com.projity.pm.assignment.HasTimeDistributedData;
0093: import com.projity.pm.assignment.timesheet.UpdatesFromTimesheet;
0094: import com.projity.pm.calendar.HasCalendar;
0095: import com.projity.pm.calendar.WorkCalendar;
0096: import com.projity.pm.costing.EarnedValueMethodType;
0097: import com.projity.pm.costing.ExpenseType;
0098: import com.projity.pm.costing.HasExpenseType;
0099: import com.projity.pm.criticalpath.PredecessorTaskList;
0100: import com.projity.pm.criticalpath.ScheduleWindow;
0101: import com.projity.pm.criticalpath.TaskSchedule;
0102: import com.projity.pm.dependency.Dependency;
0103: import com.projity.pm.dependency.DependencyFormat;
0104: import com.projity.pm.dependency.DependencyService;
0105: import com.projity.pm.dependency.DependencyType;
0106: import com.projity.pm.dependency.HasDependencies;
0107: import com.projity.pm.dependency.HasDependenciesImpl;
0108: import com.projity.pm.key.HasKey;
0109: import com.projity.pm.key.HasKeyImpl;
0110: import com.projity.pm.resource.Resource;
0111: import com.projity.pm.scheduling.CanBeLeveled;
0112: import com.projity.pm.scheduling.ConstraintType;
0113: import com.projity.pm.scheduling.Schedule;
0114: import com.projity.pm.scheduling.ScheduleUtil;
0115: import com.projity.pm.snapshot.Snapshottable;
0116: import com.projity.pm.snapshot.SnapshottableImpl;
0117: import com.projity.server.access.ErrorLogger;
0118: import com.projity.server.data.DataObject;
0119: import com.projity.strings.Messages;
0120: import com.projity.util.Alert;
0121: import com.projity.util.DateTime;
0122: import com.projity.util.Environment;
0123:
0124: /**
0125: * @stereotype thing
0126: */
0127: public abstract class Task implements HasKey, HasNotes, HasCalendar,
0128: HasDependencies, Schedule, ScheduleWindow, Snapshottable,
0129: HasTimeDistributedData, HasPriority, CustomFields,
0130: BelongsToDocument, BelongsToHierarchy, DataObject,
0131: CanBeLeveled, UpdatesFromTimesheet, HasExpenseType {
0132: static final long serialVersionUID = 786665335611L;
0133: /**
0134: * @link aggregation
0135: * @supplierCardinality 0..*
0136: */
0137: /*# com.projity.pm.assignment.Assignment lnkAssignment; */
0138: transient Project project;
0139: transient Project owningProject;
0140: transient TaskSchedule currentSchedule = null;
0141:
0142: protected transient int debugDependencyOrder = -1;
0143: protected transient TaskSchedule earlySchedule;
0144: protected transient TaskSchedule lateSchedule;
0145: protected transient Snapshottable snapshots;
0146: protected transient HasDependencies dependencies;
0147: protected String notes = "";
0148: protected String wbs = "";
0149: protected boolean markTaskAsMilestone = false;
0150: protected transient CustomFieldsImpl customFields = new CustomFieldsImpl();
0151: protected transient HasKeyImpl hasKey;
0152: protected transient boolean external = false;
0153: protected transient long projectId = 0;
0154: double physicalPercentComplete = 0.0;
0155:
0156: protected long windowEarlyStart = 0;
0157: protected long windowEarlyFinish = 0;
0158: protected long windowLateStart = 0;
0159: protected long windowLateFinish = 0;
0160:
0161: protected long actualStart = 0;
0162:
0163: protected long levelingDelay = 0L;
0164: protected transient int calculationStateCount = 0;
0165: protected transient boolean markerStatus = false;
0166: protected int earnedValueMethod = EarnedValueMethodType.PERCENT_COMPLETE;
0167: protected static Field startFieldInstance = null;
0168:
0169: protected int constraintType = ConstraintType.ASAP;
0170: protected long deadline = 0;
0171: protected int expenseType = ExpenseType.NONE;
0172: protected transient boolean inSubproject = false;
0173: protected transient long lastSavedStart = 0L;
0174: protected transient long lastSavedFinish = 0L;
0175: protected transient boolean dirty;
0176:
0177: public static Field getStartField() {
0178: if (startFieldInstance == null)
0179: startFieldInstance = Configuration
0180: .getFieldFromId("Field.start");
0181: return startFieldInstance;
0182: }
0183:
0184: /**
0185: * @return Returns the taskSchedule.
0186: */
0187: public TaskSchedule getCurrentSchedule() {
0188: return currentSchedule;
0189: }
0190:
0191: private static Field endFieldInstance = null;
0192:
0193: public static Field getEndField() {
0194: if (endFieldInstance == null)
0195: endFieldInstance = Configuration
0196: .getFieldFromId("Field.finish");
0197: return endFieldInstance;
0198: }
0199:
0200: public Task() {
0201: this (true);
0202: }
0203:
0204: public Task(boolean local) {
0205: hasKey = new HasKeyImpl(local, this );
0206: currentSchedule = new TaskSchedule();
0207: initializeTransientTaskObjects();
0208: }
0209:
0210: public boolean isReadOnly() {
0211: return isExternal()
0212: || isSubproject()
0213: || (getOwningProject() != null && getOwningProject()
0214: .isReadOnly());
0215: }
0216:
0217: protected void initializeTransientTaskObjects() {
0218: currentSchedule.initSerialized(this , TaskSchedule.CURRENT);
0219: earlySchedule = new TaskSchedule(this , TaskSchedule.EARLY);
0220: lateSchedule = new TaskSchedule(this , TaskSchedule.LATE);
0221: snapshots = new SnapshottableImpl(Settings.numBaselines());
0222: dependencies = new HasDependenciesImpl(this );
0223:
0224: createSnapshot(CURRENT);
0225: ((TaskSnapshot) getCurrentSnapshot())
0226: .setCurrentSchedule(currentSchedule); // put the current schedule in the snapshot
0227: setLastSavedStart(currentSchedule.getStart());
0228: setLastSavedFinish(currentSchedule.getFinish());
0229: }
0230:
0231: protected void initializeTransientTaskObjectsAfterDeserialization() {
0232: earlySchedule = new TaskSchedule(this , TaskSchedule.EARLY);
0233: lateSchedule = new TaskSchedule(this , TaskSchedule.LATE);
0234: dependencies = new HasDependenciesImpl(this );
0235:
0236: currentSchedule = ((TaskSnapshot) getCurrentSnapshot())
0237: .getCurrentSchedule();
0238: currentSchedule.initSerialized(this , TaskSchedule.CURRENT);
0239:
0240: setLastSavedStart(currentSchedule.getStart());
0241: setLastSavedFinish(currentSchedule.getFinish());
0242: }
0243:
0244: public TaskSnapshot getBaselineSnapshot() {
0245: return (TaskSnapshot) getSnapshot(CalculationOption
0246: .getInstance().getEarnedValueBaselineId());
0247: }
0248:
0249: private TaskSnapshot createSnapshot(Object snapshotId) {
0250: TaskSnapshot newOne = new TaskSnapshot();
0251: setSnapshot(snapshotId, newOne);
0252: return newOne;
0253: }
0254:
0255: /*
0256: * (non-Javadoc)
0257: *
0258: * @see com.projity.pm.scheduling.Schedule#getHasCalendar()
0259: */
0260: public HasCalendar getHasCalendar() {
0261: return this ;
0262: }
0263:
0264: public Assignment getBaselineAssignment(Resource resource) {
0265: TaskSnapshot baseline = getBaselineSnapshot();
0266: if (baseline == null)
0267: return null;
0268: return getBaselineSnapshot().findAssignment(resource);
0269: }
0270:
0271: public Assignment getBaselineAssignment(Resource resource,
0272: Object snapshot, boolean createIfDoesntExist) {
0273: TaskSnapshot baselineSnapshot = (TaskSnapshot) getSnapshot(snapshot);
0274: if (baselineSnapshot == null) {
0275: if (createIfDoesntExist)
0276: baselineSnapshot = createSnapshot(snapshot);
0277: else
0278: return null;
0279: }
0280: Assignment assignment = baselineSnapshot
0281: .findAssignment(resource);
0282:
0283: if (assignment == null && createIfDoesntExist) {
0284: assignment = Assignment.getInstance(this , resource, 1.0, 0);
0285: baselineSnapshot.addAssignment(assignment);
0286: TaskSchedule baselineSchedule = new TaskSchedule(this ,
0287: TaskSchedule.CURRENT);
0288: //baselineSnapshot.set
0289: baselineSnapshot.setCurrentSchedule(baselineSchedule);
0290: assignment.setTaskSchedule(baselineSchedule);
0291: assignment.convertToBaselineAssignment(true);
0292: }
0293:
0294: return assignment;
0295: }
0296:
0297: /**
0298: * @return Returns the project.
0299: */
0300: public Project getProject() {
0301: return project;
0302: }
0303:
0304: public Document getDocument() {
0305: return getProject();
0306: }
0307:
0308: public WorkCalendar getProjectCalendar() {
0309: return project.getWorkCalendar();
0310: }
0311:
0312: public void connectToProject() {
0313: project.connectTask(this );
0314: }
0315:
0316: protected boolean isInitialized() {
0317: return getProject() != null && getProject().isInitialized();
0318: }
0319:
0320: /**
0321: * @param project The project to set.
0322: */
0323: public void setProject(Project project) {
0324: this .project = project;
0325: }
0326:
0327: public static Closure forProject(Closure visitor) {
0328: return new ObjectVisitor(visitor) {
0329: protected final Object getObject(Object caller) {
0330: return ((Task) caller).getProject();
0331: }
0332: };
0333: }
0334:
0335: // Don't know if need this or not. In any case, need to work out hierarchy treatment.
0336: // public NodeHierarchy getNodeHierarchy() {
0337: // //TODO use correct view ?
0338: // return Document.getTestInstance().getContextManager().getGlobalContext().getTaskContext(View.UNNAMED).getModel().getHierarchy();
0339: // }
0340: //
0341: //
0342: public static Closure forParent(Closure visitor) {
0343: return new ObjectVisitor(visitor) {
0344: protected final Object getObject(Object caller) {
0345: return ((Task) caller).getProject().getTaskOutline()
0346: .getHierarchy().getParent((Node) caller);
0347: }
0348: };
0349: }
0350:
0351: public static Closure forAllChildren(Closure visitor,
0352: Predicate filter) {
0353: return new CollectionVisitor(visitor, filter) {
0354: protected Collection getCollection(Object caller) {
0355: return ((Task) caller).getProject().getTaskOutline()
0356: .getHierarchy().getChildren((Node) caller);
0357: }
0358: };
0359: }
0360:
0361: public static Closure forAllChildren(Closure visitor) {
0362: return forAllChildren(visitor, TruePredicate.INSTANCE);
0363: }
0364:
0365: /**
0366: * @return Returns the notes.
0367: */
0368: public String getNotes() {
0369: return notes;
0370: }
0371:
0372: /**
0373: * @param notes The notes to set.
0374: */
0375: public void setNotes(String notes) {
0376: this .notes = notes;
0377:
0378: }
0379:
0380: public double getCustomCost(int i) {
0381: return customFields.getCustomCost(i);
0382: }
0383:
0384: public long getCustomDate(int i) {
0385: return customFields.getCustomDate(i);
0386: }
0387:
0388: public long getCustomDuration(int i) {
0389: return customFields.getCustomDuration(i);
0390: }
0391:
0392: public long getCustomFinish(int i) {
0393: return customFields.getCustomFinish(i);
0394: }
0395:
0396: public boolean getCustomFlag(int i) {
0397: return customFields.getCustomFlag(i);
0398: }
0399:
0400: public double getCustomNumber(int i) {
0401: return customFields.getCustomNumber(i);
0402: }
0403:
0404: public long getCustomStart(int i) {
0405: return customFields.getCustomStart(i);
0406: }
0407:
0408: public String getCustomText(int i) {
0409: return customFields.getCustomText(i);
0410: }
0411:
0412: public void setCustomCost(int i, double cost) {
0413: customFields.setCustomCost(i, cost);
0414: }
0415:
0416: public void setCustomDate(int i, long date) {
0417: customFields.setCustomDate(i, date);
0418: }
0419:
0420: public void setCustomDuration(int i, long duration) {
0421: customFields.setCustomDuration(i, duration);
0422: }
0423:
0424: public void setCustomFinish(int i, long finish) {
0425: customFields.setCustomFinish(i, finish);
0426: }
0427:
0428: public void setCustomFlag(int i, boolean flag) {
0429: customFields.setCustomFlag(i, flag);
0430: }
0431:
0432: public void setCustomNumber(int i, double number) {
0433: customFields.setCustomNumber(i, number);
0434: }
0435:
0436: public void setCustomStart(int i, long start) {
0437: customFields.setCustomStart(i, start);
0438: }
0439:
0440: public void setCustomText(int i, String text) {
0441: customFields.setCustomText(i, text);
0442: }
0443:
0444: /************************************************************************************************
0445: * Task Schedule and methods
0446: *************************************************************************************************/
0447:
0448: /**
0449: * @return start from task schedule
0450: */
0451: public long getStart() {
0452: return currentSchedule.getStart();
0453: }
0454:
0455: /**
0456: * Set the dependency start, not the task schedule start. Only the CP can do that
0457: */
0458: public void setStart(long start, FieldContext fieldContext) {
0459: if (getActualStart() != 0) // you can't change the start date of an in progress task
0460: return;
0461: start = CalendarOption.getInstance().makeValidStart(start,
0462: false);
0463: if (start != getStart()) {
0464: long projectStart = getProject().getStart();
0465: if (projectStart > start) {
0466: if (!Alert
0467: .okCancel(Messages
0468: .getString("Message.allowTaskStartBeforeProjectStart")))
0469: return;
0470: setScheduleConstraint(ConstraintType.SNLT, start);
0471:
0472: } else {
0473: setScheduleConstraint(ConstraintType.SNET, start);
0474: }
0475: }
0476: }
0477:
0478: public void setStart(long start) {
0479: setStart(start, null);
0480: }
0481:
0482: /**
0483: * @return
0484: */
0485: public long getEnd() {
0486: return currentSchedule.getFinish();
0487: }
0488:
0489: /**
0490: * @param end
0491: */
0492: public void setEnd(long end) {
0493: setEnd(end, null);
0494: }
0495:
0496: public void setEnd(long end, FieldContext fieldContext) {
0497: if (end != getEnd()) { // only set if it changes
0498: getCurrentSchedule().setEnd(end);
0499: }
0500: }
0501:
0502: /**
0503: * @return Returns the dependencyStart.
0504: */
0505: public long getDependencyStart() {
0506: return currentSchedule.getRemainingDependencyDate();
0507: }
0508:
0509: /**
0510: * @param dependencyStart The dependencyStart to set.
0511: */
0512: public void setDependencyStart(long dependencyStart) {
0513: currentSchedule.setRemainingDependencyDate(dependencyStart);
0514: }
0515:
0516: /**
0517: * Sets the duration without controls that setDuration performs
0518: *
0519: * @param duration
0520: */
0521: public abstract void setRawDuration(long duration);
0522:
0523: public long getRawDuration() {
0524: return currentSchedule.getRawDuration();
0525: }
0526:
0527: public void clearDuration() {
0528: setRawDuration(0);
0529: }
0530:
0531: /**
0532: * @return
0533: */
0534: public WorkCalendar getEffectiveWorkCalendar() {
0535: return null; // TODO figure out if this belong shere
0536: }
0537:
0538: public boolean isMilestone() {
0539: return false;
0540: }
0541:
0542: public boolean isSubproject() {
0543: return false;
0544: }
0545:
0546: transient Collection wbsChildrenNodes = null;
0547: transient Task wbsParentTask = null;
0548: transient Resource delegatedTo = null;
0549:
0550: /**
0551: * @return Returns the wbsChildrenNodes.
0552: */
0553: public Collection getWbsChildrenNodes() {
0554: return wbsChildrenNodes;
0555: }
0556:
0557: /**
0558: * @param wbsChildrenNodes The wbsChildrenNodes to set.
0559: */
0560: public void setWbsChildrenNodes(Collection wbsChildrenNodes) {
0561: //System.out.println(this + " setWbsChildrenNodes " + wbsChildrenNodes);
0562: this .wbsChildrenNodes = wbsChildrenNodes;
0563: }
0564:
0565: public List getWbsChildrenTasks() {
0566: List children = (List) getWbsChildrenNodes();
0567: return NodeList.nodeListToImplList(children);
0568: }
0569:
0570: /**
0571: * @return Returns the wbsParent.
0572: */
0573: public Task getWbsParentTask() {
0574: return wbsParentTask;
0575: }
0576:
0577: /**
0578: * @param wbsParent The wbsParent to set.
0579: */
0580: public void setWbsParent(Task wbsParentTask) {
0581: this .wbsParentTask = wbsParentTask;
0582: }
0583:
0584: /**
0585: * See if a this task is a child, grandchild... of another
0586: * @param potentialParentTask - task to see if parent
0587: * @return true if the task descends from potentialParentTask
0588: */
0589: public boolean wbsDescendentOf(Task potentialParentTask) {
0590: if (this == potentialParentTask)
0591: return true;
0592: Task parentTask = getWbsParentTask();
0593: if (parentTask == null)
0594: return false;
0595: return parentTask.wbsDescendentOf(potentialParentTask);
0596:
0597: }
0598:
0599: public boolean isParent() {
0600: return isWbsParent();
0601: }
0602:
0603: /* (non-Javadoc)
0604: * @see com.projity.pm.task.TaskSpecificFields#isParent()
0605: */
0606: public boolean isWbsParent() {
0607: if (wbsChildrenNodes == null || wbsChildrenNodes.size() == 0) {//a task has at least one assignment
0608: // System.out.println(this + " is not a wbs parent " + wbsChildrenNodes);
0609: return false;
0610: }
0611:
0612: Iterator i = wbsChildrenNodes.iterator();
0613: Object current;
0614: while (i.hasNext()) {
0615: current = ((Node) i.next()).getImpl();
0616: if (current instanceof Task) {
0617: return true;
0618: }
0619: }
0620: return false;
0621: }
0622:
0623: public String getWbsParentName() {
0624: if (wbsParentTask == null)
0625: return "";
0626: return wbsParentTask.getName();
0627: }
0628:
0629: //arranges a task in predecessor/parent order in a colleciton
0630: public void arrangeTask(Collection addTo, boolean markerStatus,
0631: int depth) {
0632: if (this .markerStatus == markerStatus) { // if task has been added, don't treat it again
0633: return;
0634: }
0635:
0636: if (Environment.isImporting() && depth >= 1000) // in case circular link in imported project - TODO this is not a perfect solution
0637: throw new RuntimeException(
0638: CircularDependencyException.RUNTIME_EXCEPTION_TEXT);
0639: // Arrange my parent
0640: Task parent = getWbsParentTask();
0641: // System.out.println("arrange " + this + " id " + getId() + " parent is " + parent );
0642: if (parent != null) {
0643: parent.arrangeTask(addTo, markerStatus, depth + 1);
0644: }
0645:
0646: // Arrange my predecessors
0647: Iterator i = getPredecessorList().iterator();
0648: Task predecessor;
0649: Dependency dep;
0650:
0651: while (i.hasNext()) {
0652: dep = (Dependency) i.next();
0653: if (dep.isDisabled())
0654: continue;
0655: predecessor = (Task) dep.getPredecessor();
0656: predecessor.arrangeTask(addTo, markerStatus, depth + 1);
0657: predecessor.arrangeChildren(addTo, markerStatus, depth);
0658: }
0659:
0660: // Process current task
0661: PredecessorTaskList.TaskReference taskReference = new PredecessorTaskList.TaskReference(
0662: this );
0663: addTo.add(taskReference);
0664: this .markerStatus = markerStatus; // mark the task as added so it won't be treated again
0665:
0666: // Arrange my children
0667: if (isWbsParent()) {
0668: taskReference.setParentBegin();
0669: arrangeChildren(addTo, markerStatus, depth);
0670: taskReference = new PredecessorTaskList.TaskReference(this );
0671: taskReference.setParentEnd();
0672: addTo.add(taskReference);
0673: }
0674: }
0675:
0676: private void arrangeChildren(Collection addTo,
0677: boolean markerStatus, int depth) {
0678:
0679: //note that it is possible that this is called for non parents
0680:
0681: Collection children = getWbsChildrenNodes(); // I depend on my predecessors children
0682: if (children != null) {
0683: Iterator p = children.iterator();
0684: Object current;
0685: Task child;
0686: while (p.hasNext()) {
0687: current = ((Node) p.next()).getImpl();
0688: if (!(current instanceof Task))
0689: continue;
0690:
0691: child = (Task) current;
0692: child.arrangeTask(addTo, markerStatus, depth + 1);
0693: }
0694: }
0695: }
0696:
0697: /**
0698: * This rather complex function determines whether one task depends on another. Because of the rules for parent tasks,
0699: * the algorithm is rather complicated. Basically:
0700: * - A (parent) task depends on its childrens predecessors, as these are potentially equivalent
0701: * - A task depends on its parents predecessors - in the case of a link to its parent task, the links applies to this task too
0702: * - A task depends on its parent, of course
0703: * - A task depends on its predecessors (obviously)
0704: * - A task depends on its predecessors children
0705: *
0706: * @param other Task to compare to
0707: * @param set - A set used to prevent treating same task twice
0708: * @return true if linking to other would cause a circular link
0709: */
0710: private boolean dependsOn(Task other, HashSet set) {
0711: // To avoid infinite loops which can occur under certain circumstances, use a set to prevent looking up twice
0712: if (set.contains(this ))
0713: return false;
0714: set.add(this );
0715:
0716: // Here is the primary exit point. We have arrived back at the other node, so it is circular
0717: if (this == other)
0718: return true;
0719:
0720: Task predecessor;
0721: Dependency dep;
0722:
0723: Collection children;
0724: Iterator i;
0725:
0726: // I depend on my children's predecessors
0727: children = getWbsChildrenNodes();
0728: Task child;
0729: Object current;
0730: if (children != null) {
0731: i = children.iterator();
0732: while (i.hasNext()) {
0733: current = ((Node) i.next()).getImpl();
0734: if (!(current instanceof Task))
0735: continue;
0736: child = (Task) current;
0737: Iterator j = child.getPredecessorList().iterator();
0738: while (j.hasNext()) {
0739: dep = (Dependency) j.next();
0740: if (dep.isDisabled())
0741: continue;
0742: predecessor = (Task) dep.getPredecessor();
0743: if (predecessor.wbsDescendentOf(this )) // skip if already belongs to parent thru an ancestry relation
0744: continue;
0745: if (predecessor.dependsOn(other, set))
0746: return true;
0747: }
0748: }
0749: }
0750:
0751: // I depend on my parent's predecessors
0752: Task parent = getWbsParentTask();
0753: if (parent != null) {
0754:
0755: // I dependon my parent
0756: if (parent.dependsOn(other, set))
0757: return true;
0758:
0759: i = parent.getPredecessorList().iterator();
0760: while (i.hasNext()) {
0761: dep = (Dependency) i.next();
0762: if (dep.isDisabled())
0763: continue;
0764: predecessor = (Task) dep.getPredecessor();
0765: if (predecessor.dependsOn(other, set)) {
0766: return true;
0767: }
0768: }
0769: }
0770:
0771: // I depend on my predecessors and their children
0772: i = getPredecessorList().iterator();
0773: while (i.hasNext()) {
0774: dep = (Dependency) i.next();
0775: if (dep.isDisabled())
0776: continue;
0777: predecessor = (Task) dep.getPredecessor(); // I depend on my predecessors
0778: if (predecessor.dependsOn(other, set))
0779: return true;
0780:
0781: // see if my children depend on other
0782: children = predecessor.getWbsChildrenNodes(); // I depend on my predecessors children
0783: if (children != null) {
0784: i = children.iterator();
0785: while (i.hasNext()) {
0786: current = ((Node) i.next()).getImpl();
0787: if (!(current instanceof Task))
0788: continue;
0789:
0790: child = (Task) current;
0791: if (child.dependsOn(other, set))
0792: return true;
0793: }
0794: }
0795:
0796: }
0797:
0798: return false;
0799: }
0800:
0801: public boolean dependsOn(HasDependencies other) {
0802: HashSet set = new HashSet();
0803: return dependsOn((Task) other, set);
0804: }
0805:
0806: /**
0807: * @return
0808: */
0809: public Date getCreated() {
0810: return hasKey.getCreated();
0811: }
0812:
0813: /**
0814: * @return
0815: */
0816: public long getId() {
0817: return hasKey.getId();
0818: }
0819:
0820: /**
0821: * @param id
0822: */
0823: public void setId(long id) {
0824: hasKey.setId(id);
0825: }
0826:
0827: /**
0828: * @param created
0829: */
0830: public void setCreated(Date created) {
0831: hasKey.setCreated(created);
0832: }
0833:
0834: public String getPredecessors() {
0835: return AssociationListFormat.getInstance(
0836: DependencyFormat
0837: .getInstance(AssociationFormatParameters
0838: .getInstance(this , true, Configuration
0839: .getFieldFromId("Field.id"),
0840: false, true))).format(
0841: getPredecessorList());
0842: }
0843:
0844: public String getSuccessors() {
0845: return AssociationListFormat.getInstance(
0846: DependencyFormat
0847: .getInstance(AssociationFormatParameters
0848: .getInstance(this , false, Configuration
0849: .getFieldFromId("Field.id"),
0850: false, true))).format(
0851: getSuccessorList());
0852: }
0853:
0854: public String getUniqueIdPredecessors() {
0855: return AssociationListFormat
0856: .getInstance(
0857: DependencyFormat
0858: .getInstance(AssociationFormatParameters
0859: .getInstance(
0860: this ,
0861: true,
0862: Configuration
0863: .getFieldFromId("Field.uniqueId"),
0864: false, true))).format(
0865: getPredecessorList());
0866: }
0867:
0868: public String getUniqueIdSuccessors() {
0869: return AssociationListFormat
0870: .getInstance(
0871: DependencyFormat
0872: .getInstance(AssociationFormatParameters
0873: .getInstance(
0874: this ,
0875: false,
0876: Configuration
0877: .getFieldFromId("Field.uniqueId"),
0878: false, true))).format(
0879: getSuccessorList());
0880: }
0881:
0882: public String getWbsPredecessors() {
0883: return AssociationListFormat.getInstance(
0884: DependencyFormat
0885: .getInstance(AssociationFormatParameters
0886: .getInstance(this , true, Configuration
0887: .getFieldFromId("Field.wbs"),
0888: true, true))).format(
0889: getPredecessorList());
0890: }
0891:
0892: public String getWbsSuccessors() {
0893: return AssociationListFormat.getInstance(
0894: DependencyFormat
0895: .getInstance(AssociationFormatParameters
0896: .getInstance(this , false, Configuration
0897: .getFieldFromId("Field.wbs"),
0898: true, true))).format(
0899: getSuccessorList());
0900: }
0901:
0902: public void setPredecessors(String predecessors)
0903: throws FieldParseException {
0904: getPredecessorList().setAssociations(
0905: predecessors,
0906: DependencyFormat
0907: .getInstance(AssociationFormatParameters
0908: .getInstance(this , true, Configuration
0909: .getFieldFromId("Field.id"),
0910: false, true)));
0911: }
0912:
0913: public void setSuccessors(String successors)
0914: throws FieldParseException {
0915: getSuccessorList().setAssociations(
0916: successors,
0917: DependencyFormat
0918: .getInstance(AssociationFormatParameters
0919: .getInstance(this , false, Configuration
0920: .getFieldFromId("Field.id"),
0921: false, true)));
0922: }
0923:
0924: public void setUniqueIdPredecessors(String predecessors)
0925: throws FieldParseException {
0926: getPredecessorList()
0927: .setAssociations(
0928: predecessors,
0929: DependencyFormat
0930: .getInstance(AssociationFormatParameters
0931: .getInstance(
0932: this ,
0933: true,
0934: Configuration
0935: .getFieldFromId("Field.uniqueId"),
0936: false, true)));
0937: }
0938:
0939: public void setUniqueIdSuccessors(String successors)
0940: throws FieldParseException {
0941: getSuccessorList()
0942: .setAssociations(
0943: successors,
0944: DependencyFormat
0945: .getInstance(AssociationFormatParameters
0946: .getInstance(
0947: this ,
0948: false,
0949: Configuration
0950: .getFieldFromId("Field.uniqueId"),
0951: false, true)));
0952: }
0953:
0954: /**
0955: * @return Returns the wbs.
0956: */
0957: public String getWbs() {
0958: return wbs;
0959: }
0960:
0961: /**
0962: * @param wbs The wbs to set.
0963: */
0964: public void setWbs(String wbs) {
0965: this .wbs = wbs;
0966: }
0967:
0968: /**
0969: * @return
0970: */
0971: public long getUniqueId() {
0972: return hasKey.getUniqueId();
0973: }
0974:
0975: /**
0976: * @param id
0977: */
0978: public void setUniqueId(long id) {
0979: hasKey.setUniqueId(id);
0980: }
0981:
0982: /**
0983: * @return
0984: */
0985: public String getName() {
0986: return hasKey.getName();
0987: }
0988:
0989: /**
0990: * @param name
0991: */
0992: public void setName(String name) {
0993: hasKey.setName(name);
0994: }
0995:
0996: public String toString() {
0997: if (getName() == null)
0998: return "<null name>";
0999: return getName();
1000: }
1001:
1002: public String getName(FieldContext context) {
1003: return hasKey.getName(context);
1004: }
1005:
1006: public boolean isNormal() {
1007: return false;
1008: }
1009:
1010: public boolean isCritical() {
1011: return false;
1012: }
1013:
1014: /** if task is currently critical or will become critical after CP */
1015: public boolean isOrWasCritical() {
1016: if (isCritical())
1017: return true;
1018: if (currentSchedule.isForward())
1019: return getEnd() >= getLateFinish();
1020: else
1021: // reverse schedule
1022: return getStart() <= getEarlyStart();
1023: }
1024:
1025: public boolean isSummary() {
1026: return isWbsParent(); //TODO need to somehow hook into view and see if parent in view's node model. yuck!
1027: }
1028:
1029: public boolean isAssignment() {
1030: return false;
1031: }
1032:
1033: /**
1034: * @return Returns the physicalPercentComplete.
1035: */
1036: public double getPhysicalPercentComplete() {
1037: return physicalPercentComplete;
1038: }
1039:
1040: /**
1041: * @param physicalPercentComplete The physicalPercentComplete to set.
1042: */
1043: public void setPhysicalPercentComplete(
1044: double physicalPercentComplete) {
1045: this .physicalPercentComplete = physicalPercentComplete;
1046: }
1047:
1048: public void markTaskAsNeedingRecalculation() {
1049: int nextStateCount = project.getCalculationStateCount() + 1;
1050: setCalculationStateCount(nextStateCount);
1051: }
1052:
1053: /**
1054: * Flags all tasks which depend on this one for scheduling as dirty
1055: * @param doSelf TODO
1056: *
1057: */
1058: void markAllDependentTasksAsNeedingRecalculation(boolean doSelf) {
1059: if (doSelf)
1060: markTaskAsNeedingRecalculation();
1061:
1062: Iterator succ = getSuccessorList().iterator();
1063: if (!succ.hasNext()) {
1064: getProject().getSchedulingAlgorithm().markBoundsAsDirty();
1065: //TODO what about reverse schedulded?
1066: } else {
1067: Task successor;
1068: // mark successors as dirty
1069: while (succ.hasNext()) {
1070: successor = (Task) ((Dependency) succ.next())
1071: .getSuccessor();
1072: successor.markTaskAsNeedingRecalculation();
1073: }
1074: }
1075: // mark parent as dirty
1076: Task parent = getWbsParentTask();
1077: while (parent != null) {
1078: parent.markTaskAsNeedingRecalculation();
1079: parent = parent.getWbsParentTask();
1080: }
1081:
1082: // mark children as dirty
1083: Collection children = getWbsChildrenNodes();
1084: if (children != null) {
1085: Iterator i = children.iterator();
1086: Object child;
1087: while (i.hasNext()) {
1088: child = ((Node) i.next()).getImpl();
1089: if (!(child instanceof Task))
1090: continue;
1091: ((Task) child).markTaskAsNeedingRecalculation();
1092: }
1093: }
1094: }
1095:
1096: void cleanUp(Object eventSource, boolean deep, boolean undo,
1097: boolean cleanDependencies) {
1098: //if (!cleanDependencies) return; //TODO was for undo of paste of linked tasks, but doesn't work
1099: markAllDependentTasksAsNeedingRecalculation(false);
1100:
1101: // remove sentinel dependencies if any
1102: project.removeStartSentinelDependency(this );
1103: project.removeEndSentinelDependency(this );
1104:
1105: // remove all links to or from
1106: LinkedList toRemove = new LinkedList(); //fix
1107: DependencyService.getInstance().remove(getPredecessorList(),
1108: toRemove);
1109: for (Iterator j = toRemove.iterator(); j.hasNext();) {
1110: DependencyService.getInstance().remove(
1111: (Dependency) j.next(), eventSource, undo); //fix
1112: }
1113: toRemove.clear();
1114: DependencyService.getInstance().remove(getSuccessorList(),
1115: toRemove);
1116: for (Iterator j = toRemove.iterator(); j.hasNext();) {
1117: DependencyService.getInstance().remove(
1118: (Dependency) j.next(), eventSource, undo); //fix
1119: }
1120:
1121: }
1122:
1123: public void recalculate(Object eventSource) {
1124: ((Project) getDocument()).updateScheduling(eventSource, this ,
1125: ObjectEvent.UPDATE, getEndField());
1126: //getDocument().getObjectEventManager().fireUpdateEvent(eventSource,this,getEndField());
1127: }
1128:
1129: public void recalculateLater(final Object eventSource) {
1130: markTaskAsNeedingRecalculation(); // task needs to be recalculated
1131:
1132: SwingUtilities.invokeLater(new Runnable() {
1133: public void run() {
1134: recalculate(eventSource);
1135: }
1136: });
1137: }
1138:
1139: /**
1140: * @return
1141: */
1142: public long getActualStart() {
1143: return actualStart;
1144: // if (currentSchedule.getPercentComplete() == 0.0D && getPercentComplete() == 0)
1145: // return 0;
1146: // return getStart();
1147: }
1148:
1149: public abstract void setActualStart(long actualStart);
1150:
1151: public long getActualFinish() {
1152: if (getPercentComplete() == 1.0)
1153: return getEnd();
1154: return 0;
1155: }
1156:
1157: /**
1158: * @param actualFinish
1159: */
1160: public void setActualFinish(long actualFinish) {
1161: long old = getActualFinish();
1162: if (actualFinish == old)
1163: return;
1164: setEnd(actualFinish);
1165: setPercentComplete(1.0);
1166: }
1167:
1168: /**
1169: * Percent complete is calculated based on assignments
1170: */
1171: public double getPercentComplete() {
1172: boolean parent = isWbsParent();
1173: DivisionSummaryVisitor divisionClosure = ScheduleUtil
1174: .percentCompleteClosureInstance(parent);
1175: Project proj = (Project) (getMasterDocument() == null ? getProject()
1176: : getMasterDocument());
1177: NodeModel nodeModel = proj.getTaskOutline();
1178: if (isWbsParent()) {
1179: try {
1180: LeafWalker.recursivelyTreatBranch(nodeModel, this ,
1181: divisionClosure);
1182: } catch (NullPointerException n) {
1183: ErrorLogger.logOnce("getPercentComplete",
1184: "getPercentComplete() Task: " + this
1185: + " Project " + project, n);
1186: return 0; // better this than crashing
1187: }
1188: } else {
1189: CollectionUtils.forAllDo(((NormalTask) this )
1190: .getAssignments(), divisionClosure);
1191: }
1192: return divisionClosure.getValue();
1193: }
1194:
1195: public boolean inProgress() {
1196: double percentComplete = getPercentComplete();
1197: return (percentComplete > 0.0D && percentComplete < 1.0D);
1198: }
1199:
1200: public boolean isComplete() {
1201: return getPercentComplete() >= 1.0D;
1202: }
1203:
1204: public boolean isUnstarted() {
1205: return getPercentComplete() == 0.0D;
1206: }
1207:
1208: public long getDurationMillis() {
1209: return Duration.millis(getDuration());
1210: }
1211:
1212: /**
1213: * @return
1214: */
1215: //&&&&&
1216: // public long getActualDuration() {
1217: // long stop = getStop();
1218: // if (stop == 0)
1219: // return 0;
1220: // return getEffectiveWorkCalendar().compare(stop,getStart(),false);
1221: // }
1222: /**
1223: * Actual duration is % complete * duration for all tasks including parents
1224: */
1225: public long getActualDuration() {
1226: long duration = getDuration();
1227: long result = Math.round(getPercentComplete()
1228: * Duration.millis(duration));
1229: return Duration.useTimeUnitOfInNone(result, duration);
1230: }
1231:
1232: /**
1233: * @param actualDuration
1234: */
1235: public void setActualDuration(long actualDuration) {
1236: actualDuration = DateTime.closestDate(Duration
1237: .millis(actualDuration));
1238:
1239: if (actualDuration == Duration.millis(getActualDuration()))
1240: return;
1241: long stop = getEffectiveWorkCalendar().add(getStart(),
1242: actualDuration, true);
1243: setStop(stop);
1244: }
1245:
1246: /**
1247: * @return
1248: */
1249: public long getRemainingDuration() {
1250: long actualDuration = getActualDuration();
1251: long result = getDurationMillis()
1252: - Duration.millis(actualDuration);
1253: return Duration.useTimeUnitOfInNone(result, actualDuration);
1254: }
1255:
1256: /**
1257: * @param remainingDuration
1258: */
1259: public void setRemainingDuration(long remainingDuration) {
1260: remainingDuration = DateTime.closestDate(Duration
1261: .millis(remainingDuration));
1262: setActualDuration(getDurationMillis() - remainingDuration);
1263: }
1264:
1265: public Object clone() {
1266: try {
1267: Task task = (Task) super .clone();
1268: _cloneTo(task);
1269: // Handle wbs outside
1270: return task;
1271: } catch (CloneNotSupportedException e) {
1272: throw new InternalError();
1273: }
1274: }
1275:
1276: public void cleanClone() {
1277: owningProject = null;
1278: project = null;
1279: }
1280:
1281: private void _cloneTo(Task task) {
1282: task.hasKey = new HasKeyImpl(isLocal(), task);
1283: task.setName(new String(getName()));
1284: task.setRawDuration(getRawDuration());
1285:
1286: task.earlySchedule = (TaskSchedule) earlySchedule
1287: .cloneWithTask(task);
1288: task.lateSchedule = (TaskSchedule) lateSchedule
1289: .cloneWithTask(task);
1290: task.customFields = (CustomFieldsImpl) customFields.clone();
1291: task.snapshots = (Snapshottable) ((SnapshottableImpl) snapshots)
1292: .cloneWithTask(task);
1293: task.currentSchedule = ((TaskSnapshot) task
1294: .getCurrentSnapshot()).getCurrentSchedule();
1295: task.notes = new String(notes);
1296: task.wbs = new String(wbs);
1297: task.wbsChildrenNodes = null;
1298: task.wbsParentTask = null;
1299:
1300: task.dependencies = new HasDependenciesImpl(task);
1301:
1302: task.currentSchedule.copyDatesAfterClone(currentSchedule);
1303: }
1304:
1305: public void cloneTo(Task task) {
1306: task.project = project;
1307: task.owningProject = owningProject;
1308:
1309: task.debugDependencyOrder = debugDependencyOrder;
1310: task.markTaskAsMilestone = markTaskAsMilestone;
1311: task.external = external;
1312: task.projectId = projectId;
1313: task.physicalPercentComplete = physicalPercentComplete;
1314:
1315: task.windowEarlyStart = windowEarlyStart;
1316: task.windowEarlyFinish = windowEarlyFinish;
1317: task.windowLateStart = windowLateStart;
1318: task.windowLateFinish = windowLateFinish;
1319:
1320: task.actualStart = actualStart;
1321:
1322: task.levelingDelay = levelingDelay;
1323: task.calculationStateCount = calculationStateCount;
1324: task.markerStatus = markerStatus;
1325: task.earnedValueMethod = earnedValueMethod;
1326: task.startFieldInstance = startFieldInstance;
1327:
1328: task.constraintType = constraintType;
1329: task.deadline = deadline;
1330: task.expenseType = expenseType;
1331: task.inSubproject = inSubproject;
1332: task.lastSavedStart = lastSavedStart;
1333: task.lastSavedFinish = lastSavedFinish;
1334: task.dirty = dirty;
1335:
1336: task.delegatedTo = delegatedTo;
1337:
1338: _cloneTo(task);
1339: }
1340:
1341: /**
1342: * @return Returns the splitDuration.
1343: */
1344: public long getSplitDuration() {
1345: long r = getResume();
1346: if (r == 0)
1347: return 0;
1348: return getEffectiveWorkCalendar().compare(r, getStop(), false);
1349: }
1350:
1351: /******************************************************************************
1352: * Getters and setters
1353: ****************************************************************************/
1354:
1355: /**
1356: * @return Returns the constraintType.
1357: */
1358: public int getConstraintType() {
1359: return constraintType;
1360: }
1361:
1362: /**
1363: * @param constraintType The constraintType to set.
1364: */
1365: public void setConstraintType(int constraintType)
1366: throws FieldParseException {
1367: int newConstraintType = makeValidConstraintType(constraintType); // limit to valid options
1368: if (newConstraintType != constraintType)
1369: throw new FieldParseException(Messages
1370: .getString("Message.parentConstraintType"));
1371: setRawConstraintType(constraintType);
1372: long d = getConstraintDate(); // save off old date, it will be reused
1373: if (!isDatelessConstraintType() && d == 0) // if the constraint requires a date and there is none, set a date to today
1374: setConstraintDate(getEffectiveWorkCalendar()
1375: .adjustInsideCalendar(DateTime.midnightToday(),
1376: false));
1377:
1378: }
1379:
1380: public void setRawConstraintType(int constraintType) {
1381: long d = getConstraintDate(); // save off old date, it will be reused
1382: clearDateConstraints(); // get rid of all constraints
1383: setScheduleConstraint(constraintType, d); // set new constraint with old date
1384:
1385: }
1386:
1387: /**
1388: * @return Returns the windowEarlyStart.
1389: */
1390: public long getWindowEarlyStart() {
1391: return windowEarlyStart;
1392: }
1393:
1394: /**
1395: * @param windowEarlyStart The windowEarlyStart to set.
1396: */
1397: public void setWindowEarlyStart(long windowEarlyStart) {
1398: this .windowEarlyStart = windowEarlyStart;
1399: }
1400:
1401: /**
1402: * @return Returns the windowEarlyFinish.
1403: */
1404: public long getWindowEarlyFinish() {
1405: return windowEarlyFinish;
1406: }
1407:
1408: /**
1409: * @param windowEarlyFinish The windowEarlyFinish to set.
1410: */
1411: public void setWindowEarlyFinish(long windowEarlyFinish) {
1412: this .windowEarlyFinish = windowEarlyFinish;
1413: }
1414:
1415: /**
1416: * @return Returns the windowLateFinish.
1417: */
1418: public long getWindowLateFinish() {
1419: return windowLateFinish;
1420: }
1421:
1422: /**
1423: * @param windowLateFinish The windowLateFinish to set.
1424: */
1425: public void setWindowLateFinish(long windowLateFinish) {
1426: this .windowLateFinish = windowLateFinish;
1427: }
1428:
1429: /**
1430: * @return Returns the windowLateStart.
1431: */
1432: public long getWindowLateStart() {
1433: return windowLateStart;
1434: }
1435:
1436: /**
1437: * @param windowLateStart The windowLateStart to set.
1438: */
1439: public void setWindowLateStart(long windowLateStart) {
1440: this .windowLateStart = windowLateStart;
1441: }
1442:
1443: /**
1444: * @param mustStart The date the task must start on.
1445: */
1446: public void setMustStartOn(long mustStart) {
1447: setWindowEarlyStart(mustStart);
1448: setWindowLateStart(mustStart);
1449: }
1450:
1451: /**
1452: * @param mustStart The date the task must finish on.
1453: */
1454: public void setMustFinishOn(long mustFinish) {
1455: setWindowEarlyFinish(mustFinish);
1456: setWindowLateFinish(mustFinish);
1457: }
1458:
1459: /**
1460: * Set constraint FNET
1461: * @param date
1462: */
1463: public void setFinishNoEarlierThan(long date) {
1464: setWindowEarlyFinish(date);
1465: }
1466:
1467: /**
1468: * Set constraint FNLT
1469: * @param date
1470: */
1471: public void setFinishNoLaterThan(long date) {
1472: setWindowLateFinish(date);
1473: setWindowEarlyFinish(0);
1474: }
1475:
1476: /**
1477: * Set constraint SNET
1478: * @param date
1479: */
1480: public void setStartNoEarlierThan(long date) {
1481: setWindowEarlyStart(date);
1482: setWindowLateStart(0);
1483: }
1484:
1485: /**
1486: * Set constraint SNLT
1487: * @param date
1488: */
1489: public void setStartNoLaterThan(long date) {
1490: setWindowLateStart(date);
1491: setWindowEarlyStart(0);
1492: }
1493:
1494: /**
1495: * @return Returns the earlyFinish.
1496: */
1497: public long getEarlyFinish() {
1498: return earlySchedule.getFinish();
1499: }
1500:
1501: /**
1502: * @return Returns the earlyStart.
1503: */
1504: public long getEarlyStart() {
1505: return earlySchedule.getStart();
1506: }
1507:
1508: /**
1509: * @return Returns the lateFinish.
1510: */
1511: public long getLateFinish() {
1512: return lateSchedule.getFinish();
1513: }
1514:
1515: /**
1516: * @return Returns the lateStart.
1517: */
1518: public long getLateStart() {
1519: return lateSchedule.getStart();
1520: }
1521:
1522: /**
1523: * Is this task reverse scheduled: Is it ALAP in forward scheduling or ASAP in reverse?
1524: * @return
1525: */
1526: public final boolean isReverseScheduled() {
1527: if (project.isForward())
1528: return constraintType == ConstraintType.ALAP;
1529: else
1530: return constraintType == ConstraintType.ASAP;
1531: }
1532:
1533: protected final boolean isDatelessConstraintType() {
1534: return constraintType == ConstraintType.ALAP
1535: || constraintType == ConstraintType.ASAP;
1536:
1537: }
1538:
1539: /*********************************************************************************
1540: * Constraints
1541: ***********************************************************************************/
1542: private void clearDateConstraints() {
1543: setWindowEarlyStart(0);
1544: setWindowLateStart(0);
1545: setWindowEarlyFinish(0);
1546: setWindowLateFinish(0);
1547: }
1548:
1549: // public Object[] fieldOptionsScheduleConstraint() {
1550: // if (isWbsParent()) {
1551: // Configuration.getFieldFromId("Field.scheduleConstraint");
1552: //
1553: //
1554: // } else {
1555: // return null; // use default
1556: // }
1557: //
1558: // }
1559:
1560: public void setScheduleConstraint(int constraintType, long date) {
1561: this .constraintType = constraintType;
1562: if (constraintType == ConstraintType.FNET) {
1563: setFinishNoEarlierThan(date);
1564: } else if (constraintType == ConstraintType.FNLT) {
1565: setFinishNoLaterThan(date);
1566: } else if (constraintType == ConstraintType.SNET) {
1567: setStartNoEarlierThan(date);
1568: } else if (constraintType == ConstraintType.SNLT) {
1569: setStartNoLaterThan(date);
1570: } else if (constraintType == ConstraintType.MSO) {
1571: setMustStartOn(date);
1572: } else if (constraintType == ConstraintType.MFO) {
1573: setMustFinishOn(date);
1574: } else {
1575: clearDateConstraints();
1576: }
1577:
1578: }
1579:
1580: public void setScheduleConstraintAndUpdate(int constraintType,
1581: long date) {
1582: setScheduleConstraint(constraintType, date);
1583: getDocument().getObjectEventManager().fireUpdateEvent(this ,
1584: this ,
1585: Configuration.getFieldFromId("Field.constraintType"));
1586: }
1587:
1588: public void setConstraintDate(long date) {
1589: if (date == 0) {
1590: clearDateConstraints();
1591: constraintType = getProject().getDefaultConstraintType();
1592: } else {
1593: date = getEffectiveWorkCalendar().adjustInsideCalendar(
1594: date, false); // make date valid
1595:
1596: }
1597: setScheduleConstraint(constraintType, date);
1598:
1599: }
1600:
1601: public long getConstraintDate() {
1602: if (constraintType == ConstraintType.FNET) {
1603: return getWindowEarlyFinish();
1604: } else if (constraintType == ConstraintType.FNLT) {
1605: return getWindowLateFinish();
1606: } else if (constraintType == ConstraintType.SNET) {
1607: return getWindowEarlyStart();
1608: } else if (constraintType == ConstraintType.SNLT) {
1609: return getWindowLateStart();
1610: } else if (constraintType == ConstraintType.MSO) {
1611: return getWindowEarlyStart();
1612: } else if (constraintType == ConstraintType.MFO) {
1613: return getWindowEarlyFinish();
1614: } else {
1615: return 0;
1616: }
1617: }
1618:
1619: public boolean isReadOnlyConstraintDate(FieldContext fieldContext) {
1620: return getConstraintType() == ConstraintType.ALAP
1621: || getConstraintType() == ConstraintType.ASAP;
1622: }
1623:
1624: /**************************************************************************
1625: * Critical path methods for slack
1626: ***************************************************************************/
1627: public final long getTotalSlack() {
1628: return getEffectiveWorkCalendar().compare(getLateFinish(),
1629: getEarlyFinish(), false);
1630: }
1631:
1632: /** The amount of excess time an activity has between its Early Start and Late Start dates. */
1633: public final long getStartSlack() {
1634: return getEffectiveWorkCalendar().compare(getLateStart(),
1635: getEarlyStart(), false);
1636: }
1637:
1638: /** The amount of excess time an activity has between its Early Finish and Late Finish dates. */
1639: public final long getFinishSlack() {
1640: return getEffectiveWorkCalendar().compare(getLateFinish(),
1641: getEarlyFinish(), false); // note that this is same as total float
1642: }
1643:
1644: /**
1645: * Used to calculate the free slack on a given dependency
1646: *
1647: * @param dependency
1648: * @return
1649: */
1650: private static long calcFreeSlack(Dependency dependency) {
1651: ScheduleWindow predecessor = (ScheduleWindow) dependency
1652: .getPredecessor();
1653: ScheduleWindow successor = (ScheduleWindow) dependency
1654: .getSuccessor();
1655: long t = 0;
1656: WorkCalendar cal = dependency.getEffectiveWorkCalendar();
1657: if (dependency.getDependencyType() == DependencyType.FS) {
1658: t = cal.compare(cal.add(successor.getEarlyStart(),
1659: -dependency.getLeadValue(), true), predecessor
1660: .getEarlyFinish(), false);
1661: } else if (dependency.getDependencyType() == DependencyType.FF) {
1662: t = cal.compare(cal.add(successor.getEarlyFinish(),
1663: -dependency.getLeadValue(), true), predecessor
1664: .getEarlyFinish(), false);
1665: } else if (dependency.getDependencyType() == DependencyType.SS) {
1666: t = cal.compare(cal.add(successor.getEarlyStart(),
1667: -dependency.getLeadValue(), true), predecessor
1668: .getEarlyStart(), false);
1669: } else if (dependency.getDependencyType() == DependencyType.SF) {
1670: t = cal.compare(cal.add(successor.getEarlyFinish(),
1671: -dependency.getLeadValue(), true), predecessor
1672: .getEarlyStart(), false);
1673: }
1674: return t;
1675: }
1676:
1677: /**
1678: * Used in calculating free slack. Free Float = For FS: Early Start (next
1679: * activity) � Lag (if any) � Early Finish*
1680: *
1681: * @param dependencyList
1682: * @param duration
1683: * @return
1684: */
1685: public long getFreeSlack() {
1686: long least = getTotalSlack(); // free slack is at most the total slack
1687: Dependency dependency;
1688: for (Iterator i = getSuccessorList().iterator(); i.hasNext();) {
1689: dependency = (Dependency) i.next();
1690: least = Math.min(least, calcFreeSlack(dependency));
1691: }
1692: return least;
1693: }
1694:
1695: /**********************************************************************************
1696: * Access to Dependencies
1697: **********************************************************************************/
1698:
1699: public AssociationList getPredecessorList() {
1700: return dependencies.getPredecessorList();
1701: }
1702:
1703: public AssociationList getSuccessorList() {
1704: return dependencies.getSuccessorList();
1705: }
1706:
1707: public AssociationList getDependencyList(boolean pred) {
1708: return dependencies.getDependencyList(pred);
1709: }
1710:
1711: /************************************************************************************
1712: * Critical Path stuff
1713: **************************************************************************************/
1714:
1715: /* (non-Javadoc)
1716: * @see com.projity.pm.criticalpath.ScheduleWindow#calcOffsetFrom(long, boolean, boolean)
1717: */
1718: public long calcOffsetFrom(long startDate, long dependencyDate,
1719: boolean ahead, boolean remainingOnly, boolean useSooner) {
1720: throw new RuntimeException(
1721: "calcOffsetFrom should not be called in ScheduleWindow");
1722: }
1723:
1724: public long getElapsedDuration() {
1725: return Math.round(getEffectiveWorkCalendar().compare(getEnd(),
1726: getStart(), true)
1727: * CalendarOption.getInstance()
1728: .getFractionOfDayThatIsWorking());
1729: }
1730:
1731: public long getDeadline() {
1732: return deadline;
1733: }
1734:
1735: public void setDeadline(long deadline) {
1736: if (deadline != 0L)
1737: deadline = CalendarOption.getInstance().makeValidEnd(
1738: deadline, false);
1739: this .deadline = deadline;
1740: }
1741:
1742: public boolean isMarkTaskAsMilestone() {
1743: return markTaskAsMilestone;
1744: }
1745:
1746: public void setMarkTaskAsMilestone(boolean markTaskAsMilestone) {
1747: this .markTaskAsMilestone = markTaskAsMilestone;
1748: }
1749:
1750: public int getEarnedValueMethod() {
1751: return earnedValueMethod;
1752: }
1753:
1754: public void setEarnedValueMethod(int earnedValueMethod) {
1755: this .earnedValueMethod = earnedValueMethod;
1756: }
1757:
1758: /** Update a task from the Update Project dialog. There are several options:
1759: *
1760: * @param date Date to either update completion to, or to move remaining work to
1761: * @param updateWorkAsCompleteThrough If true, then update % complete, if false, then move remaining
1762: * to the date
1763: * @param setFractionalPercentComplete If true, then allow setting of % complete for uncompleted tasks,
1764: * otherwise, if a task is not completed, it's current completion will not be modified
1765: * @return True if task was updated, false if unchanged
1766: */
1767: public boolean updateProjectTask(long date,
1768: boolean updateWorkAsCompleteThrough,
1769: boolean setFractionalPercentComplete) {
1770: long start = getStart();
1771: long end = getEnd();
1772: long completedDate = getStop();
1773: boolean updated = false;
1774: if (updateWorkAsCompleteThrough) {
1775: if (setFractionalPercentComplete) {
1776: if (completedDate != end) {// if task is not finished, adjust its completion. This may actually reduce % complete
1777: setStop(date);
1778: updated = true;
1779: }
1780: } else if (date >= end) { // if date is equal or later to end date of task, set its percent complete to 100%, otherwise do nothing
1781: setPercentComplete(1.0);
1782: updated = true;
1783: }
1784: } else {
1785: if (date > start) { // move remaining after status date
1786: moveRemainingToDate(date);
1787: updated = true;
1788: }
1789: }
1790: if (updated) {
1791: recalculate(this );
1792: setDirty(true);
1793: }
1794: return updated;
1795: }
1796:
1797: /**
1798: * @return Returns the calculationStateCount.
1799: */
1800: public final int getCalculationStateCount() {
1801: return calculationStateCount;
1802: }
1803:
1804: /**
1805: * @param calculationStateCount The calculationStateCount to set.
1806: */
1807: public final void setCalculationStateCount(int calculationStateCount) {
1808: this .calculationStateCount = calculationStateCount;
1809: }
1810:
1811: public void updateEndSentinel() {
1812: if (getSuccessorList().isEmpty()) { // if pred has no successors, tell end sentinel about it
1813: project.addEndSentinelDependency(this );
1814: } else { // make sure not in sentinel's list
1815: project.removeEndSentinelDependency(this );
1816: }
1817: }
1818:
1819: public void updateStartSentinel() {
1820: if (getPredecessorList().isEmpty()) { // if pred has no successors, tell end sentinel about it
1821: project.addStartSentinelDependency(this );
1822: } else { // make sure not in sentinel's list
1823: project.removeStartSentinelDependency(this );
1824: }
1825: }
1826:
1827: public final TaskSchedule getEarlySchedule() {
1828: return earlySchedule;
1829: }
1830:
1831: public final TaskSchedule getLateSchedule() {
1832: return lateSchedule;
1833: }
1834:
1835: public final TaskSchedule getSchedule(int scheduleType) {
1836: if (scheduleType == TaskSchedule.CURRENT)
1837: return currentSchedule;
1838: else if (scheduleType == TaskSchedule.EARLY)
1839: return earlySchedule;
1840: else
1841: return lateSchedule;
1842: }
1843:
1844: /**
1845: * Used for debugging primarily - says if a task was just modified by last recalculation - either has same count or is one less
1846: * @return
1847: */
1848: public boolean isJustModified() {
1849: if (project.getSchedulingAlgorithm() == null)
1850: return false;
1851: return calculationStateCount
1852: + PredecessorTaskList.CALCULATION_STATUS_STEP >= project
1853: .getSchedulingAlgorithm().getCalculationStateCount();
1854: }
1855:
1856: public abstract boolean hasDuration();
1857:
1858: /**
1859: * @param markerStatus The markerStatus to set.
1860: */
1861: public final void setMarkerStatus(boolean markerStatus) {
1862: this .markerStatus = markerStatus;
1863: }
1864:
1865: /**
1866: * Set the task to be forward or rerverse scheduled
1867: * @param forward
1868: */
1869: public void setForward(boolean forward) {
1870: getCurrentSchedule().setForward(forward);
1871: restrictToValidConstraintType();
1872:
1873: }
1874:
1875: /**
1876: * Parent tasks have only 3 possible consraint types ASAP/ALAP (depending on forward or reverse scheduling), SNET, and FNLT
1877: */
1878: void restrictToValidConstraintType() {
1879: setRawConstraintType(makeValidConstraintType(getConstraintType()));
1880: }
1881:
1882: protected int makeValidConstraintType(int type) {
1883: if (isWbsParent()) { // parents have a limited choice of constraint types
1884: if (type == ConstraintType.FNLT
1885: || type == ConstraintType.SNET)
1886: return type;
1887: else
1888: return project.getDefaultConstraintType();
1889: }
1890: return type;
1891:
1892: }
1893:
1894: // public boolean isNew() {
1895: // return hasKey.isNew();
1896: // }
1897:
1898: public static Predicate instanceof Predicate() {
1899: return new Predicate() {
1900: public boolean evaluate(Object arg0) {
1901: return arg0 instanceof Task;
1902: }
1903: };
1904: }
1905:
1906: public void invalidateSchedules() {
1907: earlySchedule.invalidate();
1908: lateSchedule.invalidate();
1909: }
1910:
1911: public Document getMasterDocument() {
1912: if (getProject().getSchedulingAlgorithm() == null)
1913: return null;
1914: return getProject().getSchedulingAlgorithm()
1915: .getMasterDocument();
1916: }
1917:
1918: public String getTaskAndProjectName() {
1919: Project p = getOwningProject();
1920: if (p == null)
1921: p = getProject();
1922: return getName() + " (" + p.getName() + ")";
1923: }
1924:
1925: public String getSubprojectFile() {
1926: return null;
1927: }
1928:
1929: public boolean fieldHideSubprojectFile(FieldContext fieldContext) {
1930: return true;
1931: }
1932:
1933: public void setSubprojectFile(String sub) {
1934: }
1935:
1936: public boolean isSubprojectReadOnly() {
1937: return false;
1938: }
1939:
1940: public boolean fieldHideSubprojectReadOnly(FieldContext fieldContext) {
1941: return true;
1942: }
1943:
1944: public long getParentId(int outlineNumber) {
1945: NodeModel model = project.getTaskOutline(outlineNumber);
1946: if (model == null)
1947: return 0;
1948: Node node = model.getParent(model.search(this ));
1949: Object impl = node.getImpl();
1950: if (impl != null && impl instanceof HasKey)
1951: return ((HasKey) impl).getId();
1952: return 0;
1953: }
1954:
1955: public int getOutlineLevel(int outlineNumber) {
1956: NodeModel model = project.getTaskOutline(outlineNumber);
1957: if (model == null)
1958: return 0;
1959: Node node = model.getParent(model.search(this ));
1960: return model.getHierarchy().getLevel(node);
1961: }
1962:
1963: public int getOutlineLevel() {
1964: return getOutlineLevel(OutlineCollection.DEFAULT_OUTLINE);
1965: }
1966:
1967: public final int getDebugDependencyOrder() {
1968: return debugDependencyOrder;
1969: }
1970:
1971: public final void setDebugDependencyOrder(int debugDependencyOrder) {
1972: this .debugDependencyOrder = debugDependencyOrder;
1973: }
1974:
1975: public final long getLevelingDelay() {
1976: return levelingDelay;
1977: }
1978:
1979: public final void setLevelingDelay(long levelingDelay) {
1980: this .levelingDelay = levelingDelay;
1981: }
1982:
1983: public CustomFields getCustomFields() {
1984: return customFields;
1985: }
1986:
1987: public void updateCachedDuration() {
1988: setRawDuration(getDuration());
1989: }
1990:
1991: public boolean isDirty() {
1992: return dirty || getStart() != getLastSavedStart()
1993: || getEnd() != getLastSavedFinish();
1994: }
1995:
1996: public void setDirty(boolean dirty) {
1997: this .dirty = dirty;
1998: if (dirty && project != null)
1999: project.setGroupDirty(true);
2000: }
2001:
2002: public boolean isMissedDeadline() {
2003: if (deadline == 0)
2004: return false;
2005: else
2006: return deadline < getEnd();
2007: }
2008:
2009: public final boolean isExternal() {
2010: return external;
2011: }
2012:
2013: public final void setExternal(boolean external) {
2014: this .external = external;
2015: }
2016:
2017: public final long getProjectId() {
2018: return projectId;
2019: }
2020:
2021: public final void setProjectId(long projectId) {
2022: this .projectId = projectId;
2023: }
2024:
2025: public Project getRootProject() {
2026: Task parent = getWbsParentTask();
2027: if (parent == null)
2028: return getProject();
2029: return parent.getRootProject();
2030: }
2031:
2032: public Project getEnclosingProject() {
2033: if (isSubproject())
2034: return ((SubProj) this ).getSubproject();
2035: Task parent = getWbsParentTask();
2036: if (parent == null)
2037: return getProject();
2038: return parent.getEnclosingProject();
2039: }
2040:
2041: public SubProj getEnclosingSubproject() {
2042: Task parent = getWbsParentTask();
2043:
2044: if (parent == null)
2045: return null;
2046: if (parent.isSubproject())
2047: return (SubProj) parent;
2048: return parent.getEnclosingSubproject();
2049: }
2050:
2051: /**
2052: * Will return a node in the master project that holds he subproject
2053: * @return
2054: */
2055: public Node getEnclosingSubprojectNode() {
2056: SubProj s = getEnclosingSubproject();
2057: if (s == null)
2058: return null;
2059: return ((Task) s).getProject().getTaskOutline().search(s);
2060: }
2061:
2062: public boolean liesInSubproject() {
2063: Task parent = getWbsParentTask();
2064: if (parent == null)
2065: return false;
2066: if (parent.isSubproject())
2067: return true;
2068: return parent.liesInSubproject();
2069: }
2070:
2071: public final boolean isInSubproject() {
2072: return inSubproject;
2073: }
2074:
2075: public final void setInSubproject(boolean inSubproject) {
2076: this .inSubproject = inSubproject;
2077: }
2078:
2079: public final Project getOwningProject() {
2080: return owningProject;
2081: }
2082:
2083: public final void setOwningProject(Project owningProject) {
2084: this .owningProject = owningProject;
2085: }
2086:
2087: public void copyScheduleTo(Task to) {
2088: to.getCurrentSchedule().setStart(
2089: getCurrentSchedule().getStart());
2090: to.getCurrentSchedule().setFinish(
2091: getCurrentSchedule().getFinish());
2092: to.setRawDuration(getDuration());
2093: }
2094:
2095: /**
2096: * Set all schedules to a fixed start and end
2097: * @param start
2098: * @param finish
2099: */
2100: protected void setAllSchedules(long start, long finish) {
2101: getCurrentSchedule().setStart(start);
2102: getCurrentSchedule().setFinish(finish);
2103: getEarlySchedule().setStart(start);
2104: getEarlySchedule().setFinish(finish);
2105: getLateSchedule().setStart(start);
2106: getLateSchedule().setFinish(finish);
2107: }
2108:
2109: public void setAllSchedulesToCurrentDates() {
2110: long start = getCurrentSchedule().getStart();
2111: long finish = getCurrentSchedule().getFinish();
2112: setAllSchedules(start, finish);
2113:
2114: }
2115:
2116: public boolean isAssignable() {
2117: return !isReadOnly();
2118: }
2119:
2120: public final long getLastSavedFinish() {
2121: return lastSavedFinish;
2122: }
2123:
2124: public final void setLastSavedFinish(long currendFinish) {
2125: this .lastSavedFinish = currendFinish;
2126: }
2127:
2128: public final long getLastSavedStart() {
2129: return lastSavedStart;
2130: }
2131:
2132: public final void setLastSavedStart(long currentStart) {
2133: this .lastSavedStart = currentStart;
2134: }
2135:
2136: public int getExpenseType() {
2137: return expenseType;
2138: }
2139:
2140: public void setExpenseType(int budgetType) {
2141: this .expenseType = budgetType;
2142: }
2143:
2144: public int getEffectiveExpenseType() {
2145: int result = expenseType;
2146: if (result == ExpenseType.NONE) {
2147: Task parent = getWbsParentTask();
2148: if (parent != null)
2149: result = parent.getEffectiveExpenseType();
2150: }
2151: if (result == ExpenseType.NONE)
2152: result = getOwningProject().getEffectiveExpenseType();
2153: return result;
2154: }
2155:
2156: public boolean startsBeforeProject() { // special case for SNLT tasks that start before project
2157: return (getConstraintType() == ConstraintType.SNLT // this was changed from SNET - MSP mistakenly displays SNET for these tasks
2158: && getPredecessorList().isEmpty() && getConstraintDate() < getOwningProject()
2159: .getStart());
2160: }
2161:
2162: /**
2163: * Get value of delegateTo, or if null, recursively get parent's value
2164: * @return
2165: */
2166: public Resource getDelegatedTo() {
2167: if (delegatedTo == null) {
2168: Task parent = getWbsParentTask();
2169: if (parent != null)
2170: return parent.getDelegatedTo();
2171: }
2172: return delegatedTo;
2173: }
2174:
2175: public void setDelegatedTo(Resource delegatedTo) {
2176: Resource old = this .delegatedTo;
2177: this .delegatedTo = delegatedTo;
2178: // if (old == null) {
2179: // SwingUtilities.invokeLater(new Runnable() {
2180: //
2181: // public void run() {
2182: // Task newOne = getOwningProject().cloneTask(Task.this);
2183: // }});
2184: // }
2185: }
2186:
2187: public boolean isDelegatedToUser() {
2188: Resource del = getDelegatedTo();
2189: return del != null
2190: && Environment.getLogin().equals(del.getUserAccount());
2191: }
2192:
2193: public String getDelegatedToName() {
2194: Resource del = getDelegatedTo();
2195: return del == null ? "" : del.getName();
2196: }
2197:
2198: public void forSnapshotsAssignments(Closure c, boolean onlyCurrent) {
2199: if (onlyCurrent)
2200: forSnapshotsAssignments(c, -1);
2201: else {
2202: for (int s = 0; s < Settings.numBaselines(); s++) {
2203: forSnapshotsAssignments(c, s);
2204: }
2205: }
2206: }
2207:
2208: public void forSnapshotsAssignments(Closure c, int s) {
2209: TaskSnapshot snapshot;
2210: if (s == -1)
2211: snapshot = (TaskSnapshot) getCurrentSnapshot();
2212: else
2213: snapshot = (TaskSnapshot) getSnapshot(new Integer(s));
2214: if (snapshot == null)
2215: return;
2216: AssociationList snapshotAssignments = snapshot
2217: .getHasAssignments().getAssignments();
2218: if (snapshotAssignments.size() > 0) {
2219: for (Iterator j = snapshotAssignments.iterator(); j
2220: .hasNext();) {
2221: Assignment assignment = (Assignment) j.next();
2222: c.execute(assignment);
2223: }
2224: }
2225: }
2226:
2227: public void forSnapshots(Closure c) {
2228: }
2229:
2230: public void forSnapshots(Closure c, int s) {
2231: }
2232:
2233: }
|