sâmbătă, 18 iulie 2015

JSF 2.3 new feature: registrable DataModels

Before reading this article I strongly recommend you to read the below two articles of Arjan Tijms:

JSF 2.3 new feature: registrable DataModels



With @FacesDataModel custom DataModel wrappers can be registered, but those wrappers can not (yet) override any of the build-in types.

Usage involve two steps:
 - register your wrapper by annotating it with @FacesDataModel
 - designates the type this wrapper is able to handle via forClass attribute

WRAPPER MODEL

@FacesDataModel(forClass = fooCollection.class)
public class FooCollectionModel<E> extends DataModel<E> {
    
 @Override
 public E getRowData() {
  // access FooCollection here
 }

 @Override
 public void setWrappedData(Object fooCollection) {
  // likely just store fooCollection
 }

 // Other methods omitted for brevity
}

USE COLLECTION IN YOUR BEANS

@Named
public class FooBean {
 public FooCollection<Foo> getFoo() {
  // return fooCollection
 }
}

IN DATA TABLE IT WORKS OUT OF THE BOX

<h:dataTable value="#{fooBean.foo}" var="t">
 <h:column>
  #{t} | #{t.property}
 </h:column>
</h:dataTable>

For example, let's define an ArraySet as below:

public class ArraySet<T> implements Cloneable, Serializable {

 private ArrayList list;

 public ArraySet() {
  list = new ArrayList();
 }

 public ArraySet(Collection collection) {
  list = new ArrayList();

  Iterator iterator = collection.iterator();
  if (collection instanceof Set) {
      while (iterator.hasNext()) {
             list.add(iterator.next());
      }
  } else {
      while (iterator.hasNext()) {
             add(iterator.next());
      }
  }
 }

 public Iterator iterator() {
  return list.iterator();
 }

 public int size() {
  return list.size();
 }

 public final boolean add(Object element) {
  boolean modified;
  if (modified = !list.contains(element)) {
      list.add(element);
  }
  return modified;
 }

 public boolean remove(Object element) {
  return list.remove(element);
 }

 public boolean isEmpty() {
  return list.isEmpty();
 }

 public boolean contains(Object element) {
  return list.contains(element);
 }

 public void clear() {
  list.clear();
 }

 public Object get(int index) {
  return list.get(index);
 }

 @Override
 public Object clone() throws CloneNotSupportedException {
  try {
      ArraySet newSet = (ArraySet) super.clone();
      newSet.list = (ArrayList) list.clone();
      return newSet;
  } catch (CloneNotSupportedException e) {
      throw new InternalError();
  }
 }
}

For this collection we can write a sortable model, as below:

@FacesDataModel(forClass = ArraySet.class)
public final class SortDataModel<T> extends DataModel<T> {

 private static final Logger LOG = Logger.getLogger(SortDataModel.class.getName());

 private int index = -1;
 private ArraySet list;
 private Integer[] rows;

 public SortDataModel() {
  LOG.info("Using the SortDataModel !");
 }

 public SortDataModel(List<T> list) {
  super();
  LOG.info("Using the SortDataModel !");
  setWrappedData(list);
 }

 @Override
 public boolean isRowAvailable() {
  if (list == null) {
      return (false);
  } else if ((index >= 0) && (index < list.size())) {
      return (true);
  } else {
      return (false);
  }
 }

 @Override
 public int getRowCount() {
  if (list == null) {
      return (-1);
  }
  return (list.size());
 }

 @Override
 public T getRowData() {

  if (list == null) {
      return (null);
  } else if (!isRowAvailable()) {
      try {
          throw new Exception();
      } catch (Exception ex) {
          Logger.getLogger(SortDataModel.class.getName()).log(Level.SEVERE, null, ex);
      }
  } else {
      return ((T) list.get(index));
  }
  return (null);
 }

 @Override
 public int getRowIndex() {
  return (index);
 }

 @Override
 public void setRowIndex(int rowIndex) {

  if (rowIndex < -1) {
      throw new IllegalArgumentException();
  }
  int old = index;

  if ((0 <= rowIndex) && (rowIndex < rows.length)) {
       index = rows[rowIndex];
  } else {
       index = rowIndex;
  }

  if (list == null) {
      return;
  }

  DataModelListener[] listeners = getDataModelListeners();
  if ((old != index) && (listeners != null)) {
       Object rowData = null;
       if (isRowAvailable()) {
           rowData = getRowData();
       }
       DataModelEvent event = new DataModelEvent(this, index, rowData);
       int n = listeners.length;
       for (int i = 0; i < n; i++) {
            if (null != listeners[i]) {
                listeners[i].rowSelected(event);
            }
       }
  }
 }

 @Override
 public Object getWrappedData() {
  return (this.list);
 }

 @Override
 public void setWrappedData(Object data) {

  if (data == null) {
      list = null;
      setRowIndex(-1);
  } else {
      list = (ArraySet) data;

      initRows();

      index = -1;
      setRowIndex(0);
      sortThis((T key_1, T key_2) -> {
       if (key_1 == null ? key_2 == null : key_1.equals(key_2)) {
           return 1;
       }
       if (key_1 == null) {
           return -1;
       }
       if (key_2 == null) {
           return 1;
       }
       return String.valueOf(key_1).compareTo(String.valueOf(key_2));
      });
  }
 }

 public void sortThis(final Comparator<T> comparator) {
  Comparator<Integer> rowc = (Integer key_1, Integer key_2) -> {
   T key_1_data = getData(key_1);
   T key_2_data = getData(key_2);
   return comparator.compare(key_1_data, key_2_data);
  };
  Arrays.sort(rows, rowc);
 }

 private T getData(int row) {
  int baseRowIndex = getRowIndex();
  setRowIndex(row);
  T newRowData = getRowData();
  setRowIndex(baseRowIndex);

  return newRowData;
 }

 private void initRows() {
  int rowCount = getRowCount();
  if (rowCount != -1) {
      this.rows = new Integer[rowCount];
      for (int i = 0; i < rowCount; ++i) {
           rows[i] = i;
      }
  }
 }
}

Now, let's fill up an ArraySet:

String players[] = {"Rafa", "Federer", "Tonga", "Novak", "Murray"};
ArraySet arraySet = new ArraySet(Arrays.asList(players));

And, in the JSF page we can used it like below:

<h:dataTable value="#{playerBean.arraySet}" var="t" border="1">               
 <h:column>
  <f:facet name="header">Player:</f:facet>
  #{t}
 </h:column>
</h:dataTable>

And, the output will be:

The complete application is available here (I've tested with Mojarra 2.3.0-m04 under Payara 4).

Niciun comentariu:

Trimiteți un comentariu