Table Models

   

When you use a Java object, array, list, result set, or JSTL result object to represent table data, h:dataTable wraps those objects in a model that extends the javax.faces.model.DataModel class. All of those model classes, listed below, reside in the javax.faces.model package:

  • ArrayDataModel

  • ListDataModel

  • ResultDataModel

  • ResultSetDataModel

  • ScalarDataModel

h:dataTable deals with the models listed above; it never directly accesses the object array, list, etc. you specify with the value attribute. You can, however, access those objects yourself with the DataModel.getWrappedObject method. That method comes in handy for adding and removing table rows.

Editing Table Models

It's easy to add and delete table rows with two methods provided by all data models: getWrappedObject() and setWrappedObject(). Let's see how it works with an application, shown in Figure 5-11, that allows users to delete rows from a table.

Figure 5-11. Deleting Table Rows

graphics/05fig11.jpg


Going from top to bottom, Figure 5-11 shows the deletion of a single row. When a user activates the Delete selected names button, the JSF implementation invokes an action listener method that deletes the selected rows. That method looks like this:

 

 public String deleteNames() {     if (!getAnyNamesMarkedForDeletion())         return null;     Name[] currentNames = (Name[]) model.getWrappedData();     Name[] newNames = new Name[currentNames.length -         getNumberOfNamesMarkedForDeletion()];     for(int i= 0, j = 0; i < currentNames.length; ++i) {         Name name = (Name) currentNames[i];             if (!name.isMarkedForDeletion()) {             newNames[j++] = name;         }     }     model.setWrappedData(newNames);     return null;    } } 

The deleteNames method obtains a reference to the current set of names by calling the model's getWrappedData method. Then it creates a new array whose size is the size of the current array minus the number of names that have been marked for deletion. Subsequently, the method adds each of the current names to the new array, leaving out names that were marked for deletion. Finally, the method calls the model's setWrappedData method to reset the model with the new array of names.

The JSF implementation invokes the TableData.deleteNames method when the Delete selected names button is activated. The deleteNames method obtains a reference to the current array of names by invoking the data model's getWrappedObject method. The deleteNames method subsequently creates a new array without the names marked for deletion that it pushes to the model with the setWrappedObject method.

Although the preceding example doesn't illustrate adding rows, it does illustrate the principle: Get the current object wrapped by the model with getWrappedObject(), modify it (by adding or deleting rows), and then reset the wrapped object with setWrappedObject().

Figure 5-12 shows the directory structure for the application. Listing 5-17 through Listing 5-19 list the pertinent files from the application shown in Figure 5-11.

Figure 5-12. The Directory Structure for the Delete Example

graphics/05fig12.jpg


NOTE

graphics/note_icon.gif

Calling the model's setWrappedData() to reset the model's data is one way to delete rows. We could've also reset the model itself, like this:

 

 model = new ArrayDataModel(newNames); 


Listing 5-17. delete/index.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>  4.    <f:view>  5.       <head>  6.          <link href="styles.css" rel="stylesheet" type="text/css"/>  7.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  8.          <title>  9.             <h:outputText value="#{msgs.windowTitle}"/> 10.          </title> 11.       </head> 12.       <body> 13.          <h:form> 14.             <h:dataTable value="#{tableData.names}" var="name" 15.                style header 16.                columnClasses="last,first"> 17.                <h:column rendered="#{tableData.editable}"> 18.                   <f:facet name="header"> 19.                      <h:outputText value="#{msgs.deleteColumnHeader}"/> 20.                   </f:facet> 21.                   <h:selectBooleanCheckbox value="#{name.markedForDeletion}" 22.                      onchange="submit()"/> 23.                </h:column> 24.                <h:column> 25.                   <f:facet name="header"> 26.                      <h:outputText value="#{msgs.lastColumnHeader}"/> 27.                   </f:facet> 28.                   <h:outputText value="#{name.last}"/> 29.                   <f:verbatim>,</f:verbatim> 30.                </h:column> 31.                <h:column> 32.                   <f:facet name="header"> 33.                      <h:outputText value="#{msgs.firstColumnHeader}"/> 34.                   </f:facet> 35.                   <h:outputText value="#{name.first}"/> 36.                </h:column> 37.             </h:dataTable> 38.             <p> 39.             <h:outputText value="#{msgs.editPrompt}"/> 40.             <h:selectBooleanCheckbox onchange="submit()" 41.                value="#{tableData.editable}"/> 42.             <h:commandButton value="#{msgs.deleteButtonText}" 43.                rendered="#{tableData.editable}" 44.                action="#{tableData.deleteNames}" 45.                disabled="#{not tableData.anyNamesMarkedForDeletion}"/> 46.          </h:form> 47.       </body> 48.    </f:view> 49. </html> 

Listing 5-18. delete/WEB-INF/classes/com/corejsf/Name.java
  1. package com.corejsf;  2.  3. public class Name {  4.    private String first;  5.    private String last;  6.    private boolean markedForDeletion = false;  7.  8.    public Name(String first, String last) {  9.       this.first = first; 10.       this.last = last; 11.    } 12. 13.    public void setFirst(String newValue) { first = newValue; } 14.    public String getFirst() { return first; } 15. 16.   public void setLast(String newValue) { last = newValue; } 17.   public String getLast() { return last; } 18. 19.   public boolean isMarkedForDeletion() { return markedForDeletion; } 20.   public void setMarkedForDeletion(boolean newValue) { 21.      markedForDeletion = newValue; 22.   } 23.} 

Listing 5-19. delete/WEB-INF/classes/com/corejsf/TableData.java
  1. package com.corejsf;  2.  3. import javax.faces.model.DataModel;  4. import javax.faces.model.ArrayDataModel;  5.  6. public class TableData {  7.    private boolean editable = false;  8.    private ArrayDataModel model = null;  9. 10.    private static final Name[] names = { 11.       new Name("Anna", "Keeney"), 12.       new Name("John", "Wilson"), 13.       new Name("Mariko", "Randor"), 14.       new Name("William", "Dupont"), 15.    }; 16. 17.    public TableData() { model = new ArrayDataModel(names); } 18. 19.    public DataModel getNames() { return model; } 20. 21.    public boolean isEditable() { return editable; } 22.    public void setEditable(boolean newValue) { editable = newValue; } 23. 24.    public String deleteNames() { 25.       if (!getAnyNamesMarkedForDeletion()) 26.          return null; 27. 28.       Name[] currentNames = (Name[]) model.getWrappedData(); 29.       Name[] newNames = new Name[currentNames.length 30.          - getNumberOfNamesMarkedForDeletion()]; 31. 32.       for(int i = 0, j = 0; i < currentNames.length; ++i) { 33.          Name name = (Name) currentNames[i]; 34.          if (!name.isMarkedForDeletion()) { 35.             newNames[j++] = name; 36.          } 37.       } 38.       model.setWrappedData(newNames); 39.       return null; 40.    } 41. 42.    public int getNumberOfNamesMarkedForDeletion() { 43.       Name[] currentNames = (Name[]) model.getWrappedData(); 44.       int cnt = 0; 45. 46.       for(int i = 0; i < currentNames.length; ++i) { 47.          Name name = (Name) currentNames[i]; 48.          if (name.isMarkedForDeletion()) 49.             ++cnt; 50.       } 51.       return cnt; 52.    } 53. 54.    public boolean getAnyNamesMarkedForDeletion() { 55.       Name[] currentNames = (Name[]) model.getWrappedData(); 56.       for(int i = 0; i < currentNames.length; ++i) { 57.          Name name = (Name) currentNames[i]; 58.          if (name.isMarkedForDeletion()) 59.             return true; 60.       } 61.       return false; 62.    } 63. } 

We've seen how to perform simple manipulation of a data model. Sometimes, a little more sophistication is required, for example, when you sort or filter a model's data.

Sorting and Filtering

To sort or filter tables with h:dataTable, you need to implement a table model that decorates one of the table models listed on page 185. Figure 5-13 shows what it means to decorate a table model.

Figure 5-13. Data Model Filter

graphics/05fig13.gif


Instances of UIData the component associated with h:dataTable invoke methods on their model. When you decorate the model, those method calls are intercepted by a model of your own one that mostly just forwards method calls to the original model. Typically, sorting models override the setRowIndex method to return a sorted index instead of the original model's index. Let's see how that works.

Figure 5-14 shows an application that sorts table columns.

Figure 5-14. Sorting Table Columns

graphics/05fig14.jpg


The application shown in Figure 5-14 sorts table columns by decorating a table data model. First, we specify the h:dataTable's value attribute like this:

 

 <h:dataTable value="#{tableData.names}" var="name" ...>     ... 

The TableData.getNames method returns a data model:

 

 public class TableData {    private DataModel filterModel = null;    private static final Name[] names = {       new Name("Anna", "Keeney"),       new Name("John", "Wilson"),       new Name("Mariko", "Randor"),       new Name("William", "Dupont"),    };    public TableData() {      ArrayDataModel model = new ArrayDataModel(names);       filterModel = new SortFilterModel(model);    }    public DataModel getNames() {       return filterModel;    } } 

When the tableData object is created, it creates an ArrayDataModel instance, passing it the array of names. That is the original model. Then the TableData constructor wraps that model in a sorting model. When the getNames method is subsequently called to populate the data table, that method returns the sorting model. The sorting model is implemented like this:

 

 public class SortFilterModel extends DataModel {    private DataModel model;    private Row[] rows;    ...     public SortFilterModel(DataModel model) {         this.model = model;         int rowCnt = model.getRowCount();         if (rowCnt != -1) {             rows = new Row[rowCnt];             for (int i = 0; i < rowCnt; ++i) {             rows[i] = new Row(i);         }     }     public void setRowIndex(int rowIndex) {       if (rowIndex == -1 || rowIndex >= model.getRowCount()) {           model.setRowIndex(rowIndex);       }       else {          model.setRowIndex(rows[rowIndex].row);       }    }    ... } 

Notice that we create an our own array of indices that represent sorted indices. We return a sorted index from the setRowIndex method when the indicated index is in range.

So how does the sorting happen? The SortFilterModel class provides two methods, sortByFirst() and sortByLast():

 

 public String sortByLast() {    Arrays.sort(rows, byLast);    return null; } public String sortByFirst() {    Arrays.sort(rows, byFirst);    return null; } 

The byLast and byFirst variables are comparators. The former compares last names and the latter compares first names. You can see the implementation of the comparators in Listing 5-21 on page 196.

The directory structure for the sorting example is shown in Figure 5-15. Listing 5-20 through Listing 5-26 provide full listings of the application.

Listing 5-20. sort/index.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>  4.    <f:view>  5.       <head>  6.          <link href="site.css" rel="stylesheet" type="text/css"/>  7.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  8.          <title>  9.             <h:outputText value="#{msgs.windowTitle}"/> 10.          </title> 11.       </head> 12.       <body> 13.          <h:form> 14.             <h:dataTable value="#{tableData.names}" var="name" 15.                style header 16.                columnClasses="last,first"> 17.                <h:column> 18.                   <f:facet name="header"> 19.                      <h:commandLink action="#{tableData.names.sortByLast}"> 20.                           <h:outputText value="#{msgs.lastColumnHeader}"/> 21.                       </h:commandLink> 22.                   </f:facet> 23.                   <h:outputText value="#{name.last}"/> 24.                   <f:verbatim>,</f:verbatim> 25.                </h:column> 26.                <h:column> 27.                   <f:facet name="header"> 28.                      <h:commandLink action="#{tableData.names.sortByFirst}"> 29.                           <h:outputText value="#{msgs.firstColumnHeader}"/> 30.                       </h:commandLink> 31.                   </f:facet> 32.                   <h:outputText value="#{name.first}"/> 33.                </h:column> 34.             </h:dataTable> 35.          </h:form> 36.       </body> 37.    </f:view> 38. </html> 

Listing 5-21. sort/WEB-INF/classes/com/corejsf/SortFilterModel.java
   1. package com.corejsf;   2.   3. import java.util.Arrays;   4. import java.util.Comparator;   5. import javax.faces.model.DataModel;   6. import javax.faces.model.DataModelListener;   7.   8. public class SortFilterModel extends DataModel {   9.    private DataModel model;  10.    private Row[] rows;  11.  12.    private static Comparator byLast = new  13.       Comparator() {  14.          public int compare(Object o1, Object o2) {  15.             Row r1 = (Row) o1;  16.             Row r2 = (Row) o2;  17.             Name n1 = (Name) r1.getData();  18.             Name n2 = (Name) r2.getData();  19.             return n1.getLast().compareTo(n2.getLast());  20.          }  21.    };  22.  23.    private static Comparator byFirst = new  24.       Comparator() {  25.          public int compare(Object o1, Object o2) {  26.             Row r1 = (Row) o1;  27.             Row r2 = (Row) o2;  28.             Name n1 = (Name) r1.getData();  29.             Name n2 = (Name) r2.getData();  30.             return n1.getFirst().compareTo(n2.getFirst());  31.          }  32.    };  33.  34.    private class Row {  35.       private int row;  36.       public Row(int row) {  37.          this.row = row;  38.       }  39.       public Object getData() {  40.           int originalIndex = model.getRowIndex();  41.          model.setRowIndex(row);  42.          Object thisRowData = model.getRowData();  43.          model.setRowIndex(originalIndex);  44.          return thisRowData;  45.       }  46.    }  47.  48.    public SortFilterModel(DataModel model) {  49.       this.model = model;  50.       int rowCnt = model.getRowCount();  51.       if (rowCnt != -1) {  52.          rows = new Row[rowCnt];  53.          for(int i=0; i < rowCnt; ++i) {  54.             rows[i] = new Row(i);  55.          }  56.       }  57.    }  58.  59.    public String sortByLast() {  60.       Arrays.sort(rows, byLast);  61.       return null;  62.    }  63.  64.    public String sortByFirst() {  65.       Arrays.sort(rows, byFirst);  66.       return null;  67.    }  68.  69.    public void setRowIndex(int rowIndex) {  70.       if (rowIndex == -1 || rowIndex >= model.getRowCount()) {  71.          model.setRowIndex(rowIndex);  72.       }  73.       else {  74.          model.setRowIndex(rows[rowIndex].row);  75.       }  76.    }  77.  78.    // The following methods delegate directly to the  79.    // decorated model  80.  81.    public boolean isRowAvailable() {  82.       return model.isRowAvailable();  83.    }  84.    public int getRowCount() {  85.       return model.getRowCount();  86.    }  87.    public Object getRowData() {  88.       return model.getRowData();  89.    }  90.    public int getRowIndex() {  91.       return model.getRowIndex();  92.    }  93.    public Object getWrappedData() {  94.       return model.getWrappedData();  95.    }  96.    public void setWrappedData(Object data) {  97.       model.setWrappedData(data);  98.    }  99.    public void addDataModelListener(DataModelListener listener) { 100.       model.addDataModelListener(listener); 101.    } 102.    public DataModelListener[] getDataModelListeners() { 103.       return model.getDataModelListeners(); 104.    } 105.    public void removeDataModelListener(DataModelListener listener) { 106.       model.removeDataModelListener(listener); 107.    } 108. } 

Listing 5-22. sort/WEB-INF/classes/com/corejsf/Name.java
  1. package com.corejsf;  2.  3. public class Name {  4.    private String first;  5.    private String last;  6.  7.    public Name(String first, String last) {  8.       this.first = first;  9.       this.last = last; 10.    } 11. 12.    public void setFirst(String newValue) { first = newValue; } 13.    public String getFirst() { return first; } 14. 15.    public void setLast(String newValue) { last = newValue; } 16.    public String getLast() { return last; } 17. } 

Listing 5-23. sort/WEB-INF/classes/com/corejsf/TableData.java
  1. package com.corejsf;  2.  3. import javax.faces.model.DataModel;  4. import javax.faces.model.ArrayDataModel;  5.  6. public class TableData {  7.    private DataModel filterModel = null;  8.    private static final Name[] names = {  9.       new Name("Anna", "Keeney"), 10.       new Name("John", "Wilson"), 11.       new Name("Mariko", "Randor"), 12.       new Name("William", "Dupont"), 13.    }; 14. 15.    public TableData() { 16.       ArrayDataModel model = new ArrayDataModel(names); 17.       filterModel = new SortFilterModel(model); 18.    } 19.    public DataModel getNames() { 20.       return filterModel; 21.    } 22. } 

Listing 5-24. sort/WEB-INF/faces-config.xml
  1. <?xml version="1.0"?>  2.  3. <!DOCTYPE faces-config PUBLIC  4. "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"  5. "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">  6.  7. <faces-config>  8.    <managed-bean>  9.       <managed-bean-name>tableData</managed-bean-name> 10.       <managed-bean-class>com.corejsf.TableData</managed-bean-class> 11.       <managed-bean-scope>session</managed-bean-scope> 12.    </managed-bean> 13. </faces-config> 

Listing 5-25. sort/WEB-INF/styles.css
  1. .names {  2.    border: thin solid black;  3. }  4. .namesHeader {  5.    text-align: center;  6.    font-style: italic;  7.    color: Snow;  8.    background: Teal;  9. } 10. .last { 11.    height: 25px; 12.    text-align: center; 13.    background: MediumTurquoise; 14. } 15. .first { 16.    text-align: left; 17.    background: PowderBlue; 18. } 

Listing 5-26. sort/WEB-INF/classes/com/corejsf/messages.properties
 1. windowTitle=Sorting Java Beans 2. pageTitle=An array of names: 3. firstColumnHeader=First Name 4. lastColumnHeader=Last Name 

Figure 5-15. The Directory Structure for the Sorting Example

graphics/05fig15.jpg


 

graphics/api_icon.gif

 

 javax.faces.model.DataModel 

  • int getRowCount()

    Returns the total number of rows, if known; otherwise, it returns -1. The ResultSetDataModel always returns -1 from this method.

  • Object getRowData()

    Returns the data associated with the current row

  • boolean isRowAvailable()

    Returns true if there is valid data at the current row index

  • int getRowIndex()

    Returns the index of the current row

  • void setRowIndex(int index)

    Sets the current row index and updates the scoped variable representing the current item in the collection (that variable is specified with the var attribute of h:dataTable)

  • void addDataModelListener(DataModelListener listener)

    Adds a data model listener that's notified when the row index changes

  • void removeDataModelListener(DataModelListener listener)

    Removes a data model listener

  • void setWrappedData(Object obj)

    Sets the object that a data model wraps

  • Object getWrappedData()

    Returns a data model's wrapped data



core JavaServer Faces
Core JavaServer Faces
ISBN: 0131463055
EAN: 2147483647
Year: 2003
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net