// $HeadURL$ // $Id$ // // Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College. // // Screensaver is an open-source project developed by the ICCB-L and NSRB labs // at Harvard Medical School. This software is distributed under the terms of // the GNU General Public License. package edu.harvard.med.screensaver.ui.arch.datatable.model; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.faces.model.DataModelListener; import javax.faces.model.ListDataModel; import org.apache.log4j.Logger; import edu.harvard.med.screensaver.db.Criterion; import edu.harvard.med.screensaver.db.CriterionMatchException; import edu.harvard.med.screensaver.db.SortDirection; import edu.harvard.med.screensaver.db.datafetcher.DataFetcher; import edu.harvard.med.screensaver.ui.arch.datatable.DataTableModelType; import edu.harvard.med.screensaver.ui.arch.datatable.column.CompoundColumnComparator; import edu.harvard.med.screensaver.ui.arch.datatable.column.TableColumn; public class InMemoryDataModel<R> extends DataTableModel<R> { private static Logger log = Logger.getLogger(InMemoryEntityDataModel.class); private static final Comparator<Object> PHYSICAL_ORDER_COMPARATOR = new Comparator<Object>() { public int compare(Object o1, Object o2){ return 0; } }; protected DataFetcher<R,?,?> _dataFetcher; protected List<R> _unfilteredData; private ListDataModel _baseModel = new ListDataModel(); public InMemoryDataModel(DataFetcher<R,?,?> dataFetcher) { _dataFetcher = dataFetcher; } @Override public DataTableModelType getModelType() { return DataTableModelType.IN_MEMORY; } @Override public void fetch(List<? extends TableColumn<R,?>> columns) { _unfilteredData = _dataFetcher.fetchAllData(); setWrappedData(_unfilteredData); } public void filter(List<? extends TableColumn<R,?>> columns) { if (_unfilteredData == null) { throw new IllegalStateException("fetch() must be called first"); } // TODO: consider operator types to optimize this (can short-circuit <, <=, >, >=, can do binary search with equals) ArrayList<R> filteredData = new ArrayList<R>(_unfilteredData); for (TableColumn<R,?> column : columns) { try { if (column.hasCriteria()) { Iterator<? extends R> rowIter = filteredData.iterator(); while (rowIter.hasNext()) { List<? extends Criterion<?>> criteria = column.getCriteria(); if (!matches(column.getCellValue(rowIter.next()), criteria)) { rowIter.remove(); } } } } catch (CriterionMatchException e) { e.getCriterion().reset(); // TODO: reportApplicationError(e); } } setWrappedData(filteredData); } public void sort(List<? extends TableColumn<R,?>> sortColumns, SortDirection sortDirection) { if (_unfilteredData == null) { throw new IllegalStateException("fetch() must be called first"); } Collections.sort(getData(), getComparator(sortColumns, sortDirection)); } public int getFilteredRowCount() { return getRowCount(); } /** * Get a comparator that can be used to sort rows. If there exists a compound * sorting order for the primary sort column, the comparator will take this * into account. * * @return the sort columns */ @SuppressWarnings("unchecked") public Comparator<R> getComparator(List<? extends TableColumn<R,?>> sortColumns, SortDirection sortDirection) { if (sortColumns == null || sortColumns.size()== 0) { return (Comparator<R>) new Comparator<R>() { public int compare(R e1, R e2){ return 0; } }; } if (sortColumns.size() == 1) { return sortColumns.get(0).getComparator(sortDirection); } return new CompoundColumnComparator<R>(sortColumns, sortDirection); } @SuppressWarnings("unchecked") public List<R> getData() { return (List<R>) getWrappedData(); } /** * Returns whether the specified entity matches the criterion for the column. * @param list * @param e the entity to be matched * @return boolean */ private boolean matches(Object datum, List<? extends Criterion<?>> criteria) { for (Criterion<?> criterion : criteria) { if (!criterion.matches(datum)) { return false; } } return true; } @Override public int getRowCount() { return _baseModel.getRowCount(); } @Override public Object getRowData() { return _baseModel.getRowData(); } @Override public int getRowIndex() { return _baseModel.getRowIndex(); } @Override public Object getWrappedData() { return _baseModel.getWrappedData(); } @Override public boolean isRowAvailable() { return _baseModel.isRowAvailable(); } @Override public void setRowIndex(int rowIndex) { _baseModel.setRowIndex(rowIndex); } @Override public void setWrappedData(Object data) { _baseModel.setWrappedData(data); } @Override public void addDataModelListener(DataModelListener listener) { _baseModel.addDataModelListener(listener); } @Override public DataModelListener[] getDataModelListeners() { return _baseModel.getDataModelListeners(); } @Override public Iterator<R> iterator() { return new Iterator<R>() { private int row = 0; @Override public boolean hasNext() { return row < getRowCount(); } @Override public R next() { setRowIndex(row++); return (R) getRowData(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }