Adding Drill Buttons
Let's start modifying the contents in the <olap:cellSetAxisforAxis="rows"/>. I'll add a <h:commandButton/> to let the user drill/undrill a member in the cellset table. This button must be rendered only if the member has children, and will show a '-' if the member is already drilled or a '+' otherwise. This is the corresponding Facelet markup.
<span
style="padding-left: #{m.member.depth}ex">
<h:commandButton
rendered="#{m.member.childMemberCount > 0}"
value="#{olapSample.isDrilled(component,m.position) ? '-' : '+'}"
/>
action="#{olapSample.toggleDrill(component,m.position)}"
<h:outputText
value="#{m.member.caption}"
/>
</span>
To support this markup we'll add two methods to our managed bean:
- boolean isDrilled(UIComponent source, List<Member> position);This method receives a component and a positioned member, and returns a boolean value indicating if that member is drilled or not.
- boolean toggleDrill(UIComponent source, List<Member> position);This method receives a component and a positioned member, and modifies the current query to change the drill status of the positioned member.
This is the code snippet for toggleDrill
public
void toggleDrill(UIComponent c, List<Member> position)
throws OlapException, SQLException {
// Find the UICellSetAxis within the ancestors of 'c'
while (c != null && !(c instanceof UICellSetAxis)) {
c = c.getParent();
}
if (c == null)
return;
// Get the query axis based on the UICellSetAxis information
final UICellSetAxis axisComponent = (UICellSetAxis) c;
QueryAxis queryAxis = getQuery().getAxis(
axisComponent.getCellSetAxis().getAxisOrdinal());
// Toggle drill
Member[] members = position.toArray(new Member[position.size()]);
if (queryAxis.isDrilled(members))
queryAxis.undrill(members);
else
queryAxis.drill(members);
// Invalidate the CellSet caché
cs = null;
}
The isDrilled method has a similar structure.
The only point remaining to be explained is the getQuery call, it's related to the query state saving strategy.
Query State Saving Strategy
The OlapSample managed bean is a request-scoped bean, so it doesn't keep state between requests. But, to make the table functional, we need to keep the drill state of current query between requests. So I'll save the query state in the ViewState. This would be the code:Map<String, Object> viewState = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
// Put the query in the View State
viewState.put("SavedQuery", query);
// Get the query from the View State
query = (Query)viewState.get("SavedQuery");
Unfortunately it won't work… Query instances cannot be serialized (mainly because they contain references to database connections), so they can't be added to the ViewState; I need helper class to create a serializable object from a Query instance and to reconstruct the original query from that object. That class, QuerySaver, has to static public methods:
- Object saveQuery(Query q)Generates the serializable object from the query. It will be an array of objects containing the names of the query, the source cube, axis dimensions, drilled members, etc.
- Query restoreQuery(OlapConnection cn, Object state)This method receives an object produced by saveQuery and an OlapConnection and recreates the original query.
private Query getQuery() throws OlapException {
if (query != null)
return
query;
Map<String, Object> viewMap = FacesContext.getCurrentInstance()
.getViewRoot().getViewMap();
Object savedQuery = viewMap.get("SavedQuery");
if (savedQuery == null) {
// There is no saved query, so we create the query used to
// show our initial CellSet.
query = initQuery();
} else {
query = QuerySaver.restoreQuery(getConnection(), savedQuery);
}
return
query;
}
I'm going to save the query state just before rendering the cellset. To make this I'll use attach a listener method to the preRenderComponent event for the <olap:cellSetTable>
<olap:cellSetTable
value="#{olapSample2.sampleCellSet}" …>
<f:event
type="preRenderComponent"
listener="#{olapSample.saveQuery}"/>
…
<olap:cellSetTable/>
No comments :
Post a Comment