001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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: package org.cougaar.lib.vishnu.client.custom;
027:
028: import com.bbn.vishnu.scheduling.SchObject;
029: import com.bbn.vishnu.scheduling.SchedulingData;
030: import org.cougaar.planning.ldm.asset.Asset;
031: import org.cougaar.planning.ldm.plan.PlanElement;
032: import org.cougaar.planning.ldm.plan.RoleSchedule;
033: import org.cougaar.planning.ldm.plan.Schedule;
034: import org.cougaar.planning.ldm.plan.NamedPosition;
035: import org.cougaar.planning.ldm.plan.LatLonPoint;
036: import org.cougaar.util.TimeSpan;
037: import org.cougaar.util.log.Logger;
038: import org.w3c.dom.Document;
039: import org.w3c.dom.Element;
040: import org.w3c.dom.NodeList;
041:
042: import java.util.Calendar;
043: import java.util.Collection;
044: import java.util.Date;
045: import java.util.HashMap;
046: import java.util.HashSet;
047: import java.util.Iterator;
048: import java.util.Map;
049: import java.util.Set;
050:
051: /**
052: * Fills in the fields of Vishnu objects sent to the Vishnu Scheduler. <p>
053: *
054: * Scans the format document to determine the types of each field of data.
055: * This is necessary with direct translation because when a field is added
056: * its attributes must be set. These are : <p>
057: *
058: * - datatype - the field type <p>
059: * - is_subobject - is the field's type a primitive or a subobject <p>
060: * - is_key - is this field the key field for the object <p>
061: * - is_list - is this field a list <p>
062: *
063: */
064: public class DirectDataHelper implements DataHelper {
065:
066: /**
067: * Sets up the endOfWorld time, used in createRoleScheduleListField.
068: *
069: * @see #createRoleScheduleListField
070: * @param formatDoc - the object format document
071: */
072: public DirectDataHelper(Document formatDoc,
073: SchedulingData schedData, Logger logger) {
074: Calendar calendar = Calendar.getInstance();
075: calendar.set(2100, 1, 1);
076: endOfWorld = calendar.getTime();
077:
078: this .schedData = schedData;
079:
080: this .logger = logger;
081: scanFormatDoc(formatDoc);
082: }
083:
084: /**
085: * Scans the format document to determine the types of each field of data.
086: *
087: * Stores this information in the <code>objects</code> map.
088: * @param formatDoc the object format document
089: */
090: protected void scanFormatDoc(Document formatDoc) {
091: NodeList objectFormats = formatDoc
092: .getElementsByTagName("OBJECTFORMAT");
093: for (int i = 0; i < objectFormats.getLength(); i++) {
094: Element objectFormat = (Element) objectFormats.item(i);
095: NodeList fieldFormats = objectFormat
096: .getElementsByTagName("FIELDFORMAT");
097:
098: ObjectInfo objectInfo;
099:
100: objects.put(objectFormat.getAttribute("name"),
101: objectInfo = new ObjectInfo());
102:
103: if (logger.isDebugEnabled())
104: logger.debug("DirectDataHelper.ctor - "
105: + objectFormat.getAttribute("name"));
106:
107: for (int j = 0; j < fieldFormats.getLength(); j++) {
108: Element field = (Element) fieldFormats.item(j);
109:
110: String name = field.getAttribute("name");
111: String type = field.getAttribute("datatype");
112: String sub = field.getAttribute("is_subobject");
113: String key = field.getAttribute("is_key");
114: String list = field.getAttribute("is_list");
115:
116: if (logger.isDebugEnabled())
117: logger.debug("DirectDataHelper.ctor - field "
118: + name);
119:
120: objectInfo.nameToType.put(name, type);
121: if (sub.equals("true"))
122: objectInfo.subObjects.add(type);
123: if (key.equals("true"))
124: objectInfo.keys.add(name);
125: if (list.equals("true"))
126: objectInfo.lists.add(name);
127: }
128: }
129: }
130:
131: /**
132: * Translate an asset's role schedule into a list of Vishnu intervals
133: *
134: * The interval has a start and end time and the verb of plan element in the role schedule.
135: *
136: * @param object - the SchObject to add the <code>name</code> list field to
137: * @param name - the name of the list field
138: * @param asset - the asset with the role schedule
139: */
140: public void createRoleScheduleListField(Object object, String name,
141: Asset asset) {
142: RoleSchedule unavail = asset.getRoleSchedule();
143: SchObject resource = (SchObject) object;
144:
145: startList(resource, name);
146:
147: createScheduleFields(unavail.getEncapsulatedRoleSchedule(0,
148: endOfWorld.getTime()), resource, name);
149: }
150:
151: public void createAvailableScheduleListField(Object object,
152: String name, Asset asset) {
153: SchObject resource = (SchObject) object;
154: startList(resource, name);
155:
156: Schedule availSchedule = asset.getRoleSchedule()
157: .getAvailableSchedule();
158:
159: if (availSchedule == null) {
160: if (logger.isDebugEnabled())
161: logger.debug("No available schedule on asset " + asset);
162: } else {
163: Collection coll = availSchedule
164: .getEncapsulatedScheduleElements(0, endOfWorld
165: .getTime());
166: if (coll.isEmpty())
167: logger
168: .debug("DirectDataHelper -- availSchedule is empty");
169:
170: createScheduleFields(availSchedule
171: .getEncapsulatedScheduleElements(0, endOfWorld
172: .getTime()), resource, name);
173: }
174: }
175:
176: protected void createScheduleFields(Collection schedule,
177: SchObject resource, String name) {
178: for (Iterator iter = schedule.iterator(); iter.hasNext();) {
179: TimeSpan span = (TimeSpan) iter.next();
180: SchObject interval = new SchObject(
181: SchedulingData.INTERVAL_TYPE, schedData);
182:
183: interval.addDateMillis("start", span.getStartTime());
184: interval.addDateMillis("end", span.getEndTime());
185: if (span instanceof PlanElement)
186: interval.addField("label1", "string",
187: ((PlanElement) span).getTask().getVerb()
188: .toString(), false, false);
189:
190: // resource.addField (name, "interval", interval, false, true);
191: addListValue(resource, name, "interval", interval);
192: }
193: }
194:
195: public Object startList(Object object, String name) {
196: ((SchObject) object).addListField(name);
197: return object;
198: }
199:
200: public void addListValue(Object parent, String fieldName,
201: String type, Object toAppend) {
202: ((SchObject) parent).addField(fieldName, type, toAppend, false,
203: true);
204: }
205:
206: protected Map geolocCodeCache = new HashMap();
207: protected Map latLonCache = new HashMap();
208:
209: /**
210: * Translate a Cougaar GeolocLocation into the equivalent Vishnu structure.
211: *
212: * The field names here are all "parentFieldName".name, e.g.
213: * from.geolocCode.
214: *
215: * Adds a latlong SchObject to the parent object, and adds the geoloc
216: * to the parent.
217: *
218: * Uses a geolocCode and latLon Cache to drastically reduce the number
219: * of Geoloc and latLon objects that are created.
220: *
221: * @param parent - the SchObject to add the <code>parentFieldName</code> geoloc field to
222: * @param parentFieldName - the base name of the geoloc field
223: * @param loc - the Cougaar GeolocLocation to translate into a Vishnu structure
224: */
225: public void createGeoloc(Object parent, String parentFieldName,
226: NamedPosition loc) {
227: GeolocData geolocData = (GeolocData) geolocCodeCache
228: .get(parentFieldName);
229:
230: if (geolocData == null) {
231: geolocData = new GeolocData(parentFieldName);
232: geolocCodeCache.put(parentFieldName, geolocData);
233: }
234:
235: SchObject objectParent = (SchObject) parent;
236:
237: objectParent.addField(geolocData.geoLocCode, "string", loc
238: .getUid(), false, false);
239:
240: float latDegrees = (float) loc.getLatitude().getDegrees();
241: float lonDegrees = (float) loc.getLongitude().getDegrees();
242:
243: SchObject latLonObject;
244: if ((latLonObject = (SchObject) latLonCache.get(loc.getUid())) == null) {
245: latLonObject = new SchObject("latlong", schedData);
246: // fill in the fields on the predefined type
247: latLonObject.addFloat("latitude", latDegrees);
248: latLonObject.addFloat("longitude", lonDegrees);
249: latLonCache.put(loc.getUid(), latLonObject);
250: }
251:
252: // fill in the fields on the root task/resource
253: objectParent.addField(geolocData.baseName, "latlong",
254: latLonObject, false, false);
255:
256: // e.g. base name = from.latlong.latitude
257: objectParent.addFloat(geolocData.latName, latDegrees);
258:
259: // e.g. base name = from.latlong.longitude
260: objectParent.addFloat(geolocData.lonName, lonDegrees);
261: }
262:
263: public void createLatLon(Object parent, String parentFieldName,
264: LatLonPoint loc) {
265: float latDegrees = (float) loc.getLatitude().getDegrees();
266: float lonDegrees = (float) loc.getLongitude().getDegrees();
267:
268: SchObject latlong = new SchObject("latlong", schedData);
269: // fill in the fields on the predefined type
270: latlong.addFloat("latitude", latDegrees);
271: latlong.addFloat("longitude", lonDegrees);
272:
273: SchObject objectParent = (SchObject) parent;
274:
275: // have to explicitly add these lines to the parent object!
276: objectParent
277: .addFloat(parentFieldName + ".latitude", latDegrees);
278: objectParent.addFloat(parentFieldName + ".longitude",
279: lonDegrees);
280:
281: // fill in the fields on the root task/resource
282: String val = objectParent.addField(parentFieldName, "latlong",
283: latlong, false, false);
284: if (val != null)
285: logger.warn("got error " + val + " adding lat long to "
286: + parentFieldName);
287: //else
288: // logger.warn ("field is " + objectParent.getField(parentFieldName));
289:
290: }
291:
292: private class GeolocData {
293: public String geoLocCode;
294: public String baseName;
295: public String latName;
296: public String lonName;
297:
298: public GeolocData(String parentFieldName) {
299: StringBuffer sb = new StringBuffer();
300: sb.append(parentFieldName);
301: sb.append('.');
302: sb.append("geolocCode");
303: geoLocCode = sb.toString();
304:
305: StringBuffer baseNameBuffer = new StringBuffer();
306: baseNameBuffer.append(parentFieldName);
307: baseNameBuffer.append('.');
308: baseNameBuffer.append("latlong");
309: baseName = baseNameBuffer.toString();
310:
311: StringBuffer latNameBuffer = new StringBuffer();
312: latNameBuffer.append(baseName);
313: latNameBuffer.append('.');
314: latNameBuffer.append("latitude");
315: latName = latNameBuffer.toString();
316:
317: StringBuffer lonNameBuffer = new StringBuffer();
318: lonNameBuffer.append(baseName);
319: lonNameBuffer.append('.');
320: lonNameBuffer.append("longitude");
321: lonName = lonNameBuffer.toString();
322: }
323: }
324:
325: /**
326: * create a vanilla Vishnu object
327: *
328: * @param parent - ignored here
329: * @param type - ignored here
330: */
331: public Object createObject(Object parent, String type) {
332: return new SchObject(type, schedData);
333: }
334:
335: /**
336: * shortcut to create a date field on <code>parent</code>
337: */
338: public void createDateField(Object parent, String name, Date date) {
339: if (logger.isDebugEnabled())
340: logger.debug("DirectDataHelper.createDateField - "
341: + " name " + name + " value " + date);
342:
343: ((SchObject) parent).addDate(name, date);
344: }
345:
346: /** shortcut to create a boolean field on <code>parent</code> */
347: public void createBooleanField(Object parent, String name,
348: boolean value) {
349: if (logger.isDebugEnabled())
350: logger.debug("DirectDataHelper.createBooleanField - "
351: + " name " + name + " value " + value);
352:
353: ((SchObject) parent).addBoolean(name, value);
354: }
355:
356: /** shortcut to create a float field on <code>parent</code> */
357: public void createFloatField(Object parent, String name, float value) {
358: if (logger.isDebugEnabled())
359: logger.debug("DirectDataHelper.createFloatField - "
360: + " name " + name + " value " + value);
361:
362: ((SchObject) parent).addFloat(name, value);
363: }
364:
365: /** not used in this helper */
366: public Object createField(Object parent, String name) {
367: return null;
368: }
369:
370: /** not used in this helper */
371: public Object createFieldPair(String name, String value) {
372: logger.error("huh? don't call me");
373: return null;
374: }
375:
376: /** not used in this helper */
377: public Object createField(Object parent, String name, String value) {
378: logger.error("huh? don't call me");
379: return null;
380: }
381:
382: /**
383: * Generic field creation <p>
384: *
385: * Given the parentType and the name of the field, looks up the other
386: * field attributes in the <code>objects</code> map, using the isKey, isList,
387: * and getType methods. <p>
388: *
389: * This map is set in scanFormatDoc.
390: *
391: * @see #scanFormatDoc
392: * @see #objects
393: * @param parent SchObject object to add the field to
394: * @param parentType type of the SchObject
395: * @param name field name
396: * @param value field's value
397: */
398: public void createField(Object parent, String parentType,
399: String name, String value) {
400: boolean isKey = isKey(parentType, name);
401: boolean isList = isList(parentType, name);
402: String type = getType(parentType, name);
403:
404: if (logger.isDebugEnabled())
405: logger.debug("DirectDataHelper.createField - "
406: + " parentType " + parentType + " name " + name
407: + " type " + type + " value " + value + " key "
408: + isKey + " list " + isList);
409:
410: ((SchObject) parent).addField(name, type, value, isKey, isList);
411: }
412:
413: /**
414: * In all of these methods (isKey, isList, isSub, getType), <br>
415: * if a type is not found in the objects map, it may <br>
416: * be because it's a predefined type. <p>
417: *
418: * This should never happen, though.
419: */
420: protected boolean isKey(String parentType, String name) {
421: ObjectInfo oi = (ObjectInfo) objects.get(parentType);
422: if (oi == null) {
423: if (!isPredefined(parentType))
424: logger
425: .error("DirectDataHelper.isKey - ERROR - missing parent type "
426: + parentType + " name " + name);
427: return false;
428: }
429:
430: return oi.keys.contains(name);
431: }
432:
433: protected boolean isList(String parentType, String name) {
434: ObjectInfo oi = (ObjectInfo) objects.get(parentType);
435: if (oi == null) {
436: if (!isPredefined(parentType))
437: logger
438: .error("DirectDataHelper.isList - ERROR - missing parent type "
439: + parentType + " name " + name);
440: return false;
441: }
442: return oi.lists.contains(name);
443: }
444:
445: protected boolean isSub(String parentType, String name) {
446: ObjectInfo oi = (ObjectInfo) objects.get(parentType);
447: if (oi == null) {
448: if (!isPredefined(parentType))
449: logger
450: .error("DirectDataHelper.isSub - ERROR - missing parent type "
451: + parentType);
452: return false;
453: }
454: return oi.subObjects.contains(name);
455: }
456:
457: protected String getType(String parentType, String name) {
458: ObjectInfo oi = (ObjectInfo) objects.get(parentType);
459: if (oi == null) {
460: if (!isPredefined(parentType))
461: logger
462: .error("DirectDataHelper.getType - ERROR - missing parent type "
463: + parentType + " name " + name);
464: return parentType;// probably wrong...
465: }
466: return (String) oi.nameToType.get(name);
467: }
468:
469: protected boolean isPredefined(String type) {
470: return type.equals("interval") || type.equals("xy_coord")
471: || type.equals("latlong") || type.equals("matrix");
472: }
473:
474: /**
475: * Holds object format info about objects, used in generic createField method
476: *
477: * @see DirectDataHelper#scanFormatDoc
478: */
479: protected class ObjectInfo {
480: public Map nameToType = new HashMap();
481: public Set subObjects = new HashSet();
482: public Set keys = new HashSet();
483: public Set lists = new HashSet();
484: }
485:
486: /** reference to SchedulingData object, used whenever a date is created */
487: protected SchedulingData schedData;
488:
489: /** holds Vishnu object attributes per type */
490: protected Map objects = new HashMap();
491:
492: /** used in createRoleScheduleListField */
493: protected Date endOfWorld;
494:
495: protected Logger logger;
496: }
|