Not using ADF bindings but wanted to have a sortable table? This post is for you.
I am glad to share a sortable CollectionModel implementation that is so elegantly simple. :)
This model supports in-memory sorting and filtering. The filtering concept based on groovy will be discussed on a subsequent post.
I am glad to share a sortable CollectionModel implementation that is so elegantly simple. :)
This model supports in-memory sorting and filtering. The filtering concept based on groovy will be discussed on a subsequent post.
package soadev.ext.trinidad.model; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.el.ELContext; import javax.faces.context.FacesContext; import org.apache.myfaces.trinidad.model.CollectionModel; import org.apache.myfaces.trinidad.model.SortCriterion; public class SortableFilterableModel extends CollectionModel { private List wrappedData; private List<Integer> sortedFilteredIndexList; private Integer baseIndex; private SortCriterion sortCriterion = null; public SortableFilterableModel(List wrappedData) { super(); this.wrappedData = wrappedData; sortedFilteredIndexList = new ArrayList<Integer>(); for (int i = 0; i < wrappedData.size(); i++) { sortedFilteredIndexList.add(i); } } public Object getRowKey() { return isRowAvailable() ? baseIndex : null; } public void setRowKey(Object object) { baseIndex = object == null ? -1 : ((Integer)object); } public boolean isRowAvailable() { return sortedFilteredIndexList.indexOf(baseIndex) != -1; } public int getRowCount() { return sortedFilteredIndexList.size(); } public Object getRowData() { return wrappedData.get(baseIndex); } public int getRowIndex() { return sortedFilteredIndexList.indexOf(baseIndex); } public void setRowIndex(int i) { if(i < 0 || i >= sortedFilteredIndexList.size()){ baseIndex = -1; }else{ baseIndex = sortedFilteredIndexList.get(i); } } public Object getWrappedData() { return wrappedData; } public void setWrappedData(Object object) { this.wrappedData = (List)object; } public List<Integer> getSortedFilteredIndexList() { return sortedFilteredIndexList; } @Override public boolean isSortable(String property) { try { Object data = wrappedData.get(0); Object propertyValue = evaluateProperty(data, property); // when the value is null, we don't know if we can sort it. // by default let's support sorting of null values, and let the user // turn off sorting if necessary: return (propertyValue instanceof Comparable) || (propertyValue == null); } catch (RuntimeException e) { e.printStackTrace(); return false; } } private Object evaluateProperty(Object base, String property) { ELContext elCtx = FacesContext.getCurrentInstance().getELContext(); //simple property -> resolve value directly if (!property.contains(".")) { return elCtx.getELResolver().getValue(elCtx, base, property); } int index = property.indexOf('.'); Object newBase = elCtx.getELResolver().getValue(elCtx, base, property.substring(0, index)); return evaluateProperty(newBase, property.substring(index + 1)); } @Override public List<SortCriterion> getSortCriteria() { if (sortCriterion == null) { return Collections.emptyList(); } else { return Collections.singletonList(sortCriterion); } } @Override public void setSortCriteria(List<SortCriterion> criteria) { if ((criteria == null) || (criteria.isEmpty())) { sortCriterion = null; // restore unsorted order: Collections.sort(sortedFilteredIndexList); //returns original order but still same filter } else { SortCriterion sc = criteria.get(0); sortCriterion = sc; _sort(sortCriterion.getProperty(), sortCriterion.isAscending()); } } private void _sort(String property, boolean isAscending) { if (getRowCount() == 0) { return; } if (sortedFilteredIndexList!= null && !sortedFilteredIndexList.isEmpty()) { Comparator<Integer> comp = new Comp(property); if (!isAscending) comp = new Inverter<Integer>(comp); Collections.sort(sortedFilteredIndexList, comp); } } private final class Comp implements Comparator<Integer> { public Comp(String property) { _prop = property; } public int compare(Integer x, Integer y) { Object instance1 = wrappedData.get(x); Object value1 = evaluateProperty(instance1, _prop); Object instance2 = wrappedData.get(y); Object value2 = evaluateProperty(instance2, _prop); if (value1 == null) return (value2 == null) ? 0 : -1; if (value2 == null) return 1; if (value1 instanceof Comparable) { return ((Comparable<Object>)value1).compareTo(value2); } else { // if the object is not a Comparable, then // the best we can do is string comparison: return value1.toString().compareTo(value2.toString()); } } private final String _prop; } private static final class Inverter<T> implements Comparator<T> { public Inverter(Comparator<T> comp) { _comp = comp; } public int compare(T o1, T o2) { return _comp.compare(o2, o1); } private final Comparator<T> _comp; } }sample page
<?xml version='1.0' encoding='UTF-8'?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:af="http://xmlns.oracle.com/adf/faces/rich"> <jsp:directive.page contentType="text/html;charset=UTF-8"/> <f:view> <af:document id="d1"> <af:form id="f1"> <af:panelCollection id="pc1"> <f:facet name="menus"/> <f:facet name="toolbar"> <af:toolbar id="t2"> <af:commandToolbarButton text="Action" id="ctb1" actionListener="#{viewScope.sortableFilterableForm.action}"/> </af:toolbar> </f:facet> <f:facet name="statusbar"/> <af:table var="row" rowBandingInterval="0" id="t1" value="#{viewScope.sortableFilterableForm.model}" rowSelection="multiple" binding="#{viewScope.sortableFilterableForm.table}" selectedRowKeys="#{viewScope.sortableFilterableForm.selection}" queryListener="#{viewScope.sortableFilterableForm.tableFilter}" filterModel="#{viewScope.sortableFilterableForm.descriptor}" filterVisible="true" emptyText="no result found"> <af:column sortable="true" headerText="Job Id" align="start" id="c2" filterable="true" sortProperty="jobId"> <af:outputText value="#{row.jobId}" id="ot1"/> </af:column> <af:column sortable="true" headerText="Job Title" align="start" id="c4" filterable="true" sortProperty="jobTitle"> <af:outputText value="#{row.jobTitle}" id="ot4"/> </af:column> <af:column sortable="true" headerText="Max Salary" align="start" id="c1" filterable="true" sortProperty="maxSalary"> <af:outputText value="#{row.maxSalary}" id="ot3"/> </af:column> <af:column sortable="true" headerText="Min Salary" align="start" id="c3" filterable="true" sortProperty="minSalary"> <af:outputText value="#{row.minSalary}" id="ot2"/> </af:column> <af:column sortable="true" headerText="Job Type" align="start" id="c5" filterable="true" sortProperty="jobType.color"> <af:outputText value="#{row.jobType.color}" id="ot5"/> </af:column> </af:table> </af:panelCollection> </af:form> </af:document> </f:view> </jsp:root>This is somewhat derived from the trinidad SortableModel implementation but even made simpler.