The focus for this initial version is about rendering an arbitrary cellset result. In the previous post I proposed a component structure with a main UICellSet component acting as a container of two UICellSetAxis component instances, one for each CellSetAxis, and a UICellSetCells component to handle the data cells.
I will use the delegated implementation rendering model for those components. So,
following the pattern used in the JSF API, I’ll extend those component classes
to handle HTML specific properties and methods. The resulting classes will be
·
HtmlCellSet, extending UICellSet
·
HtmlCellSetAxis,
extending UICellSetAxis, and
·
HtmlCellSetCells, extending UICellSetCells
These classes will have an associated set
of renderer classes: HtmlCellSetRenderer, HtmlCellSetCellsRenderer, HtmlColumnsAxisRenderer and HtmlRowsAxisRenderer. The last two renderers both render a HtmlCellSetAxis, but are specialized
to render a columns (ordinal 0) axis and a rows (ordinal 1) axis. This is
achived overriding the HtmlCellSetAxis.getRenderType() method to return a different type of renderer based in the type of
CellSetAxis its bound to
@Override
public String
getRendererType() {
if (isFor(TableArea.columnAxis))
return "es.cgalesanco.faces.olap4j.columnsCellSetAxis";
else
return "es.cgalesanco.faces.olap4j.rowsCellSetAxis";
}
HtmlCellSetRenderer, drives the global rendering of a CellSet.
·
encodeBegin() renders the starting <table> element, the upper-left corner
cell, and delegates (indirectly) on HtmlColumnsAxisRenderer the rendering of the <tr>
tags for the rows containing the columns axis. In
the last row of the columns axis, it delegates on HtmlRowsAxisRenderer to render the
header cells for the rows axis.
·
encodeChildren() renders the cell set rows containing the rows axis and the data
cells. Renders the <tr> elements and computes the first cell of the rows axis to be
rendered in the row (taking into account previous cells rows spans); delegates
the rendering of the cells on HtmlRowsAxisRenderer
and HTmlCellSetCellsRenderer.
The following colored HTML shows which
renderers renders which tag:
<table>
<colgroup>
<col/>
</colgroup>
<tr>
<th> </th><th colspan=”2”>Measures</th>
</tr>
<tr>
<th>Store</th><th>Unit
Sales</th></th>Store Cost</th>
</tr>
<tr>
<th>All Stores</th><td>266,733</td><td>225,627.23</td>
</tr>
<tr>
<th>USA</th><td><td>266,733</td><td>225,627.23</td>
</tr>
</table>
HtmlCellSetRenderer
|
|
HtmlRowsAxisRenderer
|
|
HtmlColumnsAxisRenderer
|
|
HtmlCellSetCellsRenderer
|
Styling the CellSet
This distribution of responsibilities
rendering the table is used to style the table using the properties of the HTML
components. The properties of these components are
HtmlCellSet
·
styleClass. The HTML style class passed through to the class attribute
of the main <table> element.
·
cornerClass. The HTML style class passed through to the <th> element
rendering the corner cell.
·
alternateClass. The HTML style class passed through to the <tr> elements
starting the odd rows rendered by the HtmlCellSetRenderer (the gray rows
in the previous colored HTML)
HtmlCellSetAxis
·
styleClass. The HTML style class passed through to the <tr>
element rendered by the HtmlColumnsAxisRenderer or the <col> element rendered by the HtmlRowsAxisRenderer.
·
headerClass. The HTML style class passed through to the <th>
elements containing the hierarchy headers.
Wrapping It Up
First, the @ManagedBean backing our sample CellSetTable;
just change the getConnection() method to fit your olap4j provider and connection string. Caching
the resulting CellSet is important, as the method getSampleCellSet() can be invoked
repeatedly within the faces components.
@ManagedBean
public class OlapSample {
private OlapConnection cn;
private CellSet cs;
public OlapConnection
getConnection() throws SQLException {
if ( cn != null )
return cn;
try {
Class.forName("mondrian.olap4j.MondrianOlap4jDriver");
} catch(ClassNotFoundException
ex) {
throw new
RuntimeException(ex);
}
Connection jdbcCn =
DriverManager.getConnection("jdbc:mondrian:"
+ "JdbcDrivers=com.mysql.jdbc.Driver;"
+ "Jdbc=jdbc:mysql://localhost/foodmart;"
+ "JdbcUser=root;JdbcPassword=root;"
+ "Catalog=file:/users/cesar/FoodMart.xml");
return cn = jdbcCn.unwrap(OlapConnection.class);
}
@PreDestroy
public void tearDown() throws SQLException {
if ( cn != null )
cn.close();
}
public CellSet
getSampleCellSet() throws ClassNotFoundException, SQLException {
if ( cs != null )
return cs;
OlapConnection cn = getConnection();
return cs =
cn.createStatement().executeOlapQuery(
"SELECT " +
"
CrossJoin([Gender].AllMembers,[Measures].AllMembers) ON COLUMNS," +
" NON EMPTY
CrossJoin([Store].AllMembers,[Store Type].AllMembers) ON ROWS " +
"FROM Sales");
}
}
And the Facelets XHTML page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:olap="http://cgalesanco.es/faces/olap4j">
<h:head>
<title>First
CellSetTable</title>
<style type="text/css">
… see below …
</style>
</h:head>
<h:body>
<olap:cellSetTable value="#{olapSample.sampleCellSet}"
styleClass="cellSet"
cornerClass="cellSetCorner"
alternateClass="alternate">
<olap:cellSetAxis forAxis="columns"
var="m"
styleClass="columnsAxisClass"
headerClass="columnsAxisHeaderClass">
<f:facet name="header">
<h:outputText value="#{m.hierarchy.caption}"
/>
</f:facet>
<h:outputText value="#{m.member.caption}"/>
</olap:cellSetAxis>
<olap:cellSetAxis forAxis="rows"
var="m"
styleClass="rowsAxisClass"
headerClass="rowsAxisHeaderClass">
<f:facet name="header">
<h:outputText value="#{m.hierarchy.caption}"/>
</f:facet>
<!-- Indents the member name based on its depth -->
<h:outputText value="#{m.member.caption}"
style="padding-left:#{m.member.depth}ex"/>
</olap:cellSetAxis>
<olap:cellSetCells var="cell">
<h:outputText value="#{cell.cell.formattedValue}"/>
</olap:cellSetCells>
</olap:cellSetTable>
</h:body>
</html>
It has the structure discussed in my
previous post; I’ve added the class attributes to allow styling and you can see
how I’ve implemented member indentation in the rows axis using the Member.getDepth()method.
And this is the CSS styles I’ve used to
render the table. They pretend to be as pedagogical as possible, so I beg your
pardon about the color scheme.
body { font-family:Verdana,Helvetica; font-size:small }
/* Every header cell will be aligned on the left and top */
.cellSet th
{ text-align:left;white-space:nowrap;vertical-align:top;
}
/* Every data cell will be aligned on the right */
.cellSet td
{ text-align:right;white-space:nowrap
}
/* Sets background color for rowsAxis cells */
.cellSet col.rowsAxisClass
{ background-color:red }
/* Sets background color for columnsAxis cells */
.cellSet tr.columnsAxisClass
{ background-color:blue;color:white; }
/* Overrides colors for hierarchy header cells within the
columnsAxis */
.cellSet th.columnsAxisHeaderClass
{ background-color:darkblue; color:white; }
/* Overrides colors for hierarchy header cells within the
rowsAxis */
.cellSet th.rowsAxisHeaderClass
{ background-color:maroon; color:white }
/* Sets style for the upper-left corner empty cell */
.cellSet th.cellSetCorner
{ background-color:yellow; }
/* Sets style for alternating rowsAxis background color
*/
.cellSet tr.alternate
th {background-color:darksalmon}
/* Sets style for alternating data cells background color
*/
.cellSet tr.alternate
td {background-color:gainsboro}
No comments :
Post a Comment