Version 0: Rendering a CellSet

Wednesday, December 07, 2011
You can download the source code for the v0 component library and the sample web project here.

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>&nbsp;</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}