/*
* Licensed Materials - Property of Perforce Software, Inc.
* © Copyright Perforce Software, Inc. 2014, 2021
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package database;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import ilog.views.gantt.IlvActivity;
import ilog.views.gantt.IlvConstraint;
import ilog.views.gantt.IlvConstraintType;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvGanttModelUtil;
import ilog.views.gantt.IlvReservation;
import ilog.views.gantt.IlvResource;
import ilog.views.gantt.IlvTimeInterval;
/**
* <code>GanttModelDBROWrapper</code> simulates a read-only database of
* scheduling data by wrapping an existing
* {@link ilog.views.gantt.IlvGanttModel}. The supplied Gantt data model is
* fully traversed to create simulated database lookup keys for each activity,
* resource, constraint, and reservation. This class supports relational queries
* on the data through the <code>GanttDBRO</code> interface.
*/
public class GanttModelDBROWrapper implements GanttDBRO {
// =========================================
// Class Constants
// =========================================
/**
* Log ID.
*/
public static final String logID = "GanttDB";
// =========================================
// Instance Variables
// =========================================
/**
* The underlying data model.
*/
private IlvGanttModel ganttData;
/**
* The simulated database latency, in milliseconds.
*/
private long delay;
/**
* The monitor that serializes access to the <code>doQueryDelay()</code>
* method.
*/
private Object delayLock;
// =========================================
// Instance Construction and Initialization
// =========================================
/**
* Creates a new <code>GanttModelDBROWrapper</code> using the data from the
* specified Gantt data model.
*/
public GanttModelDBROWrapper(IlvGanttModel data) {
delay = 0;
delayLock = new Object();
ganttData = data;
initDBKeys();
}
/**
* Computes and stores all the database lookup keys.
*/
private void initDBKeys() {
initActivityKeys();
initResourceKeys();
initReservationKeys();
initConstraintKeys();
}
/**
* Implements a delay that simulates the latency of performing a real database
* query.
*/
private void doQueryDelay() {
if (delay > 0) {
// Synchronized so multiple threads must wait for each other to do their
// delay. This simulates a single database connection accessed from
// multiple threads.
synchronized (delayLock) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
}
}
}
/**
* Returns the delay, in milliseconds, that simulates database access latency.
*/
public long getLatency() {
return delay;
}
/**
* Sets the delay, in milliseconds, that simulates database access latency.
*/
public void setLatency(long delay) {
this.delay = delay;
}
// =========================================
// Activities
// =========================================
/**
* Lookup table of activities, keyed by their lookup key.
*/
private Map<String, IlvActivity> activities;
/**
* Reverse lookup table of activity keys, keyed by the activity.
*/
private Map<IlvActivity, String> activityKeys;
/**
* The next assignable activity key value.
*/
private long nextActivityKey = 0;
/**
* Computes and stores the activity lookup keys.
*/
private void initActivityKeys() {
activities = new HashMap<String, IlvActivity>();
activityKeys = new HashMap<IlvActivity, String>();
for (Iterator<IlvActivity> i = IlvGanttModelUtil.activityPreorderIterator(ganttData); i.hasNext();) {
IlvActivity a = i.next();
String key = "Act" + (nextActivityKey++);
activities.put(key, a);
activityKeys.put(a, key);
}
}
/**
* Returns a descriptive string for the activity specified by its lookup key.
*/
protected String activityString(String key) {
IlvActivity a = activities.get(key);
StringBuffer buf = new StringBuffer();
buf.append('[');
buf.append(key);
buf.append(',');
if (a == null)
buf.append("???");
else {
buf.append('"');
buf.append(a.getName());
buf.append('"');
}
buf.append(']');
return buf.toString();
}
/**
* Returns the lookup key for the root activity or <code>null</code> if there
* are no activities.
*/
Override
public String queryRootActivityKey() {
IlvActivity root = ganttData.getRootActivity();
String key = (root == null) ? null : activityKeys.get(root);
Logger.getLogger(logID).log(Level.INFO, "[DB] Query root activity key: {0}", key);
doQueryDelay();
return key;
}
/**
* Returns the <code>Activity</code> record for the activity specified by its
* lookup key.
*/
Override
public GanttDBRO.ActivityRecord queryActivity(String key) {
IlvActivity a = activities.get(key);
ActivityRec arec;
if (a == null) {
arec = null;
} else {
arec = new ActivityRec(key, a.getID(), a.getName(), a.getStartTime(), a.getEndTime());
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query activity properties: {0}", activityString(key));
doQueryDelay();
return arec;
}
/**
* Returns the lookup key for the parent of the specified parent activity. If
* the activity is the root activity, then it has no parent and this method
* will return <code>null</code>.
*/
Override
public String queryActivityParent(String key) {
IlvActivity a = activities.get(key);
String parentKey;
if (a == null) {
parentKey = null;
} else {
IlvActivity parent = ganttData.getParentActivity(a);
if (parent == null)
parentKey = null;
else
parentKey = activityKeys.get(parent);
}
String[] logParams = { activityString(key), activityString(parentKey) };
Logger.getLogger(logID).log(Level.INFO, "[DB] Query parent key of activity: {0}, parent={1}", logParams);
doQueryDelay();
return parentKey;
}
/**
* Returns an array of lookup keys for the children of the specified parent
* activity.
*/
Override
public String[] queryActivityChildren(String key) {
IlvActivity a = activities.get(key);
String[] childKeys;
if (a == null) {
childKeys = new String[0];
} else {
childKeys = new String[ganttData.getChildActivityCount(a)];
for (int i = 0; i < childKeys.length; i++) {
childKeys[i] = activityKeys.get(ganttData.getChildActivity(a, i));
}
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query child keys of activity: {0}", activityString(key));
doQueryDelay();
return childKeys;
}
public static class ActivityRec implements GanttDBRO.ActivityRecord {
private String key;
private String name;
private String id;
private Date start;
private Date end;
private ActivityRec(String key, String id, String name, Date start, Date end) {
this.key = key;
this.id = id;
this.name = name;
this.start = start;
this.end = end;
}
Override
public String getKey() {
return key;
}
Override
public String getName() {
return name;
}
Override
public String getID() {
return id;
}
Override
public Date getStart() {
return start;
}
Override
public Date getEnd() {
return end;
}
}
// =========================================
// Resources
// =========================================
/**
* Lookup table of resources, keyed by their lookup key.
*/
private Map<String, IlvResource> resources;
/**
* Reverse lookup table of resource keys, keyed by the resource.
*/
private Map<IlvResource, String> resourceKeys;
/**
* The next assignable resource key value.
*/
private long nextResourceKey = 0;
/**
* Computes and stores the resource lookup keys.
*/
private void initResourceKeys() {
resources = new HashMap<String, IlvResource>();
resourceKeys = new HashMap<IlvResource, String>();
for (Iterator<IlvResource> i = IlvGanttModelUtil.resourcePreorderIterator(ganttData); i.hasNext();) {
IlvResource a = i.next();
String key = "Res" + (nextResourceKey++);
resources.put(key, a);
resourceKeys.put(a, key);
}
}
/**
* Returns a descriptive string for the resource specified by its lookup key.
*/
protected String resourceString(String key) {
IlvResource r = resources.get(key);
StringBuffer buf = new StringBuffer();
buf.append('[');
buf.append(key);
buf.append(',');
if (r == null)
buf.append("???");
else {
buf.append('"');
buf.append(r.getName());
buf.append('"');
}
buf.append(']');
return buf.toString();
}
/**
* Returns the lookup key for the root resource or <code>null</code> if there
* are no resources.
*/
Override
public String queryRootResourceKey() {
IlvResource root = ganttData.getRootResource();
String key = (root == null) ? null : resourceKeys.get(root);
Logger.getLogger(logID).log(Level.INFO, "[DB] Query root resource key: {0}", key);
doQueryDelay();
return key;
}
/**
* Returns the <code>Resource</code> record for the resource specified by its
* lookup key.
*/
Override
public GanttDBRO.ResourceRecord queryResource(String key) {
IlvResource r = resources.get(key);
ResourceRec rrec;
if (r == null) {
rrec = null;
} else {
rrec = new ResourceRec(key, r.getID(), r.getName(), r.getQuantity());
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query resource properties: {0}", resourceString(key));
doQueryDelay();
return rrec;
}
/**
* Returns the lookup key for the parent of the specified parent resource. If
* the resource is the root resource, then it has no parent and this method
* will return <code>null</code>.
*/
Override
public String queryResourceParent(String key) {
IlvResource r = resources.get(key);
String parentKey;
if (r == null) {
parentKey = null;
} else {
IlvResource parent = ganttData.getParentResource(r);
if (parent == null)
parentKey = null;
else
parentKey = resourceKeys.get(parent);
}
String[] logParams = { resourceString(key), resourceString(parentKey) };
Logger.getLogger(logID).log(Level.INFO, "[DB] Query parent key of resource: {0}, parent={1}", logParams);
doQueryDelay();
return parentKey;
}
/**
* Returns an array of lookup keys for the children of the specified parent
* resource.
*/
Override
public String[] queryResourceChildren(String key) {
IlvResource r = resources.get(key);
String[] childKeys;
if (r == null) {
childKeys = new String[0];
} else {
childKeys = new String[ganttData.getChildResourceCount(r)];
for (int i = 0; i < childKeys.length; i++) {
childKeys[i] = resourceKeys.get(ganttData.getChildResource(r, i));
}
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query child keys of resource: {0}", resourceString(key));
doQueryDelay();
return childKeys;
}
public static class ResourceRec implements GanttDBRO.ResourceRecord {
private String key;
private String name;
private String id;
private float quantity;
private ResourceRec(String key, String id, String name, float qty) {
this.key = key;
this.id = id;
this.name = name;
this.quantity = qty;
}
Override
public String getKey() {
return key;
}
Override
public String getName() {
return name;
}
Override
public String getID() {
return id;
}
Override
public float getQuantity() {
return quantity;
}
}
// =========================================
// Reservations
// =========================================
/**
* Lookup table of reservations, keyed by their lookup key.
*/
private Map<String, IlvReservation> reservations;
/**
* Reverse lookup table of reservation keys, keyed by the reservation.
*/
private Map<IlvReservation, String> reservationKeys;
/**
* The next assignable reservation key value.
*/
private long nextReservationKey = 0;
/**
* Computes and stores the reservation lookup keys.
*/
private void initReservationKeys() {
reservations = new HashMap<String, IlvReservation>();
reservationKeys = new HashMap<IlvReservation, String>();
for (Iterator<IlvReservation> i = ganttData.reservationIterator(); i.hasNext();) {
IlvReservation r = i.next();
String key = "Rsn" + (nextReservationKey++);
reservations.put(key, r);
reservationKeys.put(r, key);
}
}
/**
* Returns a descriptive string for the reservation specified by its lookup
* key.
*/
protected String reservationString(String key) {
IlvReservation r = reservations.get(key);
String activityKey = activityKeys.get(r.getActivity());
String resourceKey = resourceKeys.get(r.getResource());
StringBuffer buf = new StringBuffer();
buf.append('[');
buf.append(key);
buf.append(',');
buf.append(activityString(activityKey));
buf.append(',');
buf.append(resourceString(resourceKey));
buf.append(']');
return buf.toString();
}
/**
* Returns an array of reservation lookup keys derived from all the
* reservations that are traversed by the specified reservation iterator of
* the underlying data model.
*/
private String[] getReservationKeys(Iterator<IlvReservation> reservationIter) {
List<String> keyList = new ArrayList<String>();
while (reservationIter.hasNext()) {
IlvReservation reservation = reservationIter.next();
String key = reservationKeys.get(reservation);
keyList.add(key);
}
String[] keyArray = new String[keyList.size()];
keyList.toArray(keyArray);
return keyArray;
}
/**
* Returns an array of lookup keys for all the reservations.
*/
Override
public String[] queryReservations() {
String[] reservationKeys = getReservationKeys(ganttData.reservationIterator());
Logger.getLogger(logID).log(Level.INFO, "[DB] Query all reservation keys");
doQueryDelay();
return reservationKeys;
}
/**
* Returns an array of lookup keys for all the reservations that are
* associated with an activity, specified by its lookup key.
*/
Override
public String[] queryReservationsForActivity(String activityKey) {
IlvActivity activity = activities.get(activityKey);
String[] reservationKeys = getReservationKeys(ganttData.reservationIterator(activity));
Logger.getLogger(logID).log(Level.INFO, "[DB] Query reservation keys for activity: {0}",
activityString(activityKey));
doQueryDelay();
return reservationKeys;
}
/**
* Returns an array of lookup keys for all the reservations that are
* associated with a resource, specified by its lookup key.
*/
Override
public String[] queryReservationsForResource(String resourceKey) {
IlvResource resource = resources.get(resourceKey);
String[] reservationKeys = getReservationKeys(ganttData.reservationIterator(resource));
Logger.getLogger(logID).log(Level.INFO, "[DB] Query reservation keys for resource: {0}",
resourceString(resourceKey));
doQueryDelay();
return reservationKeys;
}
/**
* Returns an array of lookup keys for all the reservations that are
* associated with a resource, specified by its lookup key, and that intersect
* the specified time range.
*/
Override
public String[] queryReservationsForResource(String resourceKey, Date start, Date end) {
IlvResource resource = resources.get(resourceKey);
IlvTimeInterval interval = new IlvTimeInterval(start, end);
String[] reservationKeys = getReservationKeys(ganttData.reservationIterator(resource, interval));
Object[] logParams = { resourceString(resourceKey), start, end };
Logger.getLogger(logID).log(Level.INFO, "[DB] Query reservation keys for resource: {0}, time span: {1} - {2}",
logParams);
doQueryDelay();
return reservationKeys;
}
/**
* Returns the <code>Reservation</code> record for the resource specified by
* its lookup key.
*/
Override
public GanttDBRO.ReservationRecord queryReservation(String key) {
IlvReservation r = reservations.get(key);
ReservationRec rrec;
if (r == null) {
rrec = null;
} else {
String activityKey = activityKeys.get(r.getActivity());
String resourceKey = resourceKeys.get(r.getResource());
rrec = new ReservationRec(key, activityKey, resourceKey);
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query reservation properties: {0}", reservationString(key));
doQueryDelay();
return rrec;
}
public static class ReservationRec implements GanttDBRO.ReservationRecord {
private String key;
private String activityKey;
private String resourceKey;
private ReservationRec(String key, String activityKey, String resourceKey) {
this.key = key;
this.activityKey = activityKey;
this.resourceKey = resourceKey;
}
Override
public String getKey() {
return key;
}
Override
public String getActivityKey() {
return activityKey;
}
Override
public String getResourceKey() {
return resourceKey;
}
}
// =========================================
// Constraints
// =========================================
/**
* Lookup table of constraints, keyed by their lookup key.
*/
private Map<String, IlvConstraint> constraints;
/**
* Reverse lookup table of constraint keys, keyed by the constraint.
*/
private Map<IlvConstraint, String> constraintKeys;
/**
* The next assignable constraint key value.
*/
private long nextConstraintKey = 0;
/**
* Computes and stores the constraint lookup keys.
*/
private void initConstraintKeys() {
constraints = new HashMap<String, IlvConstraint>();
constraintKeys = new HashMap<IlvConstraint, String>();
for (Iterator<IlvConstraint> i = ganttData.constraintIterator(); i.hasNext();) {
IlvConstraint r = i.next();
String key = "Cnt" + (nextConstraintKey++);
constraints.put(key, r);
constraintKeys.put(r, key);
}
}
/**
* Returns a descriptive string for the constraint specified by its lookup
* key.
*/
protected String constraintString(String key) {
IlvConstraint c = constraints.get(key);
String fromActivityKey = activityKeys.get(c.getFromActivity());
String toActivityKey = activityKeys.get(c.getToActivity());
StringBuffer buf = new StringBuffer();
buf.append('[');
buf.append(key);
buf.append(',');
buf.append(c.getType());
buf.append(",from=");
buf.append(activityString(fromActivityKey));
buf.append(",to=");
buf.append(activityString(toActivityKey));
buf.append(']');
return buf.toString();
}
/**
* Returns an array of constraint lookup keys derived from all the constraints
* that are traversed by the specified constraint iterator of the underlying
* data model.
*/
private String[] getConstraintKeys(Iterator<IlvConstraint> constraintIter) {
List<String> keyList = new ArrayList<String>();
while (constraintIter.hasNext()) {
IlvConstraint constraint = constraintIter.next();
String key = constraintKeys.get(constraint);
keyList.add(key);
}
String[] keyArray = new String[keyList.size()];
keyList.toArray(keyArray);
return keyArray;
}
/**
* Returns an array of lookup keys for all the constraints.
*/
Override
public String[] queryConstraints() {
String[] constraintKeys = getConstraintKeys(ganttData.constraintIterator());
Logger.getLogger(logID).log(Level.INFO, "[DB] Query all constraint keys");
doQueryDelay();
return constraintKeys;
}
/**
* Returns an array of lookup keys for all the constraints that have the
* specified activity as their source or <i>from</i> activity, specified by
* its lookup key.
*/
Override
public String[] queryConstraintsFromActivity(String activityKey) {
IlvActivity activity = activities.get(activityKey);
String[] constraintKeys = getConstraintKeys(ganttData.constraintIteratorFromActivity(activity));
Logger.getLogger(logID).log(Level.INFO, "[DB] Query all constraint keys from activity: {0}",
activityString(activityKey));
doQueryDelay();
return constraintKeys;
}
/**
* Returns an array of lookup keys for all the constraints that have the
* specified activity as their target or <i>to</i> activity, specified by its
* lookup key.
*/
Override
public String[] queryConstraintsToActivity(String activityKey) {
IlvActivity activity = activities.get(activityKey);
String[] constraintKeys = getConstraintKeys(ganttData.constraintIteratorToActivity(activity));
Logger.getLogger(logID).log(Level.INFO, "[DB] Query all constraint keys to activity: {0}",
activityString(activityKey));
doQueryDelay();
return constraintKeys;
}
/**
* Returns the <code>Constraint</code> record for the constraint specified by
* its lookup key.
*/
Override
public GanttDBRO.ConstraintRecord queryConstraint(String key) {
IlvConstraint c = constraints.get(key);
ConstraintRec crec;
if (c == null) {
crec = null;
} else {
String fromActivityKey = activityKeys.get(c.getFromActivity());
String toActivityKey = activityKeys.get(c.getToActivity());
crec = new ConstraintRec(key, c.getType(), fromActivityKey, toActivityKey);
}
Logger.getLogger(logID).log(Level.INFO, "[DB] Query constraint properties: {0}", constraintString(key));
doQueryDelay();
return crec;
}
public static class ConstraintRec implements GanttDBRO.ConstraintRecord {
private String key;
private IlvConstraintType type;
private String fromActivityKey;
private String toActivityKey;
private ConstraintRec(String key, IlvConstraintType type, String fromActivityKey, String toActivityKey) {
this.key = key;
this.type = type;
this.fromActivityKey = fromActivityKey;
this.toActivityKey = toActivityKey;
}
Override
public String getKey() {
return key;
}
Override
public IlvConstraintType getType() {
return type;
}
Override
public String getFromActivityKey() {
return fromActivityKey;
}
Override
public String getToActivityKey() {
return toActivityKey;
}
}
}