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.mlm.plugin.ldm;
028:
029: import java.util.ArrayList;
030: import java.util.Calendar;
031: import java.util.Collection;
032: import java.util.Date;
033: import java.util.HashMap;
034: import java.util.Iterator;
035:
036: import org.cougaar.glm.ldm.oplan.Oplan;
037: import org.cougaar.glm.ldm.oplan.OplanFactory;
038: import org.cougaar.glm.ldm.oplan.OrgActivity;
039: import org.cougaar.glm.ldm.oplan.OrgActivityImpl;
040: import org.cougaar.glm.ldm.oplan.TimeSpan;
041: import org.cougaar.glm.ldm.plan.GeolocLocation;
042: import org.cougaar.util.TimeSpanSet;
043:
044: /** Reads OrgActivity for specified Oplan from a database table. Assumes it's
045: * being invoked on behalf of SQLOplanPlugin. Updates orgactivities maintained
046: * by SQLOplanPlugin.
047: */
048:
049: public class OrgActivityQueryHandler extends SQLOplanQueryHandler {
050: public static final String ACTIVITY_PARAMETER = "activity";
051: public static final String OPTEMPO_PARAMETER = "opTempo";
052: public static final String LOCATION_PARAMETER = "location";
053:
054: private static final String QUERY_NAME = "OrgActivityQuery";
055:
056: private static final int ACTIVITY_INDEX = 0;
057: private static final int LOCATION_INDEX = 1;
058: private static final int OPTEMPO_INDEX = 2;
059:
060: private Calendar myCalendar = Calendar.getInstance();
061:
062: private long myLastModifiedTime = TimeSpan.MIN_VALUE;
063: private long myMaxModifiedTime = TimeSpan.MIN_VALUE;
064:
065: private HashMap myOrgInfoMap = new HashMap();
066: private Oplan myOplan;
067: private String myActivityKey;
068: private String myOpTempoKey;
069: private String myLocationKey;
070:
071: /** this method is called before a query is started,
072: * before even getQuery. The default method is empty
073: * but may be overridden.
074: **/
075: public void startQuery() {
076: String oplanID = getParameter(OplanReaderPlugin.OPLAN_ID_PARAMETER);
077: myOplan = myPlugin.getOplan(oplanID);
078:
079: if (myOplan == null) {
080: throw new java.lang.IllegalArgumentException(
081: "OrgActivityQueryHandler.startQuery() - "
082: + " unable to get oplan info for "
083: + oplanID);
084: }
085:
086: myActivityKey = getParameter(ACTIVITY_PARAMETER);
087: myOpTempoKey = getParameter(OPTEMPO_PARAMETER);
088: myLocationKey = getParameter(LOCATION_PARAMETER);
089:
090: myOrgInfoMap.clear();
091: myMaxModifiedTime = myLastModifiedTime;
092: }
093:
094: /** Construct and return an SQL query to be used by the Database engine.
095: * Subclasses are required to implement this method.
096: **/
097: public String getQuery() {
098: return (String) getParameter(QUERY_NAME);
099: }
100:
101: /** Process a single row in a result set,
102: * doing whatever is required.
103: **/
104: public void processRow(Object[] rowData) {
105: if (rowData.length != 7) {
106: System.err
107: .println("OrgActivityQueryHandler.processRow() - expected 7 columns of data, "
108: + " got " + rowData.length);
109: }
110: try {
111: String relation = null;
112: if (rowData[0] instanceof String)
113: relation = (String) rowData[0];
114: else
115: relation = new String((byte[]) rowData[0], "US-ASCII");
116:
117: myMaxModifiedTime = Math.max(((Date) myOplan.getCday())
118: .getTime(), myMaxModifiedTime);
119:
120: if (relation.equals(myOpTempoKey)) {
121: processOpTempo(rowData);
122: } else if (relation.equals(myActivityKey)) {
123: processActivity(rowData);
124: } else if (relation.equals(myLocationKey)) {
125: processLocation(rowData);
126: } else {
127: System.err
128: .println("OrgActivityQueryHandler.processRow(): unexpected text in RELATION_NAME column - "
129: + relation);
130: System.err.println("Ignoring row of data: " + rowData);
131: }
132: } catch (Exception usee) {
133: System.err
134: .println("Caught exception while executing a query: "
135: + usee);
136: usee.printStackTrace();
137: }
138:
139: }
140:
141: /** this method is called when a query is complete,
142: * afer the last call to processRow. Updates OrgActivities maintained by
143: * SQLOplanPlugin
144: **/
145: public void endQuery() {
146: // myOrgInfoMap has a TimeSpanSet for each Org
147: Collection orgInfosByOrg = myOrgInfoMap.values();
148: ArrayList allOrgActivities = new ArrayList();
149: OrgInfo myOrgInfo = null;
150: for (Iterator iterator = orgInfosByOrg.iterator(); iterator
151: .hasNext();) {
152: OrgInfo orgInfo = (OrgInfo) iterator.next();
153: myOrgInfo = orgInfo;
154:
155: // Need to compute all org activities so I can update the
156: // oplan end day
157: TimeSpanSet orgActivities = getMergedOrgActivities(orgInfo);
158: allOrgActivities.addAll(orgActivities);
159: if (needToUpdate(orgInfo)) {
160:
161: /* Debugging */
162:
163: for (Iterator oaIterator = orgActivities.iterator(); oaIterator
164: .hasNext();) {
165: OrgActivity orgActivity = (OrgActivity) oaIterator
166: .next();
167: System.out.println(orgActivity.getOrgID() + " "
168: + orgActivity.getOpTempo() + " "
169: + orgActivity.getActivityType() + " "
170: + orgActivity.getTimeSpan().getStartDate()
171: + " "
172: + orgActivity.getTimeSpan().getEndDate()
173: + " " + orgActivity.getGeoLoc());
174: }
175:
176: myPlugin.updateOrgActivities(myOplan, orgInfo
177: .getOrgName(), orgActivities);
178: }
179: }
180:
181: Date currentEndDay = myOplan.getEndDay();
182: myOplan.inferEndDay(allOrgActivities);
183: if ((currentEndDay == null)
184: || (!currentEndDay.equals(myOplan.getEndDay()))) {
185: myPlugin.updateOplanInfo(myOplan);
186: }
187:
188: myLastModifiedTime = myMaxModifiedTime;
189: }
190:
191: protected void processOpTempo(Object[] rowData) {
192: try {
193: String orgName = null;
194: String opTempo = null;
195: if (rowData[1] instanceof String)
196: orgName = (String) rowData[1];
197: else
198: orgName = new String((byte[]) rowData[1], "US-ASCII");
199:
200: if (rowData[3] instanceof String)
201: opTempo = (String) rowData[3];
202: else
203: opTempo = new String((byte[]) rowData[3], "US-ASCII");
204:
205: if (!opTempo.equals(OrgActivity.HIGH_OPTEMPO)
206: && !opTempo.equals(OrgActivity.MEDIUM_OPTEMPO)
207: && !opTempo.equals(OrgActivity.LOW_OPTEMPO)) {
208: System.err
209: .println("OrgActivityQueryHandler: Unrecognized op tempo - "
210: + opTempo
211: + " - for "
212: + orgName
213: + " in " + myOplan);
214: }
215:
216: OrgInfoElement element = new OrgInfoElement(opTempo, System
217: .currentTimeMillis(), myOplan.getCday(),
218: ((Number) rowData[5]).intValue(),
219: ((Number) rowData[6]).intValue());
220:
221: OrgInfo orgInfo = (OrgInfo) myOrgInfoMap.get(orgName);
222: if (orgInfo == null) {
223: orgInfo = new OrgInfo(orgName);
224: myOrgInfoMap.put(orgName, orgInfo);
225: } else {
226: Collection opTempoCollection = orgInfo
227: .getOpTempoSchedule().intersectingSet(
228: element.getStartTime(),
229: element.getEndTime());
230:
231: if (opTempoCollection.size() > 0) {
232: System.err
233: .println("OrgActivityQueryHandler: found more than one opTempo for "
234: + orgName
235: + " during "
236: + new Date(element.getStartTime())
237: + " - "
238: + new Date(element.getEndTime()));
239: }
240: }
241: orgInfo.getOpTempoSchedule().add(element);
242:
243: } catch (Exception usee) {
244: System.err
245: .println("Caught exception while executing a query: "
246: + usee);
247: usee.printStackTrace();
248: }
249:
250: }
251:
252: protected void processActivity(Object[] rowData) {
253: try {
254: String orgName = null;
255: String activity = null;
256: if (rowData[1] instanceof String)
257: orgName = (String) rowData[1];
258: else
259: orgName = new String((byte[]) rowData[1], "US-ASCII");
260:
261: if (rowData[3] instanceof String)
262: activity = (String) rowData[3];
263: else
264: activity = new String((byte[]) rowData[3], "US-ASCII");
265:
266: OrgInfoElement element = new OrgInfoElement(activity,
267: System.currentTimeMillis(), myOplan.getCday(),
268: ((Number) rowData[5]).intValue(),
269: ((Number) rowData[6]).intValue());
270:
271: OrgInfo orgInfo = (OrgInfo) myOrgInfoMap.get(orgName);
272: if (orgInfo == null) {
273: orgInfo = new OrgInfo(orgName);
274: myOrgInfoMap.put(orgName, orgInfo);
275: } else {
276: Collection activityCollection = orgInfo
277: .getActivitySchedule().intersectingSet(
278: element.getStartTime(),
279: element.getEndTime());
280:
281: if (activityCollection.size() > 0) {
282: System.err
283: .println("OrgActivityQueryHandler: found more than one activity for "
284: + orgName
285: + " during "
286: + new Date(element.getStartTime())
287: + " - "
288: + new Date(element.getEndTime()));
289: }
290: }
291: orgInfo.getActivitySchedule().add(element);
292: } catch (Exception usee) {
293: System.err
294: .println("Caught exception while executing a query: "
295: + usee);
296: usee.printStackTrace();
297: }
298:
299: }
300:
301: protected void processLocation(Object[] rowData) {
302: try {
303: String orgName = null;
304: String locCode = null;
305: if (rowData[1] instanceof String)
306: orgName = (String) rowData[1];
307: else
308: orgName = new String((byte[]) rowData[1], "US-ASCII");
309:
310: if (rowData[3] instanceof String)
311: locCode = (String) rowData[3];
312: else
313: locCode = new String((byte[]) rowData[3], "US-ASCII");
314:
315: // look up geoloc from location string
316: GeolocLocation geoLoc = (GeolocLocation) myPlugin
317: .getLocation(locCode);
318:
319: OrgInfoElement element = new OrgInfoElement(geoLoc, System
320: .currentTimeMillis(), myOplan.getCday(),
321: ((Number) rowData[5]).intValue(),
322: ((Number) rowData[6]).intValue());
323:
324: OrgInfo orgInfo = (OrgInfo) myOrgInfoMap.get(orgName);
325: if (orgInfo == null) {
326: orgInfo = new OrgInfo(orgName);
327: myOrgInfoMap.put(orgName, orgInfo);
328: } else {
329: Collection locationCollection = orgInfo
330: .getLocationSchedule().intersectingSet(
331: element.getStartTime(),
332: element.getEndTime());
333:
334: if (locationCollection.size() > 0) {
335: System.err
336: .println("OrgActivityQueryHandler: found more than one location for "
337: + orgName
338: + " during "
339: + new Date(element.getStartTime())
340: + " - "
341: + new Date(element.getEndTime()));
342: }
343: }
344: orgInfo.getLocationSchedule().add(element);
345: } catch (Exception usee) {
346: System.err
347: .println("Caught exception while executing a query: "
348: + usee);
349: usee.printStackTrace();
350: }
351:
352: }
353:
354: protected boolean needToUpdate(OrgInfo orgInfo) {
355: return true;
356: // for (Iterator iterator = orgInfo.getActivitySchedule().iterator();
357: // iterator.hasNext();) {
358: // OrgInfoElement element = (OrgInfoElement) iterator.next();
359: // if (element.getLastModified() > myLastModifiedTime) {
360: // return true;
361: // }
362: // }
363:
364: // for (Iterator iterator = orgInfo.getLocationSchedule().iterator();
365: // iterator.hasNext();) {
366: // OrgInfoElement element = (OrgInfoElement) iterator.next();
367: // if (element.getLastModified() > myLastModifiedTime) {
368: // return true;
369: // }
370: // }
371:
372: // for (Iterator iterator = orgInfo.getOpTempoSchedule().iterator();
373: // iterator.hasNext();) {
374: // OrgInfoElement element = (OrgInfoElement) iterator.next();
375: // if (element.getLastModified() > myLastModifiedTime) {
376: // return true;
377: // }
378: // }
379:
380: // return false;
381: }
382:
383: /**
384: * getMergedOrgActivities - merges the activity, location, and optempo
385: * TimeSpanSets for the Org into a set of OrgActivities.
386: **/
387: protected TimeSpanSet getMergedOrgActivities(OrgInfo orgInfo) {
388: TimeSpanSet timeSpanSet = new TimeSpanSet();
389: if (orgInfo != null) {
390: Iterator activityIterator = orgInfo.getActivitySchedule()
391: .iterator();
392: Iterator locationIterator = orgInfo.getLocationSchedule()
393: .iterator();
394: Iterator opTempoIterator = orgInfo.getOpTempoSchedule()
395: .iterator();
396:
397: OrgInfoElement activity;
398: if (activityIterator.hasNext()) {
399: activity = (OrgInfoElement) activityIterator.next();
400: } else {
401: System.err
402: .println("OrgActivityQueryHandler:getMergedOrgActivities()"
403: + " - no activities for "
404: + orgInfo.getOrgName());
405: return timeSpanSet;
406: }
407:
408: OrgInfoElement location;
409: if (locationIterator.hasNext()) {
410: location = (OrgInfoElement) locationIterator.next();
411: } else {
412: System.err
413: .println("OrgActivityQueryHandler:getMergedOrgActivities()"
414: + " - no location for "
415: + orgInfo.getOrgName());
416: return timeSpanSet;
417: }
418:
419: OrgInfoElement opTempo;
420: if (opTempoIterator.hasNext()) {
421: opTempo = (OrgInfoElement) opTempoIterator.next();
422: } else {
423: System.err
424: .println("OrgActivityQueryHandler:getMergedOrgActivities()"
425: + " - no optempo for "
426: + orgInfo.getOrgName());
427: return timeSpanSet;
428: }
429:
430: ArrayList orgInfoElements = new ArrayList(3);
431:
432: orgInfoElements.add(ACTIVITY_INDEX, activity);
433: orgInfoElements.add(LOCATION_INDEX, location);
434: orgInfoElements.add(OPTEMPO_INDEX, opTempo);
435:
436: long start = getStart(TimeSpan.MIN_VALUE, orgInfoElements);
437: long end = TimeSpan.MAX_VALUE;
438:
439: while (start != TimeSpan.MAX_VALUE) {
440: end = getEnd(start, orgInfoElements);
441:
442: OrgActivity orgActivity = makeOrgActivity(orgInfo
443: .getOrgName(), start, end, activity, location,
444: opTempo);
445: /** Debugging**/
446: if (orgActivity == null) {
447: System.out.println(orgInfo.getOrgName()
448: + ": activity row count = "
449: + orgInfo.getActivitySchedule().size()
450: + " location row count = "
451: + orgInfo.getLocationSchedule().size()
452: + " opTempo row count = "
453: + orgInfo.getOpTempoSchedule().size());
454:
455: RuntimeException re = new RuntimeException();
456: re.printStackTrace();
457: throw re;
458: }
459: /****/
460: timeSpanSet.add(orgActivity);
461:
462: activity = endsAfter(end, activity, activityIterator);
463: orgInfoElements.set(ACTIVITY_INDEX, activity);
464:
465: location = endsAfter(end, location, locationIterator);
466: orgInfoElements.set(LOCATION_INDEX, location);
467:
468: opTempo = endsAfter(end, opTempo, opTempoIterator);
469: orgInfoElements.set(OPTEMPO_INDEX, opTempo);
470:
471: // Find start of next interval
472: start = getStart(end, orgInfoElements);
473:
474: if ((start != TimeSpan.MAX_VALUE) && (start > end)) {
475: System.out
476: .println("OrgActivityQueryHandler: found a schedule gap for "
477: + orgInfo.getOrgName()
478: + " - gap between "
479: + new Date(end)
480: + " - " + new Date(start));
481: }
482: }
483: }
484: return timeSpanSet;
485: }
486:
487: private OrgActivity makeOrgActivity(String orgName, long startTime,
488: long endTime, OrgInfoElement activity,
489: OrgInfoElement location, OrgInfoElement opTempo) {
490:
491: OrgActivityImpl orgActivity = OplanFactory.newOrgActivity(
492: orgName, myOplan.getUID());
493: myComponent.getUIDServer().registerUniqueObject(orgActivity);
494: orgActivity.setOwner(myMessageAddress);
495: orgActivity.setTimeSpan(makeOplanTimeSpan(startTime, endTime));
496:
497: if ((activity != null)
498: && (intersect(orgActivity.getTimeSpan(), activity))) {
499: orgActivity.setActivityType((String) activity.getObject());
500: } else {
501: System.err
502: .println("OrgActivityQueryHandler.makeOrgActivity() - "
503: + " no activity for "
504: + orgName
505: + " from "
506: + new Date(startTime)
507: + " to "
508: + new Date(endTime));
509: return null;
510: }
511:
512: if ((location != null)
513: && (intersect(orgActivity.getTimeSpan(), location))) {
514: orgActivity
515: .setGeoLoc((GeolocLocation) location.getObject());
516: } else {
517: System.err
518: .println("OrgActivityQueryHandler.makeOrgActivity() - "
519: + " no location for "
520: + orgName
521: + " from "
522: + new Date(startTime)
523: + " to "
524: + new Date(endTime));
525: return null;
526: }
527:
528: if ((opTempo != null)
529: && (intersect(orgActivity.getTimeSpan(), opTempo))) {
530: orgActivity.setOpTempo((String) opTempo.getObject());
531: } else {
532: System.err
533: .println("OrgActivityQueryHandler.makeOrgActivity() - "
534: + " no optempo for "
535: + orgName
536: + " from "
537: + new Date(startTime)
538: + " to "
539: + new Date(endTime));
540: return null;
541: }
542:
543: return orgActivity;
544: }
545:
546: private static long getEnd(long start, Collection timeSpans) {
547: long end = TimeSpan.MAX_VALUE;
548:
549: for (Iterator iterator = timeSpans.iterator(); iterator
550: .hasNext();) {
551: Object o = iterator.next();
552: if (o != null) {
553: TimeSpan timeSpan = (TimeSpan) o;
554: if (timeSpan.getStartTime() <= start) {
555: end = Math.min(end, timeSpan.getEndTime());
556: } else {
557: end = Math.min(end, timeSpan.getStartTime());
558: }
559: }
560: }
561:
562: return end;
563: }
564:
565: // returns TimeSpan.MAX_VALUE if there isn't any more data
566: private static long getStart(long end, Collection timeSpans) {
567: long start = TimeSpan.MAX_VALUE;
568:
569: for (Iterator iterator = timeSpans.iterator(); iterator
570: .hasNext();) {
571: Object o = iterator.next();
572: if (o != null) {
573: TimeSpan timeSpan = (TimeSpan) o;
574: if (timeSpan.getStartTime() < end) {
575: start = Math.min(start, end);
576: } else {
577: start = Math.min(start, timeSpan.getStartTime());
578: }
579: }
580: }
581:
582: return start;
583: }
584:
585: private static boolean intersect(TimeSpan ts1, TimeSpan ts2) {
586: return ((ts1 != null) && (ts2 != null)
587: && (ts1.getStartTime() < ts2.getEndTime()) && (ts1
588: .getEndTime() > ts2.getStartTime()));
589: }
590:
591: private TimeSpan makeOplanTimeSpan(long startTime, long endTime) {
592: Date startDate = normalize(new Date(startTime));
593: Date endDate = normalize(new Date(endTime));
594: Date cDate = normalize(myOplan.getCday());
595:
596: // Find cDate -> startDate
597: int startDelta = 0;
598: for (myCalendar.setTime(cDate); myCalendar.getTime().getTime() < startDate
599: .getTime(); startDelta++) {
600: myCalendar.add(Calendar.DATE, 1);
601: }
602:
603: int endDelta = startDelta;
604: for (myCalendar.setTime(startDate); myCalendar.getTime()
605: .getTime() < endDate.getTime(); endDelta++) {
606: myCalendar.add(Calendar.DATE, 1);
607: }
608:
609: return new org.cougaar.glm.ldm.oplan.TimeSpan(
610: myOplan.getCday(), startDelta, endDelta);
611: }
612:
613: private Date normalize(Date date) {
614: myCalendar.setTime(date);
615: int year = myCalendar.get(Calendar.YEAR);
616: int month = myCalendar.get(Calendar.MONTH);
617: int day = myCalendar.get(Calendar.DATE);
618:
619: // set to midnight
620: myCalendar.set(year, month, day, 0, 0);
621: return myCalendar.getTime();
622: }
623:
624: // Presumes that that OrgInfoElements are sorted by TimeSpan and non
625: // overlapping
626: private OrgInfoElement endsAfter(long start,
627: OrgInfoElement current, Iterator iterator) {
628:
629: if (current == null) {
630: // We've already walked the entire set.
631: return current;
632: }
633:
634: if (current.getEndTime() <= start) {
635: while (iterator.hasNext()) {
636: OrgInfoElement next = (OrgInfoElement) iterator.next();
637: if (next.getEndTime() > start) {
638: return next;
639: }
640: }
641: return null;
642: } else {
643: return current;
644: }
645: }
646:
647: private class OrgInfo {
648:
649: private String myOrgName;
650: private TimeSpanSet myOpTempoSchedule;
651: private TimeSpanSet myActivitySchedule;
652: private TimeSpanSet myLocationSchedule;
653:
654: public OrgInfo(String orgName) {
655: myOrgName = orgName;
656:
657: myOpTempoSchedule = new TimeSpanSet();
658: myActivitySchedule = new TimeSpanSet();
659: myLocationSchedule = new TimeSpanSet();
660: }
661:
662: public String getOrgName() {
663: return myOrgName;
664: }
665:
666: public TimeSpanSet getOpTempoSchedule() {
667: return myOpTempoSchedule;
668: }
669:
670: public TimeSpanSet getActivitySchedule() {
671: return myActivitySchedule;
672: }
673:
674: public TimeSpanSet getLocationSchedule() {
675: return myLocationSchedule;
676: }
677: }
678:
679: private class OrgInfoElement extends
680: org.cougaar.glm.ldm.oplan.TimeSpan {
681: private Object myObject;
682: private long myLastModified;
683:
684: public OrgInfoElement(Object object, long lastModified,
685: Date baseDate, int startDelta, int endDelta) {
686: super (baseDate, startDelta, endDelta);
687:
688: myObject = object;
689: myLastModified = lastModified;
690: }
691:
692: public Object getObject() {
693: return myObject;
694: }
695:
696: public long getLastModified() {
697: return myLastModified;
698: }
699: }
700: }
|