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.planning.plugin.util;
028:
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Enumeration;
032: import java.util.Iterator;
033: import java.util.Set;
034: import java.util.Vector;
035:
036: import org.cougaar.core.blackboard.AnonymousChangeReport;
037: import org.cougaar.core.blackboard.IncrementalSubscription;
038: import org.cougaar.core.service.BlackboardService;
039: import org.cougaar.planning.ldm.PlanningFactory;
040: import org.cougaar.planning.ldm.plan.Aggregation;
041: import org.cougaar.planning.ldm.plan.AllocationResult;
042: import org.cougaar.planning.ldm.plan.AspectType;
043: import org.cougaar.planning.ldm.plan.AspectValue;
044: import org.cougaar.planning.ldm.plan.Context;
045: import org.cougaar.planning.ldm.plan.Expansion;
046: import org.cougaar.planning.ldm.plan.NewComposition;
047: import org.cougaar.planning.ldm.plan.NewMPTask;
048: import org.cougaar.planning.ldm.plan.NewTask;
049: import org.cougaar.planning.ldm.plan.NewWorkflow;
050: import org.cougaar.planning.ldm.plan.PlanElement;
051: import org.cougaar.planning.ldm.plan.PlanElementImpl;
052: import org.cougaar.planning.ldm.plan.Preference;
053: import org.cougaar.planning.ldm.plan.Task;
054: import org.cougaar.util.Enumerator;
055: import org.cougaar.util.log.Logger;
056: import org.cougaar.util.log.Logging;
057:
058: /**
059: * Container for various static helper methods used by Plugins
060: * to manipulate Plan objects.
061: */
062: public class PluginHelper {
063:
064: /**
065: * Returns an AllocationResult based on the preferences of Task <t>
066: * and specified confidence rating <confrating> and success <success>.
067: * Results are estimated to be the "best" possible based on the
068: * preference scoring function. AllocationResult is null if
069: * <t> has no preferences.
070: */
071: //force everyone to specify the confidence rating and success
072: public static AllocationResult createEstimatedAllocationResult(
073: Task t, PlanningFactory ldmf, double confrating,
074: boolean success) {
075: return new AllocationResultHelper(t, null).getAllocationResult(
076: confrating, success);
077: }
078:
079: /**
080: * updatePlanElement looks for differences between the reported and
081: * estimated allocation results. If they are not equal (== for now)
082: * then the estimated value is set to the reported
083: * value. Return true if <pe> has been changed, false otherwise.
084: */
085: public static boolean updatePlanElement(PlanElement pe) {
086: AllocationResult repar = pe.getReportedResult();
087: if (repar != null) {
088: //compare the result objects.
089: // If they are NOT equal, re-set the estimated result, return true.
090: AllocationResult estar = pe.getEstimatedResult();
091: //eventually change second comparison from == to isEqual ?
092: if (!repar.isEqual(estar)) {
093: pe.setEstimatedResult(repar);
094: return true;
095: }
096: }
097: return false;
098: }
099:
100: /**
101: * For each PlanElement of <sub> which has changed, if the
102: * estimated and reported results are different, change
103: * estimated to reported and publish the PlanElement change.
104: */
105: public static void updateAllocationResult(
106: IncrementalSubscription sub) {
107: Enumeration changedPEs = sub.getChangedList();
108: while (changedPEs.hasMoreElements()) {
109: PlanElement pe = (PlanElement) changedPEs.nextElement();
110: if (checkChangeReports(sub.getChangeReports(pe),
111: PlanElement.ReportedResultChangeReport.class)) {
112: if (updatePlanElement(pe)) {
113: sub.getSubscriber().publishChange(pe);
114: }
115: }
116: }
117: }
118:
119: /**
120: * Check if a List of ChangeReports has an instance of a given class
121: **/
122: public static boolean checkChangeReports(Set reports, Class cls) {
123: if (reports == AnonymousChangeReport.SET)
124: return false;
125: if (reports == null)
126: return false; // null-check shouldn't be needed
127: for (Iterator i = reports.iterator(); i.hasNext();) {
128: if (cls.isInstance(i.next()))
129: return true;
130: }
131: return false;
132: }
133:
134: //4 different wireExpansion methods
135: //2 for single subtask, 2 for vector of subtasks
136: //2 with null estimate allocation result, 2 with specified estimated allocation result
137:
138: /**
139: * Returns an expansion based on <parent> and <subTask>,
140: * with appropriate relations set. Specifically,
141: * puts the subtask in a NewWorkflow, sets the Workflow's
142: * parent task to <parent> and sets the subtask Workflow.
143: * Sets the subtask to be removed if the Workflow is removed.
144: * If <subTask> has no context, sets it to that of <parent>
145: * Uses a null estimated AllocationResult for the expansion.
146: */
147: public static Expansion wireExpansion(Task parent, NewTask subTask,
148: PlanningFactory ldmf) {
149: //use a null estimated allocation result
150: return wireExpansion(parent, subTask, ldmf, null);
151: }
152:
153: /**
154: * Same as wireExpansion(Task, NewTask, PlanningFactory) but uses the
155: * specified AllocationResult for the expansion.
156: */
157: public static Expansion wireExpansion(Task parent, NewTask subTask,
158: PlanningFactory ldmf, AllocationResult ar) {
159:
160: NewWorkflow wf = ldmf.newWorkflow();
161:
162: wf.setParentTask(parent);
163: subTask.setWorkflow(wf);
164: subTask.setParentTask(parent);
165: wf.addTask(subTask);
166:
167: // Set the Context of the subTask to be that of the parent, unless it
168: // has already been set
169: if (subTask.getContext() == null) {
170: subTask.setContext(parent.getContext());
171: }
172:
173: //End of creating NewWorkflow. Start creating an Expansion.
174: Expansion exp = ldmf.createExpansion(parent.getPlan(), parent,
175: wf, ar);
176:
177: return exp;
178: }
179:
180: /**
181: * Wire a new subtask into an existing expansion
182: **/
183: public static void wireExpansion(Expansion exp, NewTask subTask) {
184: Task parent = exp.getTask();
185: NewWorkflow wf = (NewWorkflow) exp.getWorkflow();
186: subTask.setParentTask(parent);
187: subTask.setWorkflow(wf);
188: wf.addTask(subTask);
189:
190: // Set the Context of the subTask to be that of the parent,
191: // unless it has already been set
192: if (subTask.getContext() == null) {
193: subTask.setContext(parent.getContext());
194: }
195: }
196:
197: /**
198: * Same as wireExpansion(Task, NewTask, PlanningFactory) except that a Vector
199: * of subtasks is used. All the subtasks in the Vector are added to the
200: * Workflow.
201: */
202: public static Expansion wireExpansion(Task parentTask,
203: Vector subTasks, PlanningFactory ldmf) {
204: return wireExpansion(parentTask, subTasks, ldmf, null);
205: }
206:
207: /**
208: * Same as wireExpansion(Task, Vector, PlanningFactory) except uses
209: * the specified AllocationResult
210: */
211: public static Expansion wireExpansion(Task parentTask,
212: Vector subTasks, PlanningFactory ldmf, AllocationResult ar) {
213: NewWorkflow wf = ldmf.newWorkflow();
214:
215: wf.setParentTask(parentTask);
216:
217: Context context = parentTask.getContext();
218: for (Enumeration esubTasks = subTasks.elements(); esubTasks
219: .hasMoreElements();) {
220: NewTask myTask = (NewTask) esubTasks.nextElement();
221: myTask.setWorkflow(wf);
222: myTask.setParentTask(parentTask);
223: wf.addTask(myTask);
224: // Set the Context of the subtask if it hasn't already been set
225: if (myTask.getContext() == null) {
226: myTask.setContext(context);
227: }
228: }
229:
230: return ldmf.createExpansion(parentTask.getPlan(), parentTask,
231: wf, ar);
232: }
233:
234: /**
235: * Returns a NewTask based on <task>. The NewTask
236: * has identical Verb, DirectObject, Plan, Preferences,
237: * Context, and PrepositionalPhrases as <task>.
238: */
239: public static NewTask makeSubtask(Task task, PlanningFactory ldmf) {
240:
241: NewTask subtask = ldmf.newTask();
242:
243: // Create copy of parent Task
244: subtask.setParentTask(task);
245: subtask.setDirectObject(task.getDirectObject());
246: subtask.setPrepositionalPhrases(task.getPrepositionalPhrases());
247: subtask.setVerb(task.getVerb());
248: subtask.setPlan(task.getPlan());
249: subtask.setPreferences(task.getPreferences());
250: subtask.setContext(task.getContext());
251:
252: return subtask;
253: }
254:
255: /** Publish a new Expansion and its subtasks **/
256: public static void publishAddExpansion(BlackboardService sub,
257: Expansion exp) {
258: sub.publishAdd(exp);
259:
260: for (Enumeration esubTasks = exp.getWorkflow().getTasks(); esubTasks
261: .hasMoreElements();) {
262: Task myTask = (Task) esubTasks.nextElement();
263: sub.publishAdd(myTask);
264: }
265: }
266:
267: /**
268: * Helper to remove a task from an Expansion.
269: * Removes the task from its workflow if not already done.
270: * When doing so, also recalculate the received result on the Expansion, and publishChange
271: * the Expansion if the result is now different. This permits the Expander Plugin to
272: * copy the new result up the chain.
273: * Note that normally the ReceivedResult would be updated by the LPs when one of the other sub-tasks
274: * got a new AllocationResult. But that may not happen soon enough, or may never happen.
275: * Note that the Plugin is responsible for publishRemoving the Task or re-parenting, as desired.
276: * @param sub BlackboardService through which to do publishChange
277: * @param task sub-task being removed
278: **/
279: public static void removeSubTask(BlackboardService sub, Task task) {
280: // First, remove the task from its workflow, if not already done
281: NewWorkflow wf = (NewWorkflow) task.getWorkflow();
282: if (wf != null) {
283: for (Enumeration tasks = wf.getTasks(); tasks
284: .hasMoreElements();) {
285: if (tasks.nextElement() == task) {
286: wf.removeTask(task);
287: break;
288: }
289: }
290:
291: // The workflow now has 1 fewer tasks, so the AR aggregation will usually be different.
292: AllocationResult newRcvAR = wf.aggregateAllocationResults();
293:
294: // From this task's workflow, get the parent task's PlanElement - the Expansion
295: // See ubug 13542. Maybe this could happen if GLS was
296: // being rescinded, for example?
297: Task pTask = wf.getParentTask();
298: if (pTask == null) {
299: // This is bizarre. Log and bail
300: Logger logger = Logging.getLogger(PluginHelper.class);
301: logger.error(
302: "PluginHelper.removeSubTask: Null parent task from workflow "
303: + wf + " for task " + task,
304: new Throwable());
305: return;
306: }
307:
308: PlanElement pe = pTask.getPlanElement();
309: if (pe == null) {
310: // This is bizarre. Log and bail
311: Logger logger = Logging.getLogger(PluginHelper.class);
312: logger.error(
313: "PluginHelper.removeSubTask: Null PlanElement from parent task "
314: + pTask + " found from workflow " + wf
315: + " for task " + task, new Throwable());
316: return;
317: }
318:
319: // Sanity check that pe.getTask == pTask?
320: AllocationResult currRcvAR = pe.getReceivedResult();
321:
322: // If the newly aggregated AR is different, then change it and publishChange the expansion
323: if ((newRcvAR == null && currRcvAR != null)
324: || (newRcvAR != null && !newRcvAR
325: .isEqual(currRcvAR))) {
326: ((PlanElementImpl) pe).setReceivedResult(newRcvAR);
327: sub.publishChange(pe); // PEImpl puts a ReportedResultChangeReport on this transaction
328: }
329: // Caller should publishRemove the task or re-parent it as desired.
330: } // check if wf exists
331: // else if Task had no workflow, nothing to do
332: }
333:
334: // 2 wireaggregation methods -- one for a single parent and one for a
335: // Collection of parents
336:
337: /**
338: * Connect a parent task to an MPTask. If the MPTask does not have
339: * a Composition one is created for it. The MPTask may already
340: * have other Aggregations.
341: * @return the Aggregation created. The caller is responsible for publishing
342: * the new Aggregation.
343: **/
344: public static Aggregation wireAggregation(Task parent,
345: NewMPTask mpTask, PlanningFactory ldmf, AllocationResult ar) {
346: NewComposition composition = (NewComposition) mpTask
347: .getComposition();
348: if (composition == null) {
349: composition = ldmf.newComposition();
350: composition.setCombinedTask(mpTask);
351: mpTask.setComposition(composition);
352: }
353: Aggregation agg = ldmf.createAggregation(parent.getPlan(),
354: parent, composition, ar);
355: composition.addAggregation(agg);
356: mpTask.setParentTasks(new Enumerator(composition
357: .getParentTasks()));
358: return agg;
359: }
360:
361: /**
362: * Connect a Collection of parent tasks to an MPTask. If the
363: * MPTask does not have a Composition one is created for it. The
364: * MPTask may already have other Aggregations. An estimated
365: * AllocationResult is created for all Aggregations having a
366: * confidence rating and success flag as specified by the
367: * arguments.
368: * @param parents the parents to be wired to the MPTask
369: * @param mpTask the MPTask of the aggregation
370: * @param ldmf the factory
371: * @param confrating the confidence rating of all the created Aggregations
372: * @param success the "success" flag for all the created Aggregations
373: * @return a Collection of the Aggregations created. These have _not_ been
374: * published. The caller is responsible for publishing them.
375: **/
376: public static Collection wireAggregation(Collection parents,
377: NewMPTask mpTask, PlanningFactory ldmf, double confrating,
378: boolean success) {
379: NewComposition composition = (NewComposition) mpTask
380: .getComposition();
381: if (composition == null) {
382: composition = ldmf.newComposition();
383: composition.setCombinedTask(mpTask);
384: mpTask.setComposition(composition);
385: }
386: ArrayList result = new ArrayList(parents.size());
387: for (Iterator i = parents.iterator(); i.hasNext();) {
388: Task parent = (Task) i.next();
389: AllocationResult ar = createEstimatedAllocationResult(
390: parent, ldmf, confrating, success);
391: Aggregation agg = ldmf.createAggregation(parent.getPlan(),
392: parent, composition, ar);
393: composition.addAggregation(agg);
394: result.add(agg);
395: }
396: mpTask.setParentTasks(new Enumerator(composition
397: .getParentTasks()));
398: return result;
399: }
400:
401: // TASK PREFERENCE UTILS (taken from glm/.../TaskUtils
402: public static long getStartTime(Task task) {
403: double startTime = getPreferenceBestValue(task,
404: AspectType.START_TIME);
405: if (Double.isNaN(startTime)) {
406: throw new IllegalArgumentException(
407: "Task has no START_TIME preference");
408: }
409: return (long) startTime;
410: }
411:
412: public static long getEndTime(Task task) {
413: double endTime = getPreferenceBestValue(task,
414: AspectType.END_TIME);
415: if (Double.isNaN(endTime)) {
416: throw new IllegalArgumentException(
417: "Task has no END_TIME preference");
418: }
419: return (long) endTime;
420: }
421:
422: public static AspectValue getPreferenceBest(Task task,
423: int aspect_type) {
424: if (task == null)
425: throw new IllegalArgumentException("task cannot be null");
426: Preference task_pref = task.getPreference(aspect_type);
427: if (task_pref == null) {
428: return null;
429: }
430: if (task_pref.getScoringFunction() == null) {
431: return null;
432: }
433: return task_pref.getScoringFunction().getBest()
434: .getAspectValue();
435: }
436:
437: public static double getPreferenceBestValue(Task task,
438: int aspect_type) {
439: AspectValue best = getPreferenceBest(task, aspect_type);
440: if (best == null)
441: return Double.NaN;
442: return best.getValue();
443: }
444:
445: // AllocationResult utils (taken from glm/.../TaskUtils
446:
447: public static double getStartTime(AllocationResult ar) {
448: return getARAspectValue(ar, AspectType.START_TIME);
449: }
450:
451: public static double getEndTime(AllocationResult ar) {
452: return getARAspectValue(ar, AspectType.END_TIME);
453: }
454:
455: public static double getARAspectValue(AllocationResult ar, int type) {
456: if (ar == null)
457: return Double.NaN;
458: AspectValue[] avs = ar.getAspectValueResults();
459: for (int ii = 0; ii < avs.length; ii++) {
460: if (avs[ii].getAspectType() == type) {
461: return avs[ii].getValue();
462: }
463: }
464: return Double.NaN;
465: }
466: }
|