Break Group Formatting in a Table - Part 2

In part 1 of this series I discussed the use of the EL map mechanism as a way that we could fake a function call to manage the logic of the Break Group value display.  In this article I wanted to discuss an alternative which is to actually extend Expression Language and add a custom function to do the job in a "proper" way. 

In doing this I've taken the opportunity to make the code more flexible and generic than the Map example by allowing breaking at multiple levels and breaking in multiple tables in the same view. All in all this is a much cleaner and really simpler solution than that covered in Part 1.

Defining the Function

The EL function that I'm using here is defined in a static method within a class in the project.  It is of course something that could be easlily bundled up into a re-usable JAR but in this case I've done everthing in the ViewController project. Note that I'm using 11.1.2 here and a facelets based page, but this process is essentially the same with 11.1.1.n and JSP(X) based pages.

Here's the function class. It just defines the single static method to do the check.  Notice that this function returns a Boolean to indicate a match with the previous value, but as arguments it takes the value to compare and a key.  This key is an arbitrary String which will allow us to manage several parallel compares. For example if we wanted two break group tables on the same page we would use a unique key value for each. Likewise if you wanted to have more than one break column in a single table you just need to give each a unique identified for this value.  Secondly I've genericized the code to use Object as the type of the compare value so you should be able to break on any attribute type. 

package oracle.demo.breakgroup.el;

import java.util.HashMap;
import java.util.Map;
import oracle.adf.view.rich.context.AdfFacesContext;

public final class BreakGroupFunctions {

  //Key used to store the map we use in ViewScope
  private final static String BREAK_GROUP_STORE = "BREAK GROUP STORE";
  /**
   * EL function to compare a suuplied value with the previous value checked for the same key
   * @param compareKey the key of the value to be checked - usually unique identifies a tabel in the UI
   * @param compareValue the value to compare with the previous value supplied for this key
   * @return True if matches the previously checked value for this key
   */ 
  public static Boolean compareWithLastValue(String compareKey, Object compareValue ){
    Boolean repeatedValue = false;
    AdfFacesContext actx = AdfFacesContext.getCurrentInstance();
    Map viewScopeMap = actx.getViewScope();
    Map bgs;
    if (viewScopeMap.containsKey(BREAK_GROUP_STORE)){
      bgs = (Map)viewScopeMap.get(BREAK_GROUP_STORE);
    }
    else{
      // First access so create and populate the store map
      bgs = new HashMap(1);
      viewScopeMap.put(BREAK_GROUP_STORE, bgs);
    }
    if (bgs.containsKey(compareKey)){
      Object compareLast = bgs.get(compareKey);
      if (compareLast != null && compareLast.equals(compareValue) ){
        repeatedValue = true;
      }
      else{
        // new value, so reset what we'e got stored
        bgs.put(compareKey,compareValue);
      }
    }
    else {
      // This must be a new key so store it away
      bgs.put(compareKey, compareValue);
    }
    return repeatedValue;
  }
}

Define the TagLib

JDeveloper has a handy editor to create the Tag library (New -> Web Tier -> JSF / Facelets -> Facelets Tag Library). This will allow you to define either custom components or functions, as we are doing here.  You basically need just 3 bits of information to define your function in the taglib, the class you created, the function signature in the class and an arbitary namespace string to uniquely identify the taglib.

The resulting XML is pretty simple. Notice the <namespace> attribute which will be referenced again in the <f:view> tag of the page.

<?xml version = '1.0' encoding = 'windows-1252'?>
<facelet-taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
                version="2.0" xmlns="http://java.sun.com/xml/ns/javaee">
  <namespace>oracle.com/adf/demo/breakgroup.taglib</namespace>
  <function>
    <description>Compares the passed-in value with the last one that was passed in for this key and returns true to indicate a match or false in any other case.</description>
    <function-name>compareWithLast</function-name>
    <function-class>oracle.demo.breakgroup.el.BreakGroupFunctions</function-class>
    <function-signature>java.lang.Boolean compareWithLastValue(
                        java.lang.String, java.lang.Object)</function-signature>
  </function>
</facelet-taglib>

Wire this into the Page

So finally we need to be able to consume this new function in the page.  The first step is to create the namespace definition in the <f:view ...> tag, in this case I've used "bgf", thus:

<f:view xmlns:f="http://java.sun.com/jsf/core" 
        xmlns:af="http://xmlns.oracle.com/adf/faces/rich" 
        xmlns:bgf="oracle.com/adf/demo/breakgroup.taglib">

Then we can use that function in much the same way as the map reference we used before, but now we can pass two arguments - the value to compare and the compare key:

<af:column headerText="Department" id="c1">
  <af:outputText 
           value="#{row.DepartmentName}" 
           visible="#{!bgf:compareWithLast('departmentsTable_bg1', row.DepartmentId)}" 
           id="ot1"/>                            
</af:column>   

So there you have it. As you can see it's really simple to extend Expression Language for yourself and in doing so opens up a large set of possibilities. Before I go though I'll point you off to a couple of other resources in this subject area which will provide a little more. 

Comments:

Post a Comment:
Comments are closed for this entry.
About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today