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.util.Collection;
053: import java.util.Iterator;
054: import java.util.LinkedList;
055: import java.util.ListIterator;
056:
057: import com.projity.pm.task.SubProj;
058: import com.projity.pm.task.Task;
059:
060: /**
061: * This class implements a task list in predecessor/parent order. That is, the successors of any given
062: * task are guaranteed to be after that task in the list. Also wbs children are after their parents.
063: * This ordering is needed for the critical path algorithm.
064: */
065: public class PredecessorTaskList {
066: private LinkedList list = new LinkedList();
067: private int calculationStateCount = 0;
068: private boolean markerStatus;
069: private int numberOfReverseScheduledTasks = 0;
070: public static final int CALCULATION_STATUS_STEP = 3;
071: private SchedulingAlgorithm schedulingAlgorithm;
072:
073: PredecessorTaskList(SchedulingAlgorithm schedulingAlgorithm) {
074: this .schedulingAlgorithm = schedulingAlgorithm;
075: }
076:
077: void removeTask(Task task) {
078: if (task.isReverseScheduled())
079: numberOfReverseScheduledTasks--;
080:
081: Iterator i = list.iterator();
082: TaskReference current;
083: // the item may be in the list once or twice. It may be that it is in twice, but the
084: // task is no longer a parent
085: while (i.hasNext()) {
086: current = (TaskReference) i.next();
087: if (current.task == task) {
088: i.remove();
089: }
090: }
091: }
092:
093: ListIterator reverseIterator() {
094: return list.listIterator(list.size());
095: }
096:
097: /**
098: * Helper to arrange one task
099: * @param task
100: */
101: private void arrangeSingleTask(final Task task) {
102: task.arrangeTask(list, markerStatus, 0);
103: if (task.isReverseScheduled())
104: numberOfReverseScheduledTasks++;
105: }
106:
107: /**
108: * Add a subproject. It will convert the existing task into a parent and add all children
109: * @param subproject
110: */
111: public void addSubproject(final Task subproject) {
112: // remove sentinels
113: TaskReference startSentinel = (TaskReference) list
114: .removeFirst();
115: TaskReference endSentinel = (TaskReference) list.removeLast();
116:
117: // mark tasks to be added as not yet treated
118: boolean m = !getMarkerStatus();
119: subproject.setMarkerStatus(m);
120: subproject.markTaskAsNeedingRecalculation();
121: Iterator j = ((SubProj) subproject).getSubproject().getTasks()
122: .iterator();
123: while (j.hasNext()) {
124: Task task = (Task) j.next();
125: task.setMarkerStatus(m);
126: task.markTaskAsNeedingRecalculation();
127:
128: }
129:
130: removeTask(subproject); // remove existing one
131: arrangeSingleTask(subproject); // add it back - it will become a parent
132: // add child tasks
133: Iterator i = ((SubProj) subproject).getSubproject().getTasks()
134: .iterator();
135: while (i.hasNext())
136: arrangeSingleTask((Task) i.next());
137:
138: // put back sentinels
139: list.addFirst(startSentinel);
140: list.addLast(endSentinel);
141: }
142:
143: /**
144: * Insert a task into the list. Go thru and insert it after its parent
145: * The task being inserted is a new task and as such has no preds/succs. Just insert it after its parent
146: * * @param hasDependencies
147: */
148: void arrangeTask(Task task) {
149: if (task.isReverseScheduled())
150: numberOfReverseScheduledTasks++;
151: task.setMarkerStatus(markerStatus);
152: TaskReference previousTaskReference;
153: Task previousTask;
154: // go thru in reverse order inserting after first predecessor or parent encountered
155: ListIterator i = list.listIterator();
156: TaskReference taskReference = new TaskReference(task);
157: while (i.hasNext()) {
158: previousTaskReference = (TaskReference) i.next();
159: previousTask = previousTaskReference.getTask();
160: if (task.getWbsParentTask() == previousTask) {
161: i.add(taskReference);
162: return;
163: }
164: }
165: i.previous(); // add before end sentinel
166: i.add(taskReference);
167: }
168:
169: /**
170: * Return a list iterator - delegates to internal list
171: * @return list iterator
172: */
173: ListIterator listIterator() {
174: return list.listIterator();
175: }
176:
177: LinkedList getList() {
178: return list;
179: }
180:
181: public void dump() {
182: ListIterator i = list.listIterator();
183: Object x;
184: while (i.hasNext()) {
185: x = i.next();
186: System.out.println(x);
187: }
188: }
189:
190: synchronized int getFreshCalculationStateCount() {
191: while (calculationStateCount % CALCULATION_STATUS_STEP != 0)
192: // go by 3s so we can see what happens during different passes
193: calculationStateCount++;
194: return calculationStateCount;
195: }
196:
197: synchronized int getNextCalculationStateCount() {
198: calculationStateCount += 1; // just get next one
199: return calculationStateCount;
200: }
201:
202: int getCalculationStateCount() {
203: return calculationStateCount;
204: }
205:
206: boolean addAll(Collection tasks) {
207: Task task;
208: list.clear();
209: toggleMarkerStatus();
210: Iterator i = tasks.iterator();
211: while (i.hasNext()) {
212: task = (Task) i.next();
213: task.arrangeTask(list, markerStatus, 0);
214: if (task.isReverseScheduled())
215: numberOfReverseScheduledTasks++;
216: }
217: return true;
218: }
219:
220: private void setDebugDependencyOrder() {
221: int count = 0;
222: Iterator i = list.iterator();
223: Task task;
224: TaskReference ref;
225: while (i.hasNext()) {
226: ref = (TaskReference) i.next();
227: if (ref.getType() == TaskReference.PARENT_END)
228: continue;
229: task = ref.getTask();
230: task.setDebugDependencyOrder(count++);
231: }
232: }
233:
234: void rearrangeAll() {
235: LinkedList oldList = list;
236: // store off sentinels to put them back later
237: TaskReference startSentinel = (TaskReference) list
238: .removeFirst();
239: TaskReference endSentinel = (TaskReference) list.removeLast();
240: list = new LinkedList();
241:
242: Task task;
243: Iterator i = oldList.iterator();
244: toggleMarkerStatus();
245: while (i.hasNext()) {
246: task = ((TaskReference) i.next()).getTask();
247: arrangeSingleTask(task);
248: }
249: list.addFirst(startSentinel);
250: list.addLast(endSentinel);
251: // setDebugDependencyOrder();
252: }
253:
254: boolean hasReverseScheduledTasks() {
255: return (numberOfReverseScheduledTasks > 0);
256: }
257:
258: public static final class TaskReference implements Comparable {
259: static final int PARENT_BEGIN = -1;
260: static final int CHILD = 0;
261: static final int PARENT_END = 1;
262:
263: public TaskReference(Task task) {
264: this .task = task;
265: }
266:
267: Task task;
268: int type = CHILD;
269: TaskReference opposite = null;
270: long calculationStateCount = 0;
271:
272: /**
273: * @return Returns the task.
274: */
275: public Task getTask() {
276: return task;
277: }
278:
279: /* (non-Javadoc)
280: * @see java.lang.Comparable#compareTo(java.lang.Object)
281: */
282: public int compareTo(Object arg0) {
283: if (arg0 instanceof Task)
284: return (getTask() == arg0 ? 0 : -1);
285: return (arg0 == this ? 0 : -1);
286: }
287:
288: public void setParentBegin() {
289: type = PARENT_BEGIN;
290: }
291:
292: public void setParentEnd() {
293: type = PARENT_END;
294: }
295:
296: public String toString() {
297: String result = task.toString();
298: if (type == PARENT_BEGIN)
299: result += " begin";
300: else if (type == PARENT_END)
301: result += " end";
302: return result;
303: }
304:
305: /**
306: * @return Returns the type.
307: */
308: public int getType() {
309: return type;
310: }
311: }
312:
313: /**
314: * Refresh the Reverse schedule count - called in response to change in constraint type field
315: */
316: void recalculateReverseScheduledCount() {
317: numberOfReverseScheduledTasks = 0;
318:
319: Iterator i = list.iterator();
320: Task task;
321: while (i.hasNext()) {
322: task = ((TaskReference) i.next()).getTask();
323: if (task.isReverseScheduled())
324: numberOfReverseScheduledTasks++;
325: }
326: }
327:
328: /**
329: * @return Returns the markerStatus.
330: */
331: public final boolean getMarkerStatus() {
332: return markerStatus;
333: }
334:
335: final boolean toggleMarkerStatus() {
336: markerStatus = !markerStatus;
337: return markerStatus;
338: }
339: }
|