Drilling Down Into Gantt Filter Models - Part I
In this article, I will take you on a tour of the filter data models that are provided with ILOG JViews Gantt. Then we will go deeper as I show how to implement a filter model that provides data drill-down capabilities.
Gantt Filter Model Overview
ILOG JViews Gantt provides several filtering data model implementations. All filter models act as wrappers around an underlying data model that contains the “real data”. The purpose of a filter model is to provide an alternative subset or perspective on the data, while eliminating the overhead of data duplication. At the base of the hierarchy is the IlvFilterGanttModel class which is a thin wrapper class that, in fact, provides no filtering at all! All of the IlvFilterGanttModel methods simply delegate to the underlying model as their default behavior. In order to create an actual filter, it is necessary to subclass IlvFilterGanttModel and override appropriate methods to achieve the desired behavior. The IlvBasicFilterGanttModel subclass, which is built into the JViews Gantt library, does precisely this. It allows to set an arbitrary filtering function for activities and/or resources, which results in a subset view of the underlying data model. Usage of IlvBasicFilterGanttModel is illustrated in the Filtering Activities sample.
ILOG JViews Gantt provides another filter model in the Sorting Activities sample as source code. This is the SortGanttModel class, which generates an alternative ordering of activities in the data model. The ordering is controlled by a customizable comparator function. The Sorting Activities sample shows the SortGanttModel being used to view activities ordered by name or by start time. However, the concept is completely general and can be extended to order activities by any criteria for which you can write a function. Notice though, that the SortGanttModel does not provide a subset view of the underlying data model, it merely provides an alternate perspective.
Drill Down Concepts
Now, we will look at the design of a filter model that allows us to drill-down and view only a portion of the activity hierarchy tree. The filter model will show us a subtree of the underlying data that is rooted at an arbitrary activity. Being very clever, we will call this implementation a SubtreeGanttModel. When designing a filter model (or any data model for that matter!) it is critical to ensure that client software “sees” a consistent view of the model state through the model’s api’s. For example, if a filter model provides an alternate perspective on the data, it is mandatory that all of the filter model’s methods provide the same perspective. The events fired by a data model are also part of its public api. All event listeners on the filter model must receive events that are also consistent with the model’s perspective of the data. In summary, there are three aspects to a model’s api that must be consistent among themselves:
- Accessor methods must provide a consistent perspective of the data.
- Mutator methods must modify the model in a manner that is consistent with the data perspective.
- Events fired by the model must be consistent with the data perspective.
Getting Started With Accessing Activities
Let’s start implementing our SubtreeGanttModel by subclassing IlvFilterGanttModel and overriding the methods necessary to access the subtree of activities. For now, we will ignore handling of events and other details that we will get to later. First, we create the basic class skeleton:
public class SubtreeGanttModel extends IlvFilterGanttModel {
// The root activity of the subtree.
private IlvActivity rootActivity;
public SubtreeGanttModel() {
this(null);
}
public SubtreeGanttModel(IlvGanttModel filteredModel) {
super(filteredModel);
}
}
The rootActivity field will hold the root of the subtree perspective. If the value is null, then the SubtreeGanttModel will appear to contain no activities. This is the same behavior as any Gantt data model that has a null root activity. Otherwise, the rootActivity field must be an activity contained in the underlying filteredModel. In this case, the SubtreeGanttModel will provide a subtree perspective that is based at this activity. Obviously, we must override the getRootActivity method like this:
public IlvActivity getRootActivity() {
return rootActivity;
}
We will address overriding the setRootActivity method later. Next, let’s handle the contains(IlvHierarchyNode) method. This method is used to query whether an activity or a resource is a member of the data model. We are only modifying the perspective of the activities, so let’s factor this out into a separate helper method:
public boolean contains(IlvHierarchyNode activityOrResource) {
if (activityOrResource instanceof IlvActivity) {
return containsActivity((IlvActivity) activityOrResource);
} else {
return super.contains(activityOrResource);
}
}
private boolean containsActivity(IlvActivity activity) {
// Check that the activity is a member of the filtered data model.
IlvGanttModel filteredModel = getFilteredModel();
if (filteredModel == null || !filteredModel.contains(activity)) {
return false;
}
IlvActivity root = getRootActivity();
if (root == null) {
return false;
}
return activity == root
// Use filtered model as slight optimization
|| IlvGanttModelUtil.isDescendant(filteredModel, root, activity);
}
In this way, we only return true if the activity parameter is a member of the subtree defined by the rootActivity. We now need to override the two methods that traverse up the activity tree, from children to parents. These methods must be rewritten so that the traversal stops at the root of the subtree:
public IlvActivity getParentActivity(IlvActivity activity) {
if (activity == getRootActivity()) {
return null;
} else {
return super.getParentActivity(activity);
}
}
public int getParentActivityIndex(IlvActivity activity) {
if (activity == getRootActivity()) {
return -1;
} else {
return super.getParentActivityIndex(activity);
}
}
We do not need to override the methods that traverse down the activity tree because this behavior is unaltered in the SubtreeGanttModel. In the next part of this article series, I will detail how filtering of activities affects the filtering of constraints and reservations. We will also see how to finally write that crucial setRootActivity method. Stay tuned!







