001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.lib.util;
028:
029: import java.util.Calendar;
030: import java.util.Date;
031: import java.util.Enumeration;
032: import java.util.HashMap;
033: import java.util.Map;
034: import java.util.Vector;
035:
036: import org.cougaar.planning.ldm.PlanningFactory;
037: import org.cougaar.planning.ldm.plan.AllocationResult;
038: import org.cougaar.planning.ldm.plan.AspectScorePoint;
039: import org.cougaar.planning.ldm.plan.AspectScoreRange;
040: import org.cougaar.planning.ldm.plan.AspectType;
041: import org.cougaar.planning.ldm.plan.AspectValue;
042: import org.cougaar.planning.ldm.plan.NewTask;
043: import org.cougaar.planning.ldm.plan.PlanElement;
044: import org.cougaar.planning.ldm.plan.Preference;
045: import org.cougaar.planning.ldm.plan.ScoringFunction;
046: import org.cougaar.planning.ldm.plan.Task;
047: import org.cougaar.planning.ldm.plan.TimeAspectValue;
048: import org.cougaar.util.LRUCache;
049: import org.cougaar.util.log.Logger;
050:
051: /**
052: * This class contains preference-related methods.
053: * Can ask basic questions like ready at, early, best, and latest date for
054: * tasks.
055: *
056: * Can also create an various kinds of preferences.
057: */
058:
059: public class UTILPreference {
060: private static String myName = "UTILPreference";
061: private static double ONE_DAY = 1000 * 60 * 60 * 24; // millis
062: private static double ONE_OVER_ONE_DAY = 1.0d / ONE_DAY; // millis
063: private static double fiftyYears = ONE_DAY * 365 * 50; // millis
064: private static double boundaryDefaultScore = 0.75;
065:
066: // 0 = LRU
067: // 1 = hashmap
068: private static final int CACHE_STYLE_DEFAULT = 0;
069: private static final int CACHE_STYLE = Integer.getInteger(
070: "org.cougaar.lib.util.UTILPreference.cacheStyle",
071: CACHE_STYLE_DEFAULT).intValue();
072:
073: private static final int CACHE_SIZE_DEFAULT = 256;
074: private static final int CACHE_SIZE = Integer.getInteger(
075: "org.cougaar.lib.util.UTILPreference.cacheSize",
076: CACHE_SIZE_DEFAULT).intValue();
077:
078: protected Map startDateCache;
079: protected Map endDateCache;
080:
081: public double NO_ASPECT_VALUE = -1.0d;
082:
083: public UTILPreference(Logger l) {
084: logger = l;
085: if (CACHE_STYLE == 1) {
086: startDateCache = new HashMap(CACHE_SIZE);
087: endDateCache = new HashMap(CACHE_SIZE);
088: } else {
089: startDateCache = new LRUCache(CACHE_SIZE);
090: endDateCache = new LRUCache(CACHE_SIZE);
091: }
092: }
093:
094: /**
095: * Utility methods
096: */
097: public void replacePreference(NewTask t, Preference new_pref) {
098: // Remove the preference from the current list of
099: // preferences for this task
100: Enumeration prefs_en;
101: synchronized (t) {
102: prefs_en = t.getPreferences();
103: } // bug #2125
104: Vector this _task_prefs = UTILAllocate.enumToVector(prefs_en);
105:
106: t.setPreferences((replacePreference(this _task_prefs, new_pref))
107: .elements());
108: }
109:
110: public Vector replacePreference(Vector old_prefs,
111: Preference new_pref) {
112: Vector new_prefs = new Vector(old_prefs.size());
113:
114: Enumeration prefs_e = old_prefs.elements();
115: while (prefs_e.hasMoreElements()) {
116: Preference old_pref = (Preference) prefs_e.nextElement();
117: if (old_pref.getAspectType() != new_pref.getAspectType())
118: new_prefs.addElement(old_pref);
119: }
120:
121: // and then add the new one
122: new_prefs.addElement(new_pref);
123:
124: return new_prefs;
125: }
126:
127: /**
128: * This call generates a cost preference. Only using a single value.
129: * Should probably be more complex than this.
130: *
131: * @param ldmf the PlanningFactory
132: * @param cost the single cost value
133: * @return Preference
134: */
135:
136: public Preference makeCostPreference(PlanningFactory ldmf,
137: double cost) {
138: AspectValue lowAV = AspectValue.newAspectValue(AspectType.COST,
139: -0.01d);
140: AspectValue bestAV = AspectValue.newAspectValue(
141: AspectType.COST, 0.0d);
142: AspectValue costAV = AspectValue.newAspectValue(
143: AspectType.COST, cost);
144: ScoringFunction costSF = ScoringFunction
145: .createVScoringFunction(lowAV, bestAV, costAV, 1.0);
146: Preference costPref = ldmf.newPreference(AspectType.COST,
147: costSF);
148: return costPref;
149: }
150:
151: /**
152: * Generates a quantity preference.
153: *
154: * What should we do with the weight of the preference?
155: * Should this be set by a policy object?
156: *
157: * @param quantity desired
158: * @return Preference
159: */
160: public Preference makeQuantityPreference(PlanningFactory ldmf,
161: long quantity) {
162: AspectValue quantityAV = AspectValue.newAspectValue(
163: AspectType.QUANTITY, quantity);
164: ScoringFunction quantitySF = ScoringFunction.createNearOrAbove(
165: quantityAV, 0.0d);
166: Preference quantityPref = ldmf.newPreference(
167: AspectType.QUANTITY, quantitySF, 1.0);
168: return quantityPref;
169: }
170:
171: /**
172: * Generates a preference from 1 date.
173: *
174: * What should we do with the weight of the preference?
175: * Should this be set by a policy object?
176: *
177: * Uses the startDateCache so only distinct instances are created.
178: *
179: * @param readyAtDate - the date item is ready to move
180: * @return Preference
181: */
182: public Preference makeStartDatePreference(PlanningFactory ldmf,
183: Date readyAtDate) {
184: Preference startPref;
185: Long key = new Long(readyAtDate.getTime());
186: if ((startPref = (Preference) startDateCache.get(key)) == null) {
187: AspectValue readyAtAV = AspectValue.newAspectValue(
188: AspectType.START_TIME, readyAtDate.getTime());
189: ScoringFunction startSF = ScoringFunction
190: .createNearOrAbove(readyAtAV, 0.0d);
191: startPref = ldmf.newPreference(AspectType.START_TIME,
192: startSF, 1.0);
193: startDateCache.put(key, startPref);
194: }
195:
196: return startPref;
197: }
198:
199: /**
200: * Generates a preference from 3 dates.
201: *
202: * What should we do with the weight of the preference?
203: * Should this be set by a policy object?
204: *
205: * What should be the relative scores of early and late?
206: *
207: * Uses the endDateCache so only distinct instances are created.
208: *
209: * @param earlyDate - the earliest possible date
210: * @param bestDate - the best date
211: * @param lateDate - the latest possible date
212: * @return Preference
213: */
214: public Preference makeEndDatePreference(PlanningFactory ldmf,
215: Date earlyDate, Date bestDate, Date lateDate) {
216: Preference endPref;
217: Object key = earlyDate.getTime() + "-" + bestDate.getTime()
218: + "-" + lateDate.getTime();
219: if ((endPref = (Preference) endDateCache.get(key)) == null) {
220: endPref = ldmf.newPreference(AspectType.END_TIME,
221: new UTILEndDateScoringFunction(earlyDate, bestDate,
222: lateDate, boundaryDefaultScore));
223: endDateCache.put(key, endPref);
224: }
225:
226: return endPref;
227: }
228:
229: /**
230: * When you don't know early or latest, only best time for the task.
231: *
232: * What should we do with the weight of the preference?
233: * Should this be set by a policy object?
234: *
235: * Note that it uses one day as the slope -- i.e.
236: * a day after the bestDate, the pref is exceeded.
237: *
238: * @param bestDate - the best date
239: * @return Preference
240: */
241: public Preference makeEndDatePreference(PlanningFactory ldmf,
242: Date bestDate) {
243: AspectValue endAV = AspectValue.newAspectValue(
244: AspectType.END_TIME, bestDate.getTime());
245: ScoringFunction endSF = ScoringFunction.createPreferredAtValue(
246: endAV, ONE_OVER_ONE_DAY);
247: Preference endPref = ldmf.newPreference(AspectType.END_TIME,
248: endSF, 1.0);
249: return endPref;
250: }
251:
252: public Preference makeEndDateBelowPreference(PlanningFactory ldmf,
253: Date bestDate) {
254: AspectValue endAV = AspectValue.newAspectValue(
255: AspectType.END_TIME, bestDate.getTime());
256: ScoringFunction endSF = ScoringFunction.createNearOrBelow(
257: endAV, 0.0);
258: Preference endPref = ldmf.newPreference(AspectType.END_TIME,
259: endSF, 1.0);
260: return endPref;
261: }
262:
263: /**
264: * What should we do with the weight of the preference?
265: * Should this be set by a policy object?
266: *
267: * Note that it uses one day as the slope -- i.e.
268: * a day after the POD date, the pref is exceeded.
269: */
270: public Preference makePODDatePreference(PlanningFactory ldmf,
271: Date bestDate) {
272: if (bestDate == null || bestDate.before(new Date(1000))) {
273: logger
274: .error("UTILPreference creating bad POD_Date preference: the date is "
275: + bestDate);
276: }
277: AspectValue podAV = AspectValue.newAspectValue(
278: AspectType.POD_DATE, bestDate.getTime());
279: ScoringFunction podSF = ScoringFunction.createPreferredAtValue(
280: podAV, ONE_OVER_ONE_DAY);
281: Preference podPref = ldmf.newPreference(AspectType.POD_DATE,
282: podSF, 1.0);
283: return podPref;
284: }
285:
286: /**
287: * @return true if task has specified aspect type
288: */
289: public boolean hasPrefWithAspectType(Task taskToExamine,
290: int aspectType) {
291: return (getPrefWithAspectType(taskToExamine, aspectType) != null);
292: }
293:
294: /**
295: * Utility function to get preference from a task based on its aspect type
296: *
297: * @param taskToExamine task to examine for matching preference
298: * @param aspect_type aspect type you're hoping to find on the task's preference list
299: * @return Preference first preference found (earliest in pref enum) with the
300: * specified aspect type, null if none found
301: */
302: public Preference getPrefWithAspectType(Task taskToExamine,
303: int aspect_type) {
304: Enumeration pref_en;
305: synchronized (taskToExamine) {
306: pref_en = taskToExamine.getPreferences();
307: } // bug #2125
308: while (pref_en.hasMoreElements()) {
309: Preference return_pref = (Preference) pref_en.nextElement();
310: if (return_pref.getAspectType() == aspect_type)
311: return return_pref;
312: }
313:
314: return null;
315: }
316:
317: /**
318: * Utility function to remove pref from a task based on its aspect type
319: *
320: * @param taskToChange task to examine for matching preference
321: * @param aspect_type aspect type you're hoping to find on the task's pref list
322: * @return Preference last preference found (latest in pref enum) with the
323: * specified aspect type, null if none found
324: * NOTE that if more than one pref with this aspect type is found, ALL
325: * are removed but only the latest is returned.
326: */
327: public Preference removePrefWithAspectType(Task taskToChange,
328: int aspect_type) {
329: Vector old_prefs;
330: synchronized (taskToChange) { // bug #2125
331: old_prefs = UTILAllocate.enumToVector(taskToChange
332: .getPreferences());
333: }
334:
335: Vector new_prefs = new Vector(old_prefs.size());
336: Preference pref_to_return = null;
337:
338: Enumeration prefs_e = old_prefs.elements();
339: while (prefs_e.hasMoreElements()) {
340: Preference old_pref = (Preference) prefs_e.nextElement();
341: if (old_pref.getAspectType() != aspect_type)
342: new_prefs.addElement(old_pref);
343: else
344: pref_to_return = old_pref;
345: }
346:
347: ((NewTask) taskToChange).setPreferences(new_prefs.elements());
348:
349: return pref_to_return;
350: }
351:
352: /**
353: * Given a Preference get its best aspect value
354: * @param pref the preference to examine
355: * @return double best aspect value for the preference
356: */
357: public double getPreferenceBestValue(Preference pref) {
358: ScoringFunction sfunc = pref.getScoringFunction();
359: return sfunc.getBest().getAspectValue().getValue();
360: }
361:
362: /**
363: * Given a Preference get its best aspect value
364: * @param pref the preference to examine
365: * @return double best aspect value for the preference
366: */
367: public AspectValue getPreferenceBestAspectValue(Preference pref) {
368: ScoringFunction sfunc = pref.getScoringFunction();
369: return sfunc.getBest().getAspectValue();
370: }
371:
372: /**
373: * Examine plan element for a reported aspect value
374: *
375: * Many times easier to use type specific variants -- like getReportedReadyAt
376: */
377: public double getReportedAspectValue(PlanElement pe, int aspectType) {
378: AllocationResult result = pe.getReportedResult();
379: if (result == null)
380: return NO_ASPECT_VALUE;
381: return result.getValue(aspectType);
382: }
383:
384: /**
385: * get the Cost preference of a task.
386: *
387: * @param t the Task object
388: * @return date at which task is ready
389: */
390:
391: public double getCost(Task t) {
392: try {
393: Preference pref = getPrefWithAspectType(t, AspectType.COST);
394: ScoringFunction sfunc = pref.getScoringFunction();
395: return sfunc.getBest().getAspectValue().getValue();
396: } catch (NullPointerException npe) {
397: throw new UTILRuntimeException(classname
398: + ".getCost () : task\n\t" + t.getUID()
399: + "\n\thas no COST preference?");
400: }
401: }
402:
403: /**
404: * Get reported cost from plan element
405: */
406: public double getReportedCost(PlanElement pe) {
407: return getReportedAspectValue(pe, AspectType.COST);
408: }
409:
410: /**
411: * get the Quantity preference of a task.
412: *
413: * @param t the Task object
414: * @return date at which task is ready
415: */
416:
417: public long getQuantity(Task t) {
418: try {
419: Preference pref = getPrefWithAspectType(t,
420: AspectType.QUANTITY);
421: ScoringFunction sfunc = pref.getScoringFunction();
422: return sfunc.getBest().getAspectValue().longValue();
423: } catch (NullPointerException npe) {
424: throw new UTILRuntimeException(classname
425: + ".getQuantity () : task\n\t" + t.getUID()
426: + "\n\thas no QUANTITY preference?");
427: }
428: }
429:
430: /**
431: * Get reported quantity from plan element
432: */
433: public long getReportedQuantity(PlanElement pe) {
434: return (long) getReportedAspectValue(pe, AspectType.QUANTITY);
435: }
436:
437: /**
438: * Returns the READYAT Date from task object, or new Date if READYAT
439: * date is null
440: *
441: * @param t the Task object
442: * @return date at which task is ready
443: */
444:
445: public Date getReadyAt(Task t) {
446: try {
447: Preference pref = getPrefWithAspectType(t,
448: AspectType.START_TIME);
449: ScoringFunction sfunc = pref.getScoringFunction();
450: return new Date(sfunc.getBest().getAspectValue()
451: .longValue());
452: } catch (NullPointerException npe) {
453: throw new UTILRuntimeException(classname
454: + ".getReadyAt () : task\n\t" + t
455: + "\n\thas no START_TIME preference?");
456: }
457: }
458:
459: /**
460: * Get reported ready at date from plan element
461: */
462: public Date getReportedReadyAt(PlanElement pe) {
463: double value = getReportedAspectValue(pe, AspectType.START_TIME);
464: if (value != NO_ASPECT_VALUE)
465: return new Date((long) value);
466: return null;
467: }
468:
469: /**
470: * Returns the POD Date from task object, null if POD date not a pref on this task
471: *
472: * @param t the Task with the pref
473: * @return point of departure date for task, null if no POD date pref
474: */
475:
476: public Date getPODDate(Task t) {
477: Preference pod_pref = getPrefWithAspectType(t,
478: AspectType.POD_DATE);
479: if (pod_pref == null)
480: return null;
481:
482: Date pod_date = new Date((long) (pod_pref.getScoringFunction()
483: .getBest().getValue()));
484:
485: return pod_date;
486: }
487:
488: /**
489: * Get reported POD date from plan element
490: */
491: public Date getReportedPODDate(PlanElement pe) {
492: return getReportedPODDate(pe.getReportedResult());
493: }
494:
495: /**
496: * Get reported POD date from allocation result
497: */
498: public Date getReportedPODDate(AllocationResult result) {
499: double value = result.getValue(AspectType.POD_DATE);
500: if (value != NO_ASPECT_VALUE)
501: return new Date((long) value);
502: return null;
503: }
504:
505: /**
506: * Returns the desired earliest arrival date from task object or null if
507: * that date is null.
508: *
509: * Should work with both TOPSEndDates and end dates generated from outside of TOPS.
510: *
511: * If not an TOPSEndDate, then looks at the valid ranges for the scoring function,
512: * gets the first one, and returns its start point.
513: *
514: * @param t - the Task object
515: * @return Date
516: */
517: public Date getEarlyDate(Task t) {
518: UTILEndDateScoringFunction edsf = getEndDateSF(t);
519: if (edsf == null) {
520: Enumeration validRanges = getValidEndDateRanges(t);
521: for (; validRanges.hasMoreElements();) {
522: AspectScoreRange range = (AspectScoreRange) validRanges
523: .nextElement();
524: return new Date(((AspectScorePoint) range
525: .getRangeStartPoint()).getAspectValue()
526: .longValue());
527: }
528: }
529: return edsf.getEarlyDate();
530: }
531:
532: /**
533: * Get reported End date from plan element
534: */
535: public Date getReportedEndDate(PlanElement pe) {
536: return getReportedEndDate(pe.getReportedResult());
537: }
538:
539: /**
540: * Get reported End date from allocation result
541: */
542: public Date getReportedEndDate(AllocationResult ar) {
543: double value = ar.getValue(AspectType.END_TIME);
544: if (value != NO_ASPECT_VALUE)
545: return new Date((long) value);
546: return null;
547: }
548:
549: /**
550: * Returns the desired best arrival date from task object or null if
551: * that date is null.
552: *
553: * Should work with both TOPSEndDates and end dates generated from outside of TOPS.
554: *
555: * @param t - the Task object
556: * @return Date
557: */
558: public Date getBestDate(Task t) {
559: UTILEndDateScoringFunction edsf = getEndDateSF(t);
560: if (edsf == null) {
561: Preference endDatePref = getPrefWithAspectType(t,
562: AspectType.END_TIME);
563: return new Date((long) getPreferenceBestValue(endDatePref));
564: }
565: return edsf.getBestDate();
566: }
567:
568: /**
569: * Returns the desired latest arrival date from task object or null if
570: * that date is null.
571: *
572: * Should work with both TOPSEndDates and end dates generated from outside of TOPS.
573: *
574: * If not an TOPSEndDate, then looks at the valid ranges for the scoring function,
575: * gets the last one, and returns its end point.
576: *
577: * @param t - the Task object
578: * @return Date
579: */
580: public Date getLateDate(Task t) {
581: UTILEndDateScoringFunction edsf = getEndDateSF(t);
582: if (edsf == null) {
583: Enumeration validRanges = getValidEndDateRanges(t);
584: for (; validRanges.hasMoreElements();) {
585: AspectScoreRange range = (AspectScoreRange) validRanges
586: .nextElement();
587:
588: if (!validRanges.hasMoreElements())
589: return new Date(((AspectScorePoint) range
590: .getRangeEndPoint()).getAspectValue()
591: .longValue());
592: }
593: }
594: return edsf.getLateDate();
595: }
596:
597: protected Enumeration getValidEndDateRanges(Task t) {
598: Preference endDatePref = getPrefWithAspectType(t,
599: AspectType.END_TIME);
600: return getValidEndDateRanges(endDatePref);
601: }
602:
603: protected Enumeration getValidEndDateRanges(Preference endDatePref) {
604: Calendar cal = java.util.Calendar.getInstance();
605: cal.set(2200, 0, 0, 0, 0, 0);
606: cal.set(Calendar.MILLISECOND, 0);
607: Date endOfRange = (Date) cal.getTime();
608:
609: Enumeration validRanges = endDatePref
610: .getScoringFunction()
611: .getValidRanges(
612: TimeAspectValue.create(AspectType.END_TIME, 0l),
613: TimeAspectValue.create(AspectType.END_TIME,
614: endOfRange));
615: return validRanges;
616: }
617:
618: protected UTILEndDateScoringFunction getEndDateSF(Task t) {
619: try {
620: Preference endDatePref = getPrefWithAspectType(t,
621: AspectType.END_TIME);
622: ScoringFunction func = endDatePref.getScoringFunction();
623: if (!(func instanceof UTILEndDateScoringFunction))
624: return null;
625: return (UTILEndDateScoringFunction) endDatePref
626: .getScoringFunction();
627: } catch (NullPointerException npe) {
628: throw new UTILRuntimeException(
629: classname
630: + ".getBestDate () : \n\ttask\n\t"
631: + t
632: + "\n\thas no END_TIME preference to ask for best date.");
633: }
634: }
635:
636: private static final String classname = UTILPreference.class
637: .getName();
638: protected Logger logger;
639: // protected UTILAllocate alloc;
640: }
|