/* * 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.Arrays; 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.IlvHierarchyNode; import ilog.views.gantt.IlvReservation; import ilog.views.gantt.IlvResource; import ilog.views.gantt.IlvTimeInterval; import ilog.views.gantt.model.IlvAbstractActivity; import ilog.views.gantt.model.IlvAbstractConstraint; import ilog.views.gantt.model.IlvAbstractGanttModel; import ilog.views.gantt.model.IlvAbstractReservation; import ilog.views.gantt.model.IlvAbstractResource; /** * <code>DBROGanttModel</code> is a read-only Gantt data model that connects to * an underlying {@link GanttDBRO}. The data model caches the results of * querying the database to optimize speed at the expense of memory usage. * Activities, resources, constraints, and reservations are defined as inner * classes. */ public class DBROGanttModel extends IlvAbstractGanttModel { // ========================================= // Class Constants // ========================================= /** * Log ID. */ public static final String logID = "DBModel"; // ========================================= // Instance Variables // ========================================= /** * The database. */ private GanttDBRO db; // ========================================= // Instance Construction and Initialization // ========================================= /** * Creates a new <code>DBROGanttModel</code> that queries the specified * database. */ public DBROGanttModel(GanttDBRO database) { db = database; initActivities(); initResources(); initConstraints(); initReservations(); } // ========================================= // Accessing // ========================================= /** * Returns the underlying database. */ public GanttDBRO getDatabase() { return db; } // ========================================= // Activities and Resources // ========================================= /** * Returns whether the specified activity or resource is a member of the data * model. * * @param activityOrResource * The activity or resource. */ Override public boolean contains(IlvHierarchyNode activityOrResource) { if (activityOrResource instanceof IlvActivity) return contains((IlvActivity) activityOrResource); else return contains((IlvResource) activityOrResource); } // ========================================= // Activities // ========================================= /** * The database lookup key of the root activity. */ private String rootActivityKey; /** * Indicates whether <code>rootActivityKey</code> is valid. */ private boolean rootActivityQueried; /** * Cached {@link ActivityState}s, indexed by their database lookup key. */ private Map<String, ActivityState> activityCache; /** * The <code>ActivityState</code> inner class. Each instance maintains state * info on an activity's hierarchical relationships. */ class ActivityState { /** * The activity for this state info. */ Activity activity; /** * Indicates whether the parent ID for the activity has been queried. If * <code>true</code>, then the value in <code>parentKey</code> is valid. */ boolean parentQueried; /** * The database lookup key of the activity's parent. If <code>null</code>, * then the activity is the root activity and has no parent. This value is * only valid if <code>parentQueried</code> is <code>true</code>. */ String parentKey; /** * The list of lookup keys for the activity's children. If the child keys * have not been queried yet, then <code>childKeys</code> will be * <code>null</code>. */ List<String> childKeys; /** * Creates an <code>ActivityState</code> for the specified activity. */ public ActivityState(Activity activity) { this.activity = activity; } /** * Returns the activity. */ public final Activity getActivity() { return activity; } /** * Returns the activity-state for the parent of the activity or * <code>null</code> if the activity is the root activity. */ public final ActivityState getParentState() { if (!parentQueried) { Activity activity = getActivity(); if (activity == getRootActivity()) parentKey = null; else parentKey = getDatabase().queryActivityParent(getActivity().getDBKey()); parentQueried = true; } if (parentKey == null) return null; return getActivityState(parentKey); } /** * Returns the parent of the activity or <code>null</code> if the activity * is the root activity. */ public final Activity getParent() { ActivityState parentState = getParentState(); if (parentState == null) return null; return parentState.getActivity(); } /** * If needed, queries the database for the child activity lookup keys. */ private final void initChildrenIfNeeded() { if (childKeys != null) return; String[] keyArray = getDatabase().queryActivityChildren(getActivity().getDBKey()); childKeys = new ArrayList<String>(keyArray.length); for (int i = 0; i < keyArray.length; i++) childKeys.add(keyArray[i]); } /** * Returns the number of children. */ public final int getChildCount() { initChildrenIfNeeded(); return childKeys.size(); } /** * Returns the activity-state for the child activity at the specified index. */ public final ActivityState getChildState(int index) { initChildrenIfNeeded(); String childKey = childKeys.get(index); return getActivityState(childKey); } /** * Returns the child activity at the specified index. */ public final IlvActivity getChild(int index) { ActivityState childState = getChildState(index); return childState.getActivity(); } /** * Returns the index of the specified child. */ public final int getChildIndex(String dbKey) { initChildrenIfNeeded(); return childKeys.indexOf(dbKey); } } /** * Initializes the collection of activities of this data model. */ private void initActivities() { rootActivityKey = null; rootActivityQueried = false; activityCache = new HashMap<String, DBROGanttModel.ActivityState>(); } /** * Verify that the specified activity is an instance of <code>Activity</code>. * If not, this method throws an <code>IllegalArgumentException</code>. */ protected final Activity checkActivity(IlvActivity activity) { if (!(activity instanceof Activity)) throw new IllegalArgumentException(activity + " is not a member of " + this); return (Activity) activity; } /** * Returns the activity-state for the specified activity. If the activity is * not already cached, an activity and its associated state object are created * and inserted into the cache. * * @param dbKey * The lookup key for the activity. It is assumed that the key * represents a valid activity in the database. */ protected final ActivityState getActivityState(String dbKey) { ActivityState activityState = activityCache.get(dbKey); if (activityState == null) { Activity a = createActivity(dbKey); activityState = new ActivityState(a); activityCache.put(dbKey, activityState); } return activityState; } /** * Creates a new activity object from the specified lookup key. It is assumed * that the key represents a valid activity in the database. In general, the * activity instance should not query the database immediately when it is * created. Instead, it should lazy query the database when properties are are * first accessed. Subclasses can override this method as needed to create * customized activities. * * @param dbKey * The database lookup key. */ protected Activity createActivity(String dbKey) { Logger.getLogger(logID).log(Level.INFO, "[Model] Creating Activity instance for {0}", dbKey); Activity newActivity = new Activity(dbKey); return newActivity; } /** * Returns whether the specified activity is a member of the data model. * * @param activity * The activity. */ protected synchronized boolean contains(IlvActivity activity) { if (!(activity instanceof Activity)) return false; Activity a = (Activity) activity; String dbKey = a.getDBKey(); ActivityState activityState = activityCache.get(dbKey); if (activityState != null) return true; GanttDBRO.ActivityRecord dbRecord = getDatabase().queryActivity(dbKey); if (dbRecord != null) { a.dbRecord = dbRecord; activityState = new ActivityState(a); activityCache.put(dbKey, activityState); return true; } else { return false; } } /** * Returns the root activity of the data model or <code>null</code> if the * data model contains no activities. */ Override public synchronized IlvActivity getRootActivity() { if (!rootActivityQueried) { rootActivityKey = db.queryRootActivityKey(); rootActivityQueried = true; } if (rootActivityKey == null) return null; ActivityState activityState = getActivityState(rootActivityKey); return activityState.getActivity(); } /** * Sets the root activity of the data model. */ Override public void setRootActivity(IlvActivity root) { } /** * Returns the parent activity of the specified activity or <code>null</code> * if the activity is the root activity of the data model. * * @param activity * The activity. */ Override public synchronized IlvActivity getParentActivity(IlvActivity activity) { Activity a = checkActivity(activity); if (a == getRootActivity()) return null; String id = a.getDBKey(); ActivityState activityState = getActivityState(id); return activityState.getParent(); } /** * Returns the index of the specified activity within its parent activity. If * the activity is the root activity of the data model, then -1 is returned. * * @param activity * The activity. */ Override public synchronized int getParentActivityIndex(IlvActivity activity) { Activity a = checkActivity(activity); if (a == getRootActivity()) return -1; String childKey = a.getDBKey(); ActivityState childState = getActivityState(childKey); ActivityState parentState = childState.getParentState(); return parentState.getChildIndex(childKey); } /** * Returns the number of children of the specified parent activity. * * @param parent * The parent activity. * @return The number of child activities. */ Override public synchronized int getChildActivityCount(IlvActivity parent) { Activity p = checkActivity(parent); ActivityState parentState = getActivityState(p.getDBKey()); return parentState.getChildCount(); } /** * Returns the child of the specified parent activity at index * <code>index</code>. * * @param parent * The parent activity. * @param index * The child index. * @return The child activity at index <code>index</code>. */ Override public synchronized IlvActivity getChildActivity(IlvActivity parent, int index) { Activity p = checkActivity(parent); ActivityState parentState = getActivityState(p.getDBKey()); return parentState.getChild(index); } /** * Returns the index of the specified child in the parent activity's list of * children. * * @param parent * The parent activity. * @param child * The child activity to find the index of. * @return The index of the child activity, or -1 if the activity is not a * child of parent. */ Override public synchronized int getChildActivityIndex(IlvActivity parent, IlvActivity child) { Activity p = checkActivity(parent); Activity c = checkActivity(child); ActivityState parentState = getActivityState(p.getDBKey()); return parentState.getChildIndex(c.getDBKey()); } /** * Adds <i>newActivity</i> as a child of <i>parent</i> activity at the * specified location in its list of child activities. */ Override public void addActivity(IlvActivity newActivity, IlvActivity parent, int index) { } /** * Removes the child activity from <i>parent</i> at the specified location in * its list of child activities. */ Override public void removeActivity(IlvActivity parent, int index) { } /** * Removes the specified child <i>activity</i> from its parent. */ Override public void removeActivity(IlvActivity activity) { } /** * Moves the specified activity from its current location in the tree to a new * location. */ Override public void moveActivity(IlvActivity activity, IlvActivity newParent, int newIndex) { } /** * <code>Activity</code> is an inner class that implements a read-only * activity object. */ public class Activity extends IlvAbstractActivity { private String key; private GanttDBRO.ActivityRecord dbRecord; private IlvTimeInterval interval; // duplicate of start and end in db record /** * Creates a new <code>Activity</code> from the specified database key. * * @param key * The database lookup key. */ protected Activity(String key) { if (key == null) throw new IllegalArgumentException("Null database key"); this.key = key; } /** * Queries the database for the properties of this activity. */ protected void queryPropertiesIfNeeded() { if (dbRecord != null) return; dbRecord = getDatabase().queryActivity(key); interval = new IlvTimeInterval(dbRecord.getStart(), dbRecord.getEnd()); } /** * Returns the database key of this activity. */ public String getDBKey() { return key; } /** * Sets the data model to which the activity and all its children belong. */ Override public void setGanttModelImpl(IlvGanttModel model) { } /** * Returns the name of the activity. */ Override public String getName() { queryPropertiesIfNeeded(); return dbRecord.getName(); } /** * Sets the name of the activity. */ Override public void setName(String name) { } /** * Returns the activity ID. */ Override public String getID() { queryPropertiesIfNeeded(); return dbRecord.getID(); } /** * Sets the activity ID. */ Override public void setID(String id) { } /** * Returns the time interval of the activity. */ Override public IlvTimeInterval getTimeInterval() { queryPropertiesIfNeeded(); return interval; } /** * Sets the time interval of the activity. */ Override public void setTimeInterval(IlvTimeInterval interval) { } /** * Returns a parameter string that represents the state of this activity. */ Override protected String paramString() { StringBuffer buf = new StringBuffer("key="); buf.append(getDBKey()); buf.append(','); buf.append(super.paramString()); return buf.toString(); } } // ========================================= // Resources // ========================================= /** * The database lookup key of the root resource. */ private String rootResourceKey; /** * Indicates whether <code>rootResourceKey</code> is valid. */ private boolean rootResourceQueried; /** * Cached {@link ResourceState}s, indexed by their database lookup key. */ private Map<String, ResourceState> resourceCache; /** * The <code>ResourceState</code> inner class. Each instance maintains state * info on a resource's hierarchical relationships. */ class ResourceState { /** * The resource for this state info. */ Resource resource; /** * Indicates whether the parent ID for the resource has been queried. If * <code>true</code>, then the value in <code>parentKey</code> is valid. */ boolean parentQueried; /** * The database lookup key of the resource's parent. If <code>null</code>, * then the resource is the root resource and has no parent. This value is * only valid if <code>parentQueried</code> is <code>true</code>. */ String parentKey; /** * The list of lookup keys for the resource's children. If the child keys * have not been queried yet, then <code>childKeys</code> will be * <code>null</code>. */ List<String> childKeys; /** * Creates a <code>ResourceState</code> for the specified resource. */ public ResourceState(Resource resource) { this.resource = resource; } /** * Returns the resource. */ public final Resource getResource() { return resource; } /** * Returns the resource-state for the parent of the resource or * <code>null</code> if the resource is the root resource. */ public final ResourceState getParentState() { if (!parentQueried) { Resource resource = getResource(); if (resource == getRootResource()) parentKey = null; else parentKey = getDatabase().queryResourceParent(getResource().getDBKey()); parentQueried = true; } if (parentKey == null) return null; return getResourceState(parentKey); } /** * Returns the parent of the resource or <code>null</code> if the resource * is the root resource. */ public final Resource getParent() { ResourceState parentState = getParentState(); if (parentState == null) return null; return parentState.getResource(); } /** * If needed, queries the database for the child resource lookup keys. */ private final void initChildrenIfNeeded() { if (childKeys != null) return; String[] keyArray = getDatabase().queryResourceChildren(getResource().getDBKey()); childKeys = new ArrayList<String>(keyArray.length); for (int i = 0; i < keyArray.length; i++) childKeys.add(keyArray[i]); } /** * Returns the number of children. */ public final int getChildCount() { initChildrenIfNeeded(); return childKeys.size(); } /** * Returns the resource-state for the child resource at the specified index. */ public final ResourceState getChildState(int index) { initChildrenIfNeeded(); String childKey = childKeys.get(index); return getResourceState(childKey); } /** * Returns the child resource at the specified index. */ public final IlvResource getChild(int index) { ResourceState childState = getChildState(index); return childState.getResource(); } /** * Returns the index of the specified child. */ public final int getChildIndex(String dbKey) { initChildrenIfNeeded(); return childKeys.indexOf(dbKey); } } /** * Initializes the collection of resources of this data model. */ private void initResources() { rootResourceKey = null; rootResourceQueried = false; resourceCache = new HashMap<String, DBROGanttModel.ResourceState>(); } /** * Verify that the specified resource is an instance of <code>Resource</code>. * If not, this method throws an <code>IllegalArgumentException</code>. */ protected final Resource checkResource(IlvResource resource) { if (!(resource instanceof Resource)) throw new IllegalArgumentException(resource + " is not a member of " + this); return (Resource) resource; } /** * Returns the resource-state for the specified resource. If the resource is * not already cached, a resource and its associated state object are created * and inserted into the cache. * * @param dbKey * The lookup key for the resource. It is assumed that the key * represents a valid resource in the database. */ protected final ResourceState getResourceState(String dbKey) { ResourceState resourceState = resourceCache.get(dbKey); if (resourceState == null) { Resource r = createResource(dbKey); resourceState = new ResourceState(r); resourceCache.put(dbKey, resourceState); } return resourceState; } /** * Creates a new resource object from the specified lookup key. It is assumed * that the key represents a valid resource in the database. In general, the * resource instance should not query the database immediately when it is * created. Instead, it should lazy query the database when properties are are * first accessed. Subclasses can override this method as needed to create * customized resources. * * @param dbKey * The database lookup key. */ protected Resource createResource(String dbKey) { Logger.getLogger(logID).log(Level.INFO, "[Model] Creating Resource instance for {0}", dbKey); Resource newResource = new Resource(dbKey); return newResource; } /** * Returns whether the specified resource is a member of the data model. * * @param resource * The resource. */ protected synchronized boolean contains(IlvResource resource) { if (!(resource instanceof Resource)) return false; Resource r = (Resource) resource; String dbKey = r.getDBKey(); ResourceState resourceState = resourceCache.get(dbKey); if (resourceState != null) return true; GanttDBRO.ResourceRecord dbRecord = getDatabase().queryResource(dbKey); if (dbRecord != null) { r.dbRecord = dbRecord; resourceState = new ResourceState(r); resourceCache.put(dbKey, resourceState); return true; } else { return false; } } /** * Returns the root resource of the data model or <code>null</code> if the * data model contains no resources. */ Override public synchronized IlvResource getRootResource() { if (!rootResourceQueried) { rootResourceKey = db.queryRootResourceKey(); rootResourceQueried = true; } if (rootResourceKey == null) return null; ResourceState resourceState = getResourceState(rootResourceKey); return resourceState.getResource(); } /** * Sets the root resource of the data model. */ Override public void setRootResource(IlvResource root) { } /** * Returns the parent resource of the specified resource or <code>null</code> * if the resource is the root resource of the data model. * * @param resource * The resource. */ Override public synchronized IlvResource getParentResource(IlvResource resource) { Resource r = checkResource(resource); if (r == getRootResource()) return null; String id = r.getDBKey(); ResourceState resourceState = getResourceState(id); return resourceState.getParent(); } /** * Returns the index of the specified resource within its parent resource. If * the resource is the root resource of the data model, then -1 is returned. * * @param resource * The resource. */ Override public synchronized int getParentResourceIndex(IlvResource resource) { Resource r = checkResource(resource); if (r == getRootResource()) return -1; String childKey = r.getDBKey(); ResourceState childState = getResourceState(childKey); ResourceState parentState = childState.getParentState(); return parentState.getChildIndex(childKey); } /** * Returns the number of children of the specified parent resource. * * @param parent * The parent resource. * @return The number of child resources. */ Override public synchronized int getChildResourceCount(IlvResource parent) { Resource p = checkResource(parent); ResourceState parentState = getResourceState(p.getDBKey()); return parentState.getChildCount(); } /** * Returns the child of the specified parent resource at index * <code>index</code>. * * @param parent * The parent resource. * @param index * The child index. * @return The child resource at index <code>index</code>. */ Override public synchronized IlvResource getChildResource(IlvResource parent, int index) { Resource p = checkResource(parent); ResourceState parentState = getResourceState(p.getDBKey()); return parentState.getChild(index); } /** * Returns the index of the specified child in the parent resource's list of * children. * * @param parent * The parent resource. * @param child * The child resource to find the index of. * @return The index of the child resource, or -1 if the resource is not a * child of parent. */ Override public synchronized int getChildResourceIndex(IlvResource parent, IlvResource child) { Resource p = checkResource(parent); Resource c = checkResource(child); ResourceState parentState = getResourceState(p.getDBKey()); return parentState.getChildIndex(c.getDBKey()); } /** * Adds <i>newResource</i> as a child of <i>parent</i> resource at the * specified location in its list of child resources. */ Override public void addResource(IlvResource newResource, IlvResource parent, int index) { } /** * Removes the child resource from <i>parent</i> at the specified location in * its list of child resources. */ Override public void removeResource(IlvResource parent, int index) { } /** * Removes the specified child <i>resource</i> from its parent. */ Override public void removeResource(IlvResource resource) { } /** * Moves the specified resource from its current location in the tree to a new * location. */ Override public void moveResource(IlvResource resource, IlvResource newParent, int newIndex) { } /** * <code>Resource</code> is an inner class that implements a read-only * resource object. */ public class Resource extends IlvAbstractResource { private String key; private GanttDBRO.ResourceRecord dbRecord; /** * Creates a new <code>Resource</code> from the specified database key. * * @param key * The database lookup key. */ protected Resource(String key) { if (key == null) throw new IllegalArgumentException("Null database key"); this.key = key; } /** * Queries the database for the properties of this resource. */ protected void queryPropertiesIfNeeded() { if (dbRecord != null) return; dbRecord = getDatabase().queryResource(key); } /** * Returns the database key of this resource. */ public String getDBKey() { return key; } /** * Sets the data model to which the resource and all its children belong. */ Override public void setGanttModelImpl(IlvGanttModel model) { } /** * Returns the name of the resource. */ Override public String getName() { queryPropertiesIfNeeded(); return dbRecord.getName(); } /** * Sets the name of the resource. */ Override public void setName(String name) { } /** * Returns the resource ID. */ Override public String getID() { queryPropertiesIfNeeded(); return dbRecord.getID(); } /** * Sets the resource ID. */ Override public void setID(String id) { } /** * Returns the resource's quantity. */ Override public float getQuantity() { queryPropertiesIfNeeded(); return dbRecord.getQuantity(); } /** * Sets the resource's quantity. */ Override public void setQuantity(float x) { } /** * Returns a parameter string that represents the state of this resource. */ Override protected String paramString() { StringBuffer buf = new StringBuffer("key="); buf.append(getDBKey()); buf.append(','); buf.append(super.paramString()); return buf.toString(); } } // ========================================= // Constraints // ========================================= /** * Cached constraints, indexed by their database lookup key. */ private Map<String, Constraint> constraintCache; /** * Initializes the collection of constraints of this data model. */ private void initConstraints() { constraintCache = new HashMap<String, DBROGanttModel.Constraint>(); } /** * Returns the constraint specified by its lookup key. If the constraint is * not already cached, a constraint object is created and inserted into the * cache. * * @param dbKey * The lookup key for the constraint. It is assumed that the key * represents a valid constraint in the database. */ protected final IlvConstraint getConstraint(String dbKey) { Constraint constraint = constraintCache.get(dbKey); if (constraint == null) { constraint = createConstraint(dbKey); constraintCache.put(dbKey, constraint); } return constraint; } /** * Creates a new constraint object from the specified lookup key. It is * assumed that the key represents a valid constraint in the database. In * general, the constraint instance should not query the database immediately * when it is created. Instead, it should lazy query the database when * properties are first accessed. Subclasses can override this method as * needed to create customized constraints. * * @param dbKey * The database lookup key. */ protected Constraint createConstraint(String dbKey) { Logger.getLogger(logID).log(Level.INFO, "[Model] Creating Constraint instance for {0}", dbKey); Constraint newConstraint = new Constraint(dbKey); return newConstraint; } /** * Returns whether the specified constraint is a member of the data model. * * @param constraint * The constraint. */ Override public synchronized boolean contains(IlvConstraint constraint) { if (!(constraint instanceof Constraint)) return false; Constraint c = (Constraint) constraint; String dbKey = c.getDBKey(); if (constraintCache.get(dbKey) != null) return true; GanttDBRO.ConstraintRecord dbRecord = getDatabase().queryConstraint(dbKey); if (dbRecord != null) { c.dbRecord = dbRecord; constraintCache.put(dbKey, c); return true; } else { return false; } } /** * Returns an iterator that will traverse over the constraints specified by an * array of database keys. */ protected Iterator<IlvConstraint> constraintIterator(final String[] constraintKeys) { return new Iterator<IlvConstraint>() { Iterator<String> keyIterator = Arrays.asList(constraintKeys).iterator(); Override public boolean hasNext() { return keyIterator.hasNext(); } Override public IlvConstraint next() { String dbKey = keyIterator.next(); return getConstraint(dbKey); } Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Return an iterator over the constraints in the data model. */ Override public Iterator<IlvConstraint> constraintIterator() { return constraintIterator(db.queryConstraints()); } /** * Returns an iterator over the constraints in the data model that have the * specified activity as their source or <i>from</i> activity. */ Override public Iterator<IlvConstraint> constraintIteratorFromActivity(IlvActivity fromActivity) { return constraintIterator(db.queryConstraintsFromActivity(((Activity) fromActivity).getDBKey())); } /** * Returns an iterator over the constraints in the data model that have the * specified activity as their target or <i>to</i> activity. */ Override public Iterator<IlvConstraint> constraintIteratorToActivity(IlvActivity toActivity) { return constraintIterator(db.queryConstraintsToActivity(((Activity) toActivity).getDBKey())); } /** * Adds a constraint to the data model. */ Override public void addConstraint(IlvConstraint newConstraint) { } /** * Removes the specified <i>constraint</i> from the data model. */ Override public void removeConstraint(IlvConstraint constraint) { } /** * <code>Constraint</code> is an inner class that implements a read-only * constraint object. */ public class Constraint extends IlvAbstractConstraint { private String key; private GanttDBRO.ConstraintRecord dbRecord; /** * Creates a new <code>Constraint</code> from the specified database key. * * @param key * The database lookup key. */ private Constraint(String key) { if (key == null) throw new IllegalArgumentException("Null database key"); this.key = key; } /** * Queries the database for the properties of this constraint. */ protected void queryPropertiesIfNeeded() { if (dbRecord != null) return; dbRecord = getDatabase().queryConstraint(key); } /** * Returns this constraint's database key. */ public String getDBKey() { return key; } /** * Sets the data model to which the resource belongs. */ Override public void setGanttModelImpl(IlvGanttModel model) { } /** * Returns the type of this constraint. */ Override public IlvConstraintType getType() { queryPropertiesIfNeeded(); return dbRecord.getType(); } /** * Sets the type of this constraint. */ Override public void setType(IlvConstraintType type) { } /** * Returns the source or <i>From</i> activity for this constraint. */ Override public IlvActivity getFromActivity() { queryPropertiesIfNeeded(); ActivityState activityState = getActivityState(dbRecord.getFromActivityKey()); return activityState.getActivity(); } /** * Returns the target or <i>To</i> activity for this constraint. */ Override public IlvActivity getToActivity() { queryPropertiesIfNeeded(); ActivityState activityState = getActivityState(dbRecord.getToActivityKey()); return activityState.getActivity(); } } // ========================================= // Reservations // ========================================= /** * Cached reservations, indexed by their database lookup key. */ private Map<String, Reservation> reservationCache; /** * Initializes the collection of reservations of this data model. */ private void initReservations() { reservationCache = new HashMap<String, DBROGanttModel.Reservation>(); } /** * Returns the reservation specified by its lookup key. If the reservation is * not already cached, a reservation object is created and inserted into the * cache. * * @param dbKey * The lookup key for the reservation. It is assumed that the key * represents a valid reservation in the database. */ protected final IlvReservation getReservation(String dbKey) { Reservation reservation = reservationCache.get(dbKey); if (reservation == null) { reservation = createReservation(dbKey); reservationCache.put(dbKey, reservation); } return reservation; } /** * Creates a new reservation object from the specified lookup key. It is * assumed that the key represents a valid reservation in the database. In * general, the reservation instance should not query the database immediately * when it is created. Instead, it should lazy query the database when * properties are first accessed. Subclasses can override this method as * needed to create customized reservations. * * @param dbKey * The database lookup key. */ protected Reservation createReservation(String dbKey) { Logger.getLogger(logID).log(Level.INFO, "[Model] Creating Reservation instance for {0}", dbKey); Reservation newReservation = new Reservation(dbKey); return newReservation; } /** * Returns whether the specified reservation is a member of the data model. * * @param reservation * The reservation. */ Override public synchronized boolean contains(IlvReservation reservation) { if (!(reservation instanceof Reservation)) return false; Reservation r = (Reservation) reservation; String dbKey = r.getDBKey(); if (reservationCache.get(dbKey) != null) return true; GanttDBRO.ReservationRecord dbRecord = getDatabase().queryReservation(dbKey); if (dbRecord != null) { r.dbRecord = dbRecord; reservationCache.put(dbKey, r); return true; } else { return false; } } /** * Returns an iterator that will traverse over the reservations specified by * an array of database keys. */ protected Iterator<IlvReservation> reservationIterator(final String[] reservationKeys) { return new Iterator<IlvReservation>() { Iterator<String> keyIterator = Arrays.asList(reservationKeys).iterator(); Override public boolean hasNext() { return keyIterator.hasNext(); } Override public IlvReservation next() { String dbKey = keyIterator.next(); return getReservation(dbKey); } Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Returns an iterator over all of the reservations in the data model. */ Override public Iterator<IlvReservation> reservationIterator() { return reservationIterator(db.queryReservations()); } /** * Returns an iterator over all of the reservations in the data model that are * associated with the specified activity. */ Override public Iterator<IlvReservation> reservationIterator(IlvActivity activity) { return reservationIterator(db.queryReservationsForActivity(((Activity) activity).getDBKey())); } /** * Returns an iterator over all of the reservations in the data model that are * associated with the specified resource. */ Override public Iterator<IlvReservation> reservationIterator(IlvResource resource) { return reservationIterator(db.queryReservationsForResource(((Resource) resource).getDBKey())); } /** * Returns an iterator over all of the reservations in the data model that are * associated with the specified resource and where the assigned activity * intersects the specified time interval. */ Override public Iterator<IlvReservation> reservationIterator(IlvResource resource, IlvTimeInterval interval) { return reservationIterator( db.queryReservationsForResource(((Resource) resource).getDBKey(), interval.getStart(), interval.getEnd())); } /** * Adds a reservation to the data model. */ Override public void addReservation(IlvReservation newReservation) { } /** * Removes the specified <i>reservation</i> from the data model. */ Override public void removeReservation(IlvReservation reservation) { } /** * <code>Reservation</code> is an inner class that implements a read-only * reservation object. */ public class Reservation extends IlvAbstractReservation { private String key; private GanttDBRO.ReservationRecord dbRecord; /** * Creates a new <code>Reservation</code> from the specified database key. * * @param key * The database lookup key. */ private Reservation(String key) { if (key == null) throw new IllegalArgumentException("Null database key"); this.key = key; } /** * Queries the database for the properties of this reservation. */ protected void queryPropertiesIfNeeded() { if (dbRecord != null) return; dbRecord = getDatabase().queryReservation(key); } /** * Returns this reservation's database key. */ public String getDBKey() { return key; } /** * Sets the data model to which the resource belongs. */ Override public void setGanttModelImpl(IlvGanttModel model) { } /** * Returns the resource for this reservation. */ Override public IlvResource getResource() { queryPropertiesIfNeeded(); ResourceState resourceState = getResourceState(dbRecord.getResourceKey()); return resourceState.getResource(); } /** * Sets the resource for this reservation. */ Override public void setResource(IlvResource resource) { } /** * Returns the activity for this reservation. */ Override public IlvActivity getActivity() { queryPropertiesIfNeeded(); ActivityState activityState = getActivityState(dbRecord.getActivityKey()); return activityState.getActivity(); } /** * Returns a parameter string that represents the state of this reservation. */ Override protected String paramString() { StringBuffer buf = new StringBuffer("key="); buf.append(getDBKey()); buf.append(','); buf.append(super.paramString()); return buf.toString(); } } }