28 Feb 2013

Dynamic Table Declarative Component

In my previous post I created a tree binding definition with a correspondent iterator binding dynamically at runtime, and mentioned that this technique can be used to build a declarative component browsing data of any view object passed to the component as an attribute. In this post I'm going to show how we can do that.

The source code of our declarative component looks like this:
<af:componentDef componentVar="dyntable" var="attrs">
<af:xmlContent>   <component xmlns="http://xmlns.oracle.com/adf/faces/rich/component">     <attribute>       <attribute-name>ViewObjectName</attribute-name>       <required>true</required>     </attribute>     <attribute>       <attribute-name>RSIName</attribute-name>     </attribute>     <attribute>       <attribute-name>DataControlName</attribute-name>       <required>true</required>     </attribute>   </component> </af:xmlContent> <af:table rows="#{backingBeanScope.DynTableBean.tree.rangeSize}"           fetchSize="#{backingBeanScope.DynTableBean.tree.rangeSize}"           emptyText="#{backingBeanScope.DynTableBean.tree.viewable ?                        'No data to display.' : 'Access Denied.'}"           var="row" rowBandingInterval="0"           value="#{backingBeanScope.DynTableBean.tree.collectionModel}"           id="dc_t1">  <af:forEach items="#{backingBeanScope.DynTableBean.tree.attributeDefs}"              var="def">    <af:column headerText="#{backingBeanScope.DynTableBean.tree.labels[def.name]}"               sortable="true"               sortProperty="#{def.name}" id="dc_c1">      <af:outputText value="#{row[def.name]}" id="dc_ot1"/>    </af:column>  </af:forEach> </af:table> </af:componentDef>
The component has attributes for ViewObject name, VO's row set iterator name and data control name.  Have a note that we use a read-only dynamic table approach. Table's columns are going to be rendered dynamically by the forEach tag according to the VO's attributes and their UI hints.

Let's have a look at DynTableBean's code:

private static final String ITERATOR_NAME = "Iterator";
private static final String TREE_NAME = "TREE_";

public DCIteratorBinding getIterator(String iteratorName, String voName,
    String rsiName) {
    DCBindingContainer dcb = getBindings();
    DCIteratorBinding jub = dcb.findIteratorBinding(iteratorName);

    if (jub == null) {
        //Create and init an iterator binding definition
        JUIteratorDef jid =
                new JUIteratorDef(iteratorName, null, voName, rsiName, 25);
        HashMap initValues = new HashMap();
        initValues.put(JUTags.DataControl, getDataControlName());
        jid.init(initValues);

        //Create an iterator binding instance
        jub = jid.createIterBinding(BindingContext.getCurrent(), dcb);

        //Add the instance to the current binding container
        dcb.addIteratorBinding(iteratorName, jub);
    }
    return jub;

}


public JUCtrlHierBinding getTree() {
   DCBindingContainer dcb = getBindings();
   //May be the VEmp tree binding is already created
   JUCtrlHierBinding chb = 
     (JUCtrlHierBinding)dcb.findCtrlBinding(getTreeName());

    if (chb == null) { // if not
        //Looking for the VEmpIterator iterator
        JUIteratorBinding iter =
         (JUIteratorBinding)getIterator(getIteratorName(), getVOName(), 
             getRSIName());
       //Create and init a tree binding definition
        JUCtrlHierDef hierDef = new FacesCtrlHierDef();
        HashMap initValues = new HashMap();
        initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName());
        JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding();
        initValues.put(JUCtrlHierDef.PNAME_TypeBindings,
                       new JUCtrlHierTypeBinding[] { typeBinding });
        hierDef.init(initValues);

        //Create a tree binding instance
        chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb);

        //Add the instance to the current binding container
        dcb.addControlBinding(getTreeName(), chb);
    }
    return chb;
}

//Generate a name of the tree bindning as "TREE_" + component's Id
//For example: TREE_dc1, TREE_dc2, ...
private String getTreeName() {
  return new StringBuilder(TREE_NAME).append(getID()).toString();
}


//Generate a name of the iterator bindning as "Iterator_" + component's Id
//For example: Iterator_dc1, Iterator_dc2, ...
private String getIteratorName() {
  return new StringBuilder(ITERATOR_NAME ).append(getID()).toString();         
}

//Getters for the component's attributes -
// ViewObjectName, RSIName, DataControlName, id
private String getVOName() {
  return (String) getMyself().getAttributes().get("ViewObjectName");     
}

private String getRSIName() {
  return (String) getMyself().getAttributes().get("RSIName");     
}

private String getDataControlName() {
  return (String) getMyself().getAttributes().get("DataControlName");     
}

private String getID() {
  return (String) getMyself().getAttributes().get("id");  
}

//A couple of helper methods to get an instance of our component
private RichDynamicDeclarativeComponent getMyself() {
    RichDynamicDeclarativeComponent _this =
        (RichDynamicDeclarativeComponent)getValueObject("#{dyntable}",
                                      RichDynamicDeclarativeComponent.class);
    return _this;
}

private static Object getValueObject(String expr, Class returnType) {
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elctx = fc.getELContext();
    ExpressionFactory elFactory = fc.getApplication().getExpressionFactory();
    ValueExpression valueExpr = 
       elFactory.createValueExpression(elctx, expr, returnType);
    return valueExpr.getValue(elctx);
}


And finally, let's use our declarative component. We're going to put on a page two tables browsing departments and employees:

    <af:form id="f1">
      <!--Browsing departments view object VDept -->
      <af:declarativeComponent viewId="DynTable.jsff" id="dc1"
                               DataControlName="AppModuleDataControl"
                               ViewObjectName="VDept"
      />

      <!--Browsing employees view object VEmp -->
      <af:declarativeComponent viewId="DynTable.jsff" id="dc2"
                               DataControlName="AppModuleDataControl"
                               ViewObjectName="VEmp"
      />
    </af:form>


That's it! The sample application for this post is available here. I used JDeveloper 11.1.2.3.0.

24 Feb 2013

Dynamic Tree Binding Definition

ADF table component usually requires a tree binding definition in the PageDef file pointing to some iterator binding.  For example a table rendering rows of some VEmp view object would require the following structure in the PageDef file:

  <executables>
    <iterator Binds="VEmp" RangeSize="25" DataControl="AppModuleDataControl"        id="VEmpIterator"/>
  </executables>
  <bindings>
    <tree IterBinding="VEmpIterator" id="VEmp">
      <nodeDefinition DefName="com.cs.blog.dyntreebindingdemo.model.VEmp"
                      Name="VEmp0">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="FirstName"/>
          <Item Value="LastName"/>
          <Item Value="Email"/>
          <Item Value="PhoneNumber"/>
          <Item Value="HireDate"/>
          <Item Value="JobId"/>
          <Item Value="Salary"/>
          <Item Value="CommissionPct"/>
          <Item Value="ManagerId"/>
          <Item Value="DepartmentId"/>
          <Item Value="CreatedBy"/>
          <Item Value="CreatedDate"/>
          <Item Value="ModifiedBy"/>
          <Item Value="ModifiedDate"/>
          <Item Value="ActionComment"/>
          <Item Value="CreatedBy1"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
  </bindings>

In this post I'm going to show how we can create a tree binding definition and a corresponding iterator binding dynamically at runtime.

Let's consider a managed bean method returning a tree binding definition:

private static final String VO_NAME = "VEmp";
private static final String ITERATOR_NAME = "VEmpIterator";
private static final String TREE_NAME = "VEmp";   
private static final String DATACONTROL_NAME = "AppModuleDataControl";   

public JUCtrlHierBinding getTree() {
    DCBindingContainer dcb = getBindings();

    //May be the VEmp tree binding is already created
    JUCtrlHierBinding chb = (JUCtrlHierBinding)dcb.findCtrlBinding(TREE_NAME);

    if (chb == null) { // if not

        //Looking for the VEmpIterator iterator
        JUIteratorBinding iter =
            (JUIteratorBinding)getIterator(ITERATOR_NAME, VO_NAME);

        //Create and init a tree binding definition
        JUCtrlHierDef hierDef = new FacesCtrlHierDef();
        HashMap initValues = new HashMap();
        initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName());
        JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding();
        initValues.put(JUCtrlHierDef.PNAME_TypeBindings,
                       new JUCtrlHierTypeBinding[] { typeBinding });
        hierDef.init(initValues);

        //Create a tree binding instance
        chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb);

        //Add the instance to the current binding container
        dcb.addControlBinding(TREE_NAME, chb);
    }
    return chb;
}

And getIterator method looks for the corresponding iterator binding and creates it if it doesn't exist yet:

public DCIteratorBinding getIterator(String iteratorName, String voName)
{
  DCBindingContainer dcb = getBindings();

  //It could be created earlier 
  DCIteratorBinding jub = dcb.findIteratorBinding(iteratorName);

  if (jub==null) {// if not
    //Create and init an iterator binding definition
    JUIteratorDef jid = new JUIteratorDef(iteratorName, null, voName, null, 25);
    HashMap initValues = new HashMap();
    initValues.put(JUTags.DataControl , DATACONTROL_NAME);
    jid.init(initValues);

    //Create an iterator binding instance
    jub = jid.createIterBinding(BindingContext.getCurrent(), dcb);        

    //Add the instance to the current binding container
    dcb.addIteratorBinding(iteratorName, jub);
  }
  return jub;
}
The readonly table using our dynamic tree binding should look like this:

<af:table rows="#{DynTableBean.tree.rangeSize}"
          fetchSize="#{DynTableBean.tree.rangeSize}"
          emptyText="#{DynTableBean.tree.viewable ? 'No data to display.' : 'Access Denied.'}"
          var="row" rowBandingInterval="0"
          value="#{DynTableBean.tree.collectionModel}"
          selectedRowKeys="#{DynTableBean.tree.collectionModel.selectedRow}"
          selectionListener="#{DynTableBean.tree.collectionModel.makeCurrent}"
          rowSelection="single" id="dc_t1">
  <af:forEach items="#{DynTableBean.tree.attributeDefs}" var="def">
    <af:column headerText="#{DynTableBean.tree.labels[def.name]}" sortable="true"
               sortProperty="#{def.name}" id="dc_c1">
      <af:outputText value="#{row[def.name]}" id="dc_ot1"/>
    </af:column>
  </af:forEach>
</af:table>

The technique shown in this post can be easily used to build a declarative component responsible for browsing data of any view object passed to the component as an attribute.

That's it!