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.contour;
051:
052: import java.util.ArrayList;
053: import java.util.Collection;
054:
055: import com.projity.pm.time.MutableInterval;
056: import com.projity.strings.Messages;
057:
058: /**
059: * @stereotype strategy
060: */
061: public class PersonalContour extends AbstractContour {
062: private static final String name = Messages
063: .getString("PersonalContour.personal"); //$NON-NLS-1$
064:
065: public boolean isPersonal() {
066: return true;
067: }
068:
069: /**
070: * Calculates total work
071: */
072: public long calcTotalWork(long assignmentDuration) {
073: long work = 0;
074: for (int i = 0; i < contourBuckets.length; i++)
075: work += ((PersonalContourBucket) contourBuckets[i])
076: .calcWork();
077: return work;
078: }
079:
080: public String getName() {
081: return name;
082: }
083:
084: /* (non-Javadoc)
085: * @see com.projity.pm.assignment.contour.AbstractContour#getType()
086: */
087: public int getType() {
088: return ContourTypes.CONTOURED;
089: }
090:
091: /**
092: *
093: */
094: private PersonalContour(AbstractContourBucket contourBuckets[]) {
095: super (contourBuckets);
096: }
097:
098: public static PersonalContour makePersonal(AbstractContour contour,
099: long assignmentDuration) {
100: if (contour.isPersonal())
101: return (PersonalContour) contour;
102: AbstractContourBucket newBuckets[] = new AbstractContourBucket[contour.contourBuckets.length];
103: StandardContourBucket bucket;
104: for (int i = 0; i < contour.contourBuckets.length; i++) {
105: bucket = (StandardContourBucket) contour.contourBuckets[i];
106: newBuckets[i] = PersonalContourBucket.getInstance(bucket
107: .getBucketDuration(assignmentDuration), bucket
108: .getUnits());
109: }
110: PersonalContour newOne = getInstance(newBuckets);
111: return newOne;
112: }
113:
114: public static PersonalContour constructUsingSizeOf(
115: AbstractContour from) {
116: PersonalContour newOne = getInstance(new AbstractContourBucket[from.contourBuckets.length]);
117: newOne.maxUnits = newOne.calcMaxUnits();
118: return newOne;
119: }
120:
121: public static PersonalContour getInstance(Collection list) {
122: AbstractContourBucket newBuckets[] = new AbstractContourBucket[list
123: .size()];
124: list.toArray(newBuckets);
125: PersonalContour newContour = PersonalContour
126: .getInstance(newBuckets);
127: newContour.validate();
128: return newContour;
129: }
130:
131: public void validate() {
132: for (int i = 0; i < this .contourBuckets.length; i++)
133: if (contourBuckets[i].getBucketDuration(0) < 0
134: || contourBuckets[i].getUnits() < 0)
135: System.out.println("error neg bucket!\n"
136: + this .toString(0));
137: }
138:
139: public static PersonalContour getInstance(
140: AbstractContourBucket[] contourBuckets) {
141: return new PersonalContour(contourBuckets);
142: }
143:
144: /**
145: * Add empty duration bucket either before or after
146: * @param from
147: * @param duration
148: * @param after
149: * @return
150: */
151: public static PersonalContour addEmptyBucket(AbstractContour from,
152: long duration, boolean after) {
153: int oldLength = from.contourBuckets.length;
154: PersonalContour newContour = getInstance(new AbstractContourBucket[oldLength + 1]);
155:
156: System.arraycopy(from.contourBuckets, 0,
157: newContour.contourBuckets, after ? 0 : 1, oldLength); // replace old with new and truncate to correct length
158: newContour.contourBuckets[after ? oldLength : 0] = PersonalContourBucket
159: .getInstance(duration, 0.0);
160: return newContour;
161: }
162:
163: /**
164: * Set the duration of the personal contour. This implies either truncating the bucket array or changing the last bucket
165: * to accommodate the new duration. Note that only the last bucket will be modified. The number of buckets will never increase.
166: * Since the buckets themselves are immutable, the last element will most likely be replaced.
167: * @param newDuration: The new duration to set to.
168: */
169: public AbstractContour adjustDuration(long newDuration,
170: long actualDuration) {
171: //TODO incorporate actual duration
172:
173: PersonalContour newContour = constructUsingSizeOf(this );
174:
175: PersonalContourBucket bucket = null;
176: for (int i = 0; i < contourBuckets.length; i++) {
177: bucket = (PersonalContourBucket) contourBuckets[i];
178: newContour.contourBuckets[i] = bucket;
179: newDuration -= bucket.getDuration();
180: if (newDuration <= 0) {
181: newContour.contourBuckets[i] = bucket
182: .adjustDuration(newDuration);// adding a negative value
183: if (i < contourBuckets.length - 1) // if shortening number of buckets
184: System.arraycopy(newContour.contourBuckets, 0,
185: newContour.contourBuckets, 0, i + 1); // replace old with new and truncate to correct length
186:
187: AbstractContour result = newContour.makePacked(); // pack so as to get rid of any trailing empty buckets
188: return result;
189: }
190: }
191: // extend last bucket to account for duration
192: newContour.contourBuckets[contourBuckets.length - 1] = bucket
193: .adjustDuration(newDuration);
194: return newContour;
195: }
196:
197: // private AbstractContour makeFlatIfPossible() {
198: // if (contourBuckets.length == 1)
199: // contourBuckets[0].units
200: // }
201:
202: /**
203: * Gets all buckets before a duration, cutting a bucket in half if needed. Will return empty list if at start
204: * @param atDuration
205: * @param newBucket
206: * @return
207: */
208: private ArrayList bucketsBeforeDuration(long atDuration) {
209: ArrayList newList = new ArrayList();
210: boolean inserted = false;
211: PersonalContourBucket bucket = null;
212: long cursorDuration = 0;
213: for (int i = 0; i < contourBuckets.length; i++) {
214: bucket = (PersonalContourBucket) contourBuckets[i];
215: cursorDuration += bucket.getDuration();
216: if (cursorDuration < atDuration) { // if already treated or before start, just use existing one
217: newList.add(bucket);
218: } else {
219: long fractionedBucketDuration = atDuration
220: - (cursorDuration - bucket.getDuration());
221: if (fractionedBucketDuration > 0) {
222: if (bucket.isFiller())
223: newList.add(FillerContourBucket
224: .getInstance(fractionedBucketDuration));
225: else
226: newList.add(PersonalContourBucket.getInstance(
227: fractionedBucketDuration, bucket
228: .getUnits()));
229: }
230: break;
231: }
232: }
233: return newList;
234: }
235:
236: private ArrayList bucketsAfterDuration(long atDuration,
237: boolean excludeFiller) {
238: ArrayList newList = new ArrayList();
239: boolean inserted = false;
240: PersonalContourBucket bucket = null;
241: long cursorDuration = 0;
242: for (int i = 0; i < contourBuckets.length; i++) {
243: bucket = (PersonalContourBucket) contourBuckets[i];
244: cursorDuration += bucket.getDuration();
245: if (excludeFiller && bucket.isFiller()) // see if should skip filler buckets
246: continue;
247: if (cursorDuration > atDuration) { // if already treated or before start, just use existing one
248: if (cursorDuration - bucket.getDuration() >= atDuration) {
249: newList.add(bucket); // if start of this bucket is past atDuration, just add it
250: } else {
251: long fractionedBucketDuration = cursorDuration
252: - atDuration;
253: if (fractionedBucketDuration > 0)
254: newList.add(PersonalContourBucket.getInstance(
255: fractionedBucketDuration, bucket
256: .getUnits()));
257: }
258: }
259: }
260: return newList;
261: }
262:
263: /**
264: * Removes any buckets having filler status after a given date. This is used in the obscure case where we uncomplete
265: * work and we want to eliminate gaps that exist due to dependency dates. These gaps should not be considered part of the contour.
266: * @param atDuration
267: * @return
268: */
269: public AbstractContour removeFillerAfter(long atDuration) {
270: ArrayList newList = bucketsBeforeDuration(atDuration);
271: newList.addAll(bucketsAfterDuration(atDuration, false)); // exclude filler
272: return getInstance(newList).makePacked();
273: }
274:
275: /**
276: * Given an interval as start and end, determine the range which this interval can move in.
277: * @param start
278: * @param end
279: * @return an interval which is a superset of the start,end interval
280: */
281: public MutableInterval getRangeThatIntervalCanBeMoved(long start,
282: long end) {
283: ArrayList tempList = bucketsBeforeDuration(start);
284: long startConstraint = start;
285: long endConstraint = Long.MAX_VALUE; // by default unbounded
286: if (tempList.size() > 0) {
287: PersonalContourBucket bucket = (PersonalContourBucket) tempList
288: .get(tempList.size() - 1);
289: if (bucket.getUnits() == 0)
290: startConstraint = start - bucket.getDuration();
291: }
292: tempList.clear();
293: tempList = bucketsAfterDuration(end, false);
294: if (tempList.size() > 0) {
295: PersonalContourBucket bucket = (PersonalContourBucket) tempList
296: .get(0);
297: if (bucket.getUnits() == 0)
298: endConstraint = end + bucket.getDuration();
299: else
300: endConstraint = end;
301: }
302: return new MutableInterval(startConstraint, endConstraint);
303:
304: }
305:
306: public PersonalContour setInterval(long startDuration,
307: long endDuration, double units) {
308: ArrayList newList = new ArrayList();
309: newList.addAll(bucketsBeforeDuration(startDuration));
310: newList.add(PersonalContourBucket.getInstance(endDuration
311: - startDuration, units));
312: newList.addAll(bucketsAfterDuration(endDuration, false));
313: return getInstance(newList).makePacked();
314: }
315:
316: /**
317: * Inserts a bucket into the contour. If the bucket is past the end, it is not inserted
318: * @param atDuration
319: * @param newBucket
320: * @return
321: */
322: public PersonalContour insertBucket(long atDuration,
323: AbstractContourBucket newBucket) {
324: ArrayList newList = new ArrayList();
325: boolean inserted = false;
326: PersonalContourBucket bucket = null;
327: long cursorDuration = 0;
328: for (int i = 0; i < contourBuckets.length; i++) {
329: bucket = (PersonalContourBucket) contourBuckets[i];
330: cursorDuration += bucket.getDuration();
331: if (inserted || cursorDuration < atDuration) { // if already treated or before start, just use existing one
332: newList.add(bucket);
333: } else {
334: long d = cursorDuration - atDuration;
335: newList.add(PersonalContourBucket.getInstance(d, bucket
336: .getUnits()));
337: newList.add(newBucket); // do this bucket after
338: newList.add(PersonalContourBucket.getInstance(bucket
339: .getDuration()
340: - d, bucket.getUnits()));
341: inserted = true;
342: }
343: }
344: return getInstance(newList).makePacked();
345: }
346:
347: // public PersonalContour setContiguousDuration(long startOfRegion, long duration) {
348: //
349: // //not yet implemented
350: // ArrayList newList = new ArrayList();
351: // boolean inserted = false;
352: // PersonalContourBucket bucket = null;
353: // long cursorDuration = 0;
354: // long regionDuration = 0;
355: // for (int i=0; i < contourBuckets.length; i++) {
356: // bucket = (PersonalContourBucket)contourBuckets[i];
357: // cursorDuration += bucket.getDuration();
358: // if (inserted || cursorDuration < startOfRegion) { // if already treated or before start, just use existing one
359: // newList.add(bucket);
360: // } else { // start of region
361: // regionDuration += bucket.getDuration();
362: // if (regionDuration < duration)
363: // newList.add(bucket);
364: //
365: //
366: //
367: // long d = cursorDuration - startOfRegion;
368: // if (d > 0)
369: // newList.add(PersonalContourBucket.getInstance(d,bucket.getUnits()));
370: // // at region now
371: // //newList.add(newBucket); // do this bucket after
372: // newList.add(PersonalContourBucket.getInstance(bucket.getDuration()-d,bucket.getUnits()));
373: // inserted = true;
374: // }
375: // }
376: // return getInstance(newList).makePacked();
377: //
378: // }
379:
380: /**
381: * Extend a bucket by a duration
382: * @param atDuration - point on bucket to extend
383: * @param extendDuration - amount to extend - can be negative to contract
384: * @return - new contour
385: */
386: public PersonalContour extendBucket(long atDuration,
387: long extendDuration) {
388: PersonalContour newContour = constructUsingSizeOf(this );
389: PersonalContourBucket bucket = null;
390: boolean extended = false;
391: long cursorDuration = 0;
392: for (int i = 0; i < contourBuckets.length; i++) {
393: bucket = (PersonalContourBucket) contourBuckets[i];
394: newContour.contourBuckets[i] = bucket; // use existing bucket by default
395: cursorDuration += bucket.getDuration();
396: if (!extended && cursorDuration >= atDuration) { // if the current bucket overlaps the point
397: if (bucket.getUnits() != 0) { // if it is a working bucket, then skip it
398: extended = true;
399: newContour.contourBuckets[i] = PersonalContourBucket
400: .getInstance(Math.max(0, bucket
401: .getDuration()
402: + extendDuration), bucket
403: .getUnits());
404: }
405: }
406: }
407: return newContour.makePacked();
408: }
409:
410: /**
411: * Shift an interval (usually a bar) to the right or left
412: * @param start - start of interval (expressed as an offset from the start)
413: * @param end - end of interval (expressed as an offset from the start)
414: * @param shiftDuration (if positive, shifting right, negative means shifting left)
415: * @return
416: */
417: public PersonalContour shift(long start, long end,
418: long shiftDuration) {
419: if (shiftDuration == 0)
420: return this ;
421: ArrayList newList = new ArrayList();
422: if (shiftDuration > 0) {
423: // we are shifting to right, so remove a period corresponding to shiftDuration immediately after the interval
424: newList.addAll(bucketsBeforeDuration(end));
425: newList.addAll(bucketsAfterDuration(end + shiftDuration,
426: false));
427:
428: PersonalContour temp = getInstance(newList).makePacked();
429: newList.clear();
430:
431: //Now add in a non-work period before the start
432: newList.addAll(temp.bucketsBeforeDuration(start));
433: newList.add(PersonalContourBucket.getInstance(
434: shiftDuration, 0));
435: newList.addAll(temp.bucketsAfterDuration(start, false));
436: } else {
437: // we will be shifting to the left, so add padding after the interval so later bars will stay fixed
438: newList.addAll(bucketsBeforeDuration(end));
439: newList.add(PersonalContourBucket.getInstance(
440: -shiftDuration, 0)); // note shift duration is negative
441: newList.addAll(bucketsAfterDuration(end, false));
442:
443: PersonalContour temp = getInstance(newList).makePacked();
444: newList.clear();
445: // Now remove the period corresponding to shiftDuration before the bar
446: newList.addAll(temp.bucketsBeforeDuration(start
447: + shiftDuration)); // note shift duration is negative
448: newList.addAll(temp.bucketsAfterDuration(start, false));
449: }
450: PersonalContour result = getInstance(newList).makePacked();
451: return result;
452: }
453:
454: public AbstractContour extend(long end, long extendDuration) {
455: if (extendDuration == 0)
456: return this ;
457: PersonalContour result;
458: ArrayList newList = new ArrayList();
459: if (extendDuration > 0) {
460: newList.addAll(bucketsBeforeDuration(end));
461: newList.addAll(bucketsAfterDuration(end + extendDuration,
462: false));
463: PersonalContour temp = getInstance(newList).makePacked();
464: result = temp.extendBucket(end, extendDuration); // extend interval
465: } else {
466: newList.addAll(bucketsBeforeDuration(end + extendDuration));
467: newList.add(PersonalContourBucket.getInstance(
468: -extendDuration, 0)); // whitespace replaces area removed by shortening
469: newList.addAll(bucketsAfterDuration(end, false));
470: result = getInstance(newList).makePacked();
471: }
472: return result;
473: }
474:
475: public AbstractContour extendBefore(long start, long extendDuration) {
476: if (extendDuration == 0)
477: return this ;
478: PersonalContour result;
479: ArrayList newList = new ArrayList();
480: PersonalContour temp = extendBucket(start, -extendDuration); // extend at point
481: if (extendDuration < 0) {
482: newList.addAll(temp.bucketsBeforeDuration(start
483: + extendDuration)); // add all up to new start - extend duration is neg
484: newList.addAll(temp.bucketsAfterDuration(start, false));
485: } else {
486: newList.addAll(temp.bucketsBeforeDuration(start));
487: newList.add(PersonalContourBucket.getInstance(
488: extendDuration, 0)); // whitespace replaces area removed by shortening
489: newList.addAll(temp.bucketsAfterDuration(start, false));
490:
491: }
492: result = getInstance(newList).makePacked();
493: return result;
494: }
495:
496: /**
497: * Return an optimized contour that has no superflous info
498: * @return
499: */
500: private PersonalContour makePacked() {
501: ArrayList newList = new ArrayList();
502: PersonalContourBucket previous = null;
503: PersonalContourBucket bucket = null;
504: // go thru each bucket
505: for (int i = 0; i < contourBuckets.length; i++) {
506: bucket = (PersonalContourBucket) contourBuckets[i];
507: if (bucket == null) {
508: System.out.println("null bucket " + i
509: + " in make packed - skipping");
510: continue;
511: }
512: if (previous != null
513: && previous.getUnits() == bucket.getUnits()
514: && previous.isFiller() == bucket.isFiller()) { // if matches previous bucket, make new bucket
515: previous = PersonalContourBucket.getInstance(previous
516: .getDuration()
517: + bucket.getDuration(), bucket.getUnits());
518: } else {
519: if (previous != null) {
520: if (previous.getDuration() > 0) {// ignore 0 length buckets
521: newList.add(previous); // doesnt match, so add it
522: }
523: }
524: previous = bucket;
525: }
526:
527: }
528: if (previous != null && previous.getDuration() > 0
529: && previous.getUnits() != 0.0) // ignore 0 length buckets or ones with 0 units at end
530: newList.add(previous); // add the last one - it will not have been added yet
531: if (newList.isEmpty())
532: newList.add(PersonalContourBucket.getInstance(0, 1.0)); // add empty bucket with no duration
533: return getInstance(newList);
534:
535: }
536:
537: public AbstractContour convertToFlatIfPossible() {
538: if (getContourBuckets().length > 1)
539: return this ;
540: else
541: return StandardContour.FLAT_CONTOUR;
542: }
543:
544: /**
545: * Set the units of the personal contour by multiplying each bucket's units by a multipier.
546: * Since buckets are immutable, each element is copied to a new one
547: * The multiplier is newRate / oldRate
548: * @param multiplier: The new value to set to.
549: */
550: // public AbstractContour adjustUnitsOld(double multiplier, long actualDuration) {
551: // PersonalContour newContour = constructUsingSizeOf(this);
552: // for (int i=0; i < contourBuckets.length; i++) {
553: // newContour.contourBuckets[i] = ((PersonalContourBucket)contourBuckets[i]).adjustUnits(multiplier);
554: // }
555: // newContour.maxUnits = newContour.calcMaxUnits();
556: // return newContour;
557: // }
558: public AbstractContour adjustUnits(double multiplier,
559: long startingFrom) {
560: ArrayList newList = new ArrayList();
561: newList.addAll(bucketsBeforeDuration(startingFrom));
562: ArrayList remainingBuckets = bucketsAfterDuration(startingFrom,
563: false);
564: for (int i = 0; i < remainingBuckets.size(); i++) {
565: newList.add(((PersonalContourBucket) remainingBuckets
566: .get(i)).adjustUnits(multiplier));
567: }
568: PersonalContour newContour = getInstance(newList).makePacked();
569: newContour.maxUnits = newContour.calcMaxUnits();
570: return newContour;
571: }
572:
573: /**
574: * Replace every element of the array with another that has its work adjusted
575: */
576: public AbstractContour contourAdjustWork(double multiplier,
577: long actualDuration) {
578: if (actualDuration == 0) {
579: PersonalContour newContour = constructUsingSizeOf(this );
580: for (int i = 0; i < contourBuckets.length; i++) {
581: newContour.contourBuckets[i] = ((PersonalContourBucket) contourBuckets[i])
582: .adjustWork(multiplier);
583: }
584: newContour.makePacked();
585: newContour.maxUnits = newContour.calcMaxUnits();
586: return newContour;
587: }
588:
589: ArrayList newList = bucketsBeforeDuration(actualDuration);
590: ArrayList after = bucketsAfterDuration(actualDuration, false);
591: for (int i = 0; i < after.size(); i++) {
592: newList.add(((PersonalContourBucket) after.get(i))
593: .adjustWork(multiplier));
594: }
595: AbstractContour result = getInstance(newList).makePacked();
596: result.maxUnits = result.calcMaxUnits();
597: return result;
598: }
599:
600: /**
601: * Make a personal contour from a standard one. This is used when the user explicity changes to a personal contour, or if he types
602: * in values in the spreadsheet.
603: * It essentially clones a contour to a personal contour.
604: * @param standard
605: * @param assignmentDuration
606: */
607: public PersonalContour(AbstractContour standard,
608: long assignmentDuration) {
609: super (new PersonalContourBucket[standard.numBuckets()]);
610: for (int i = 0; i < contourBuckets.length; i++)
611: contourBuckets[i] = PersonalContourBucket
612: .getInstance(standard.getContourBuckets()[i],
613: assignmentDuration);
614: maxUnits = calcMaxUnits();
615: }
616:
617: // public AbstractContour setValue(long assignmentStart, long setStart, long setEnd, long setDuration, double value) {
618: // LinkedList newList = new LinkedList();
619: //
620: // PersonalContourBucket bucket = null;
621: // long start = assignmentStart;
622: // long end;
623: // for (int i=0; i < contourBuckets.length; i++) {
624: // bucket = (PersonalContourBucket)contourBuckets[i];
625: //
626: // if (setStart <= start) {
627: // newList.add(new PersonalContourBucket(setDuration, value));
628: // setStart = Long.MAX_VALUE;
629: // end = setEnd;
630: // }
631: // if (start + bucket.getDuration())
632: //
633: // end = start + bucket.getDuration();
634: //
635: // }
636: //
637: // newContour.contourBuckets[i] = bucket;
638: // newDuration -= bucket.getDuration();
639: // if (newDuration <= 0) {
640: // newContour.contourBuckets[i] = bucket.adjustDuration(newDuration);// adding a negative value
641: // if (i < contourBuckets.length -1 ) // if shortening number of buckets
642: // System.arraycopy(newContour.contourBuckets,0,newContour.contourBuckets,0,i+1); // replace old with new and truncate to correct length
643: // return newContour;
644: // }
645: // }
646: // // extend last bucket to account for duration
647: // newContour.contourBuckets[contourBuckets.length] = bucket.adjustDuration(newDuration);
648: // return newContour;
649: // }
650:
651: /**
652: * Remove any starting empty buckets from the contour and return the duration of those buckets
653: * @return
654: */
655: public long extractDelay() {
656: long delay = 0;
657: if (contourBuckets.length < 2)
658: return 0;
659:
660: while (contourBuckets[0].getUnits() == 0) {
661: delay += contourBuckets[0].getBucketDuration(0);
662: AbstractContourBucket newBuckets[] = new AbstractContourBucket[contourBuckets.length - 1];
663: System.arraycopy(contourBuckets, 1, newBuckets, 0,
664: newBuckets.length);
665: contourBuckets = newBuckets;
666: }
667: return delay;
668: }
669:
670: /** Remove a blank bucket, if any, that starts or occurs at a duration.
671: * This is used when updating progress to assure that a task resumes on a certain date
672: * @param atDuration
673: * @return
674: */
675: public AbstractContour removeEmptyBucketAtDuration(long atDuration) {
676: ArrayList afterList = bucketsAfterDuration(atDuration, false);
677: // if nothing after or starts with a non null bucket
678: if (afterList.isEmpty()
679: || ((AbstractContourBucket) afterList.get(0))
680: .getUnits() != 0)
681: return this ;
682: afterList.remove(0); // remove blank bucket
683: ArrayList newList = bucketsBeforeDuration(atDuration);
684: newList.addAll(afterList); // exclude filler
685: return getInstance(newList).makePacked();
686:
687: }
688:
689: public Object clone() {
690: return super.clone();
691: }
692: }
|