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.assignment;
051:
052: import java.io.IOException;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.io.Serializable;
056: import java.util.ArrayList;
057: import java.util.Collection;
058: import java.util.Iterator;
059: import java.util.List;
060: import java.util.ListIterator;
061:
062: import org.apache.commons.collections.Closure;
063: import org.apache.commons.collections.Predicate;
064: import org.apache.commons.collections.functors.TruePredicate;
065:
066: import com.projity.algorithm.ReverseQuery;
067: import com.projity.algorithm.TimeIteratorGenerator;
068: import com.projity.algorithm.buffer.CalculatedValues;
069: import com.projity.algorithm.buffer.IntervalCallback;
070: import com.projity.algorithm.buffer.NonGroupedCalculatedValues;
071: import com.projity.association.AssociationList;
072: import com.projity.field.FieldContext;
073: import com.projity.functor.CollectionVisitor;
074: import com.projity.options.ScheduleOption;
075: import com.projity.pm.calendar.WorkCalendar;
076: import com.projity.pm.criticalpath.TaskSchedule;
077: import com.projity.pm.resource.Resource;
078: import com.projity.pm.resource.ResourceImpl;
079: import com.projity.pm.scheduling.SchedulingType;
080: import com.projity.pm.task.NormalTask;
081: import com.projity.pm.task.Task;
082: import com.projity.pm.time.HasStartAndEnd;
083: import com.projity.pm.time.MutableInterval;
084:
085: /**
086: * Implementation of class which contains assignments
087: */
088: public class HasAssignmentsImpl implements HasAssignments,
089: HasTimeDistributedData, Serializable, Cloneable {
090: //private static Log log = LogFactory.getLog(HasAssignmentsImpl.class);
091: transient AssociationList assignments;
092:
093: //TODO scheduling rule and effort driven don't make sense for resources, so make them go away?
094: int schedulingRule = ScheduleOption.getInstance()
095: .getSchedulingRule();
096: boolean effortDriven = ScheduleOption.getInstance()
097: .isEffortDriven();
098:
099: public HasAssignmentsImpl() {
100: assignments = new AssociationList();
101: }
102:
103: public boolean isReadOnlyEffortDriven(FieldContext fieldContext) {
104: return getSchedulingType() == SchedulingType.FIXED_WORK;
105: }
106:
107: /**
108: * Copy constructor: It does a deep copy of assignments
109: * @param from
110: */
111: private HasAssignmentsImpl(HasAssignmentsImpl from) {
112: this ();
113: Iterator i = from.assignments.iterator();
114: while (i.hasNext()) {
115: assignments.add(new Assignment((Assignment) i.next()));
116: }
117: }
118:
119: public HasAssignmentsImpl(Collection details) {
120: this ();
121: Iterator i = details.iterator();
122: while (i.hasNext()) {
123: assignments
124: .add(new Assignment((AssignmentDetail) i.next()));
125: }
126: }
127:
128: /**
129: * @param schedule
130: * @return
131: */
132: // public HasAssignments cloneWithSchedule(TaskSchedule currentSchedule) {
133: // return cloneWithSchedule(currentSchedule,null);
134: // }
135: // public HasAssignments cloneWithSchedule(TaskSchedule currentSchedule,Collection details) {
136: // HasAssignmentsImpl newOne;
137: // if (details==null) newOne= new HasAssignmentsImpl(this);
138: // else newOne= new HasAssignmentsImpl(details);
139: // newOne.setScheduleForAssignments(currentSchedule);
140: // return newOne;
141: // }
142: public HasAssignments cloneWithSchedule(TaskSchedule currentSchedule) {
143: HasAssignmentsImpl newOne = new HasAssignmentsImpl(this );
144: newOne.setScheduleForAssignments(currentSchedule);
145: return newOne;
146: }
147:
148: private void setScheduleForAssignments(TaskSchedule currentSchedule) {
149: Iterator i = assignments.iterator();
150: Assignment assignment;
151: while (i.hasNext()) {
152: assignment = (Assignment) i.next();
153: assignment.setTaskSchedule(currentSchedule);
154: assignment.convertToBaselineAssignment(false);
155: }
156: }
157:
158: //very deep copy of assignments contrary to copy constructor which doesn't clone assigments' detail
159: public HasAssignments deepCloneWithTask(Task task) { //TODO doesn't
160: HasAssignmentsImpl newOne = (HasAssignmentsImpl) cloneWithTask(task);
161: return newOne;
162: }
163:
164: public Object clone() {
165: try {
166: return super .clone();
167: } catch (CloneNotSupportedException e) {
168: throw new InternalError();
169: }
170: }
171:
172: public Object cloneWithTask(Task task) {
173: HasAssignmentsImpl clone = (HasAssignmentsImpl) clone();
174: clone.assignments = new AssociationList();
175:
176: //TODO doesn't work when it's copied between projects
177: Iterator i = assignments.iterator();
178: while (i.hasNext()) {
179: clone.assignments.add(((Assignment) i.next())
180: .cloneWithTask(task));
181: // clone.assignments.add(((Assignment)i.next()).cloneWithResourceAndTask(ResourceImpl.getUnassignedInstance(),task));
182: break;
183: }
184:
185: // Iterator i = assignments.iterator();
186: // while (i.hasNext()) {
187: // clone.assignments.add(((Assignment)i.next()).cloneWithTask(task));
188: // }
189:
190: return clone;
191: }
192:
193: public Object cloneWithResource(Resource resource) {
194: HasAssignmentsImpl clone = (HasAssignmentsImpl) clone();
195: clone.assignments = new AssociationList();
196: Iterator i = assignments.iterator();
197: while (i.hasNext()) {
198: clone.assignments.add(((Assignment) i.next())
199: .cloneWithResource(resource));
200: }
201:
202: return clone;
203: }
204:
205: /* (non-Javadoc)
206: * @see com.projity.pm.assignment.HasAssignments#getAssignments()
207: */
208: public AssociationList getAssignments() {
209: return assignments;
210: }
211:
212: /* (non-Javadoc)
213: * @see com.projity.pm.assignment.HasAssignments#addAssignment(com.projity.pm.assignment.Assignment)
214: */
215: public void addAssignment(Assignment assignment) {
216: assignments.add(assignment);
217: }
218:
219: /* (non-Javadoc)
220: * @see com.projity.pm.assignment.HasAssignments#removeAssignment(com.projity.pm.assignment.Assignment)
221: */
222: public void removeAssignment(Assignment assignment) {
223: assignments.remove(assignment);
224: }
225:
226: /**
227: * Finds an assignment given a resource
228: */
229: public Assignment findAssignment(Resource resource) {
230: Iterator i = assignments.iterator();
231: Assignment result = null;
232: while (i.hasNext()) {
233: result = (Assignment) i.next();
234: if (result.getResource() == resource)
235: return result;
236: }
237: return null;
238: }
239:
240: /**
241: * Finds an assignment given a task
242: */
243: public Assignment findAssignment(Task task) {
244: Iterator i = assignments.iterator();
245: Assignment result = null;
246: while (i.hasNext()) {
247: result = (Assignment) i.next();
248: if (result.getTask() == task)
249: return result;
250: }
251: return null;
252: }
253:
254: /**
255: * @return Returns the schedulingRule.
256: */
257: public int getSchedulingType() {
258: return schedulingRule;
259: }
260:
261: /**
262: * @param schedulingType The schedulingRule to set.
263: */
264: public void setSchedulingType(int schedulingType) {
265: this .schedulingRule = schedulingType;
266: }
267:
268: /**
269: * @return Returns the effortDriven.
270: */
271: public boolean isEffortDriven() {
272: return effortDriven;
273: }
274:
275: /**
276: * @param effortDriven The effortDriven to set.
277: */
278: public void setEffortDriven(boolean effortDriven) {
279: this .effortDriven = effortDriven;
280: }
281:
282: /* (non-Javadoc)
283: * @see com.projity.pm.assignment.HasTimeDistributedData#collectIntervalGenerators(java.lang.Object, java.util.Collection)
284: */
285: public void buildReverseQuery(ReverseQuery reverseQuery) {
286: Iterator i = assignments.iterator();
287: Assignment assignment;
288: while (i.hasNext()) {
289: assignment = (Assignment) i.next();
290: if (assignment.isDefault()
291: && !reverseQuery.isAllowDefaultAssignments())
292: continue;
293: assignment.buildReverseQuery(reverseQuery);
294: }
295: }
296:
297: /* (non-Javadoc)
298: * @see com.projity.pm.assignment.HasAssignments#updateAssignment(com.projity.pm.assignment.Assignment)
299: */
300: public void updateAssignment(Assignment modified) {
301: ListIterator i = assignments.listIterator();
302: Assignment current = null;
303: while (i.hasNext()) {
304: current = (Assignment) i.next();
305: if (current.getTask() == modified.getTask()
306: && current.getResource() == modified.getResource()) {
307: i.set(modified); // replace current with new one
308: break;
309: }
310: }
311: }
312:
313: public static Closure forAllAssignments(Closure visitor,
314: Predicate filter) {
315: return new CollectionVisitor(visitor, filter) {
316: protected final Collection getCollection(Object arg0) {
317: return ((HasAssignments) arg0).getAssignments();
318: }
319: };
320: }
321:
322: public static Closure forAllAssignments(Closure visitor) {
323: return forAllAssignments(visitor, TruePredicate.INSTANCE);
324: }
325:
326: public void forEachInterval(Closure visitor, Object type,
327: WorkCalendar workCalendar) {
328: NonGroupedCalculatedValues calculatedValues = new NonGroupedCalculatedValues(
329: false, 0);
330: ListIterator i = assignments.listIterator();
331: Assignment assignment = null;
332:
333: while (i.hasNext()) { // add in all child groups
334: assignment = (Assignment) i.next();
335: barCallback.setWorkCalendar(assignment
336: .getEffectiveWorkCalendar()); // use this assignments cal because it might work on off calendar time
337: assignment.calcDataBetween(type, null, calculatedValues);
338: }
339: calculatedValues.makeContiguousNonZero(barCallback,
340: workCalendar);
341: //calculatedValues.dump();
342: }
343:
344: public void forEachWorkingInterval(final Closure visitor,
345: boolean mergeWorking, WorkCalendar workCalendar) {
346: barCallback.initialize(workCalendar, visitor, true);
347: forEachInterval(visitor, ACTUAL_WORK, workCalendar);
348: /* if the splitting should be at latest bar use this code
349: barCallback.finish();
350: barCallback.initialize(workCalendar,visitor,false);
351: */
352: barCallback.initialize(workCalendar, visitor, true);
353: forEachInterval(visitor, REMAINING_WORK, workCalendar);
354: }
355:
356: private static BarSeriesCallback barCallback = new BarSeriesCallback();
357:
358: private static class BarSeriesCallback implements IntervalCallback {
359: long barStart = 0;
360: WorkCalendar workCalendar;
361: MutableInterval interval = new MutableInterval(0, 0);
362: Closure visitor;
363: long previousEnd = 0;
364:
365: private void executeVisitor(long start, long end) {
366: start = Math.max(start, previousEnd); // prevent overlap in case of multiple assignments that do not have same advancement
367: if (start > end)
368: return;
369:
370: interval.setStart(start);
371: interval.setEnd(end);
372:
373: previousEnd = end;
374: //System.out.println("bar " + new Date(start) + " " + new Date(end));
375: visitor.execute(interval);
376: barStart = 0;
377: }
378:
379: public void setWorkCalendar(WorkCalendar workCalendar) {
380: this .workCalendar = workCalendar;
381:
382: }
383:
384: private static double ALMOST_ZERO = 0.00001;
385:
386: public void add(int index, long start, long end, double value) {
387: if (value <= ALMOST_ZERO) { // because of rounding errors, treat 0 as something very small
388: if (workCalendar.compare(end, start, false) == 0)
389: return;
390: if (barStart > 0) {
391: start = workCalendar.adjustInsideCalendar(start,
392: true);
393: executeVisitor(barStart, start);
394: }
395: } else {
396: if (barStart == 0) {
397: //hk barStart = start;
398: barStart = workCalendar.adjustInsideCalendar(start,
399: false);
400: }
401: if (index == 0) {// last bar, must draw
402: end = workCalendar.adjustInsideCalendar(end, true);
403: executeVisitor(barStart, end);
404: // System.out.println("last bar " + new Date(start) + " " + new Date(end));
405: }
406: }
407: }
408:
409: private void initialize(WorkCalendar workCalendar,
410: Closure visitor, boolean firstTime) {
411: if (firstTime)
412: previousEnd = 0;
413: this .workCalendar = workCalendar;
414: this .visitor = visitor;
415: }
416:
417: private void finish() {
418: if (barStart != 0) {
419: // System.out.println("finishing bar " + new Date(barStart) + " " + new Date(previousEnd));
420: executeVisitor(barStart, previousEnd);
421: }
422: barStart = 0;
423: }
424: }
425:
426: /* (non-Javadoc)
427: * @see com.projity.pm.costing.EarnedValueValues#acwp(long, long)
428: */
429: public double acwp(long start, long end) {
430: return TimeDistributedDataConsolidator.acwp(start, end,
431: childrenToRollup());
432: }
433:
434: /* (non-Javadoc)
435: * @see com.projity.pm.costing.EarnedValueValues#bac(long, long)
436: */
437: public double bac(long start, long end) {
438: return TimeDistributedDataConsolidator.bac(start, end,
439: childrenToRollup());
440: }
441:
442: /* (non-Javadoc)
443: * @see com.projity.pm.costing.EarnedValueValues#bcwp(long, long)
444: */
445: public double bcwp(long start, long end) {
446: return TimeDistributedDataConsolidator.bcwp(start, end,
447: childrenToRollup());
448: }
449:
450: /* (non-Javadoc)
451: * @see com.projity.pm.costing.EarnedValueValues#bcws(long, long)
452: */
453: public double bcws(long start, long end) {
454: return TimeDistributedDataConsolidator.bcws(start, end,
455: childrenToRollup());
456: }
457:
458: /* (non-Javadoc)
459: * @see com.projity.pm.assignment.HasTimeDistributedData#cost(long, long)
460: */
461: public double cost(long start, long end) {
462: return TimeDistributedDataConsolidator.cost(start, end,
463: childrenToRollup());
464: }
465:
466: /* (non-Javadoc)
467: * @see com.projity.pm.assignment.HasTimeDistributedData#cost(long, long)
468: */
469: public double baselineCost(long start, long end) {
470: return TimeDistributedDataConsolidator.baselineCost(start, end,
471: childrenToRollup());
472: }
473:
474: /* (non-Javadoc)
475: * @see com.projity.pm.assignment.HasTimeDistributedData#cost(long, long)
476: */
477: public long baselineWork(long start, long end) {
478: return TimeDistributedDataConsolidator.baselineWork(start, end,
479: childrenToRollup(), true);
480: }
481:
482: /* (non-Javadoc)
483: * @see com.projity.pm.assignment.HasTimeDistributedData#actualCost(long, long)
484: */
485: public double actualCost(long start, long end) {
486: return TimeDistributedDataConsolidator.actualCost(start, end,
487: childrenToRollup());
488: }
489:
490: /* (non-Javadoc)
491: * @see com.projity.pm.assignment.HasTimeDistributedData#work(long, long)
492: */
493: public long work(long start, long end) {
494: return TimeDistributedDataConsolidator.work(start, end,
495: childrenToRollup(), true);
496: }
497:
498: /* (non-Javadoc)
499: * @see com.projity.pm.assignment.HasTimeDistributedData#actualWork(long, long)
500: */
501: public long actualWork(long start, long end) {
502: return TimeDistributedDataConsolidator.actualWork(start, end,
503: childrenToRollup(), true);
504: }
505:
506: public long remainingWork(long start, long end) {
507: return TimeDistributedDataConsolidator.remainingWork(start,
508: end, childrenToRollup(), true);
509: }
510:
511: public void calcDataBetween(Object type,
512: TimeIteratorGenerator generator, CalculatedValues values) {
513: Iterator i = getAssignments().iterator();
514: while (i.hasNext()) {
515: ((Assignment) i.next()).calcDataBetween(type, generator,
516: values);
517: }
518:
519: }
520:
521: public static List extractOppositeList(List list, boolean leftObject) {
522: Iterator i = list.iterator();
523: ArrayList assignments = new ArrayList();
524: while (i.hasNext()) { // go thru tasks or resources
525: Object object = i.next();
526: if (!(object instanceof HasAssignments))
527: continue; //TODO currently getting voidNodeImpl's. This should go away when fixed
528: HasAssignments hasAssignments = (HasAssignments) object;
529: assignments.addAll(hasAssignments.getAssignments());
530: }
531: return AssociationList.extractDistinct(assignments, leftObject);
532: }
533:
534: /* (non-Javadoc)
535: * @see com.projity.pm.assignment.HasTimeDistributedData#childrenToRollup()
536: */
537: public Collection childrenToRollup() {
538: return assignments;
539: }
540:
541: /**
542: *
543: */
544: private class AssignmentDurationSummer implements Closure {
545: private long sum;
546: private WorkCalendar workCalendar;
547:
548: AssignmentDurationSummer(WorkCalendar workCalendar) {
549: this .workCalendar = workCalendar;
550: sum = 0;
551: }
552:
553: public void execute(Object arg0) {
554: HasStartAndEnd interval = (HasStartAndEnd) arg0;
555: sum += workCalendar.compare(interval.getEnd(), interval
556: .getStart(), false);
557: }
558:
559: public long getSum() {
560: return sum;
561: }
562: }
563:
564: /**
565: * Compute the sum of active assignment durations. If there are multiple assignments, then
566: * the calendar time of the union of active periods is used, otherwise, if just one assignment
567: * (which could be the default assignment), use the assignment duration
568: * @param workCalendar
569: * @return
570: */
571: public long calcActiveAssignmentDuration(WorkCalendar workCalendar) {
572: AssociationList assignments = getAssignments();
573: // Most of the time there is just one assignment. If that's the case, use the assignment duration
574: if (assignments.size() == 1)
575: return ((Assignment) assignments.getFirst())
576: .getDurationMillis();
577: AssignmentDurationSummer summer = new AssignmentDurationSummer(
578: workCalendar);
579: forEachWorkingInterval(summer, false, workCalendar);
580: return summer.getSum();
581:
582: }
583:
584: private void writeObject(ObjectOutputStream s) throws IOException {
585: s.defaultWriteObject();
586: }
587:
588: private void readObject(ObjectInputStream s) throws IOException,
589: ClassNotFoundException {
590: s.defaultReadObject();
591: assignments = new AssociationList();
592: }
593:
594: /* (non-Javadoc)
595: * @see com.projity.pm.assignment.HasTimeDistributedData#fixedCost(long, long)
596: */
597: public double fixedCost(long start, long end) {
598: // TODO Auto-generated method stub
599: return 0;
600: }
601:
602: /* (non-Javadoc)
603: * @see com.projity.pm.assignment.HasTimeDistributedData#actualFixedCost(long, long)
604: */
605: public double actualFixedCost(long start, long end) {
606: // TODO Auto-generated method stub
607: return 0;
608: }
609:
610: /* (non-Javadoc)
611: * @see com.projity.pm.assignment.HasTimeDistributedData#isLabor()
612: */
613: public boolean isLabor() {
614: // TODO Auto-generated method stub
615: return false;
616: }
617:
618: /* (non-Javadoc)
619: * @see com.projity.pm.assignment.HasAssignments#hasLaborAssignment()
620: */
621: public boolean hasLaborAssignment() {
622: Iterator i = assignments.iterator();
623: while (i.hasNext()) {
624: if (((Assignment) i.next()).isLabor())
625: return true;
626: }
627: return false;
628: }
629:
630: /* (non-Javadoc)
631: * @see com.projity.pm.assignment.HasAssignments#invalidateAssignmentCalendars()
632: */
633: public void invalidateAssignmentCalendars() {
634: Iterator i = assignments.iterator();
635: while (i.hasNext()) {
636: ((Assignment) i.next()).invalidateAssignmentCalendar();
637: }
638: }
639:
640: public boolean hasActiveAssignment(long start, long end) {
641: Iterator i = assignments.iterator();
642: Assignment assignment;
643: while (i.hasNext()) {
644: assignment = (Assignment) i.next();
645: if (assignment.isActiveBetween(start, end))
646: return true;
647: }
648: return false;
649: }
650:
651: public long getEarliestAssignmentStart() {
652: long result = Long.MAX_VALUE;
653: Iterator i = assignments.iterator();
654: while (i.hasNext()) {
655: result = Math.min(result, ((Assignment) i.next())
656: .getStart());
657: }
658: return result;
659: }
660:
661: }
|