/* FakerMatrixModel.java {{IS_NOTE Purpose: Description: History: Feb 23, 2012 2:29:27 PM , Created by jumperchen }}IS_NOTE Copyright (C) 2012 Potix Corporation. All Rights Reserved. */ package org.zkoss.support.bivision; import java.util.AbstractList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.zkoss.lang.Objects; import org.zkoss.zkmax.zul.MatrixModel; import org.zkoss.zul.AbstractListModel; import org.zkoss.zul.event.ListDataEvent; import org.zkoss.zul.ext.Sortable; /** * A faker MatrixModel is used to handle a unlimited big table data. * @author jumperchen */ public class FakerMatrixModel extends AbstractListModel implements MatrixModel, Sortable { // a rendering function private interface Fun { public T apply(int index); } // A faker of key list implementation that contains a key to speed up the performance. // Because Java Collection framework didn't handle it well for huge data, it will // go through whole the list entries to receive the value for those methods, // hashCode(), equals(), and toString() private class FakerKeyList extends AbstractList { final int _size; Map _updateCache = new HashMap (); final Fun _fn; final String _key; public FakerKeyList(int size, int key, Fun fn) { _size = size; _key = key + "_" + size; _fn = fn; } @Override public int size() { return _size; } @Override public boolean isEmpty() { return _size == 0; } @Override public T get(int index) { // if changed, returns the changed value Object val = _updateCache.get(String.valueOf(index)); if (val != null) return (T) val; return (T) _fn.apply(index); } @Override public T set(int index, T element) { _updateCache.put(String.valueOf(index), element); return element; } @Override public int hashCode() { return _key.hashCode(); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof FakerKeyList) { return _key.equals(((FakerKeyList)(obj))._key); } return false; } @Override public String toString() { return _key; } } private int _colSize; private int _rowSize; private Map> _rowCache; private List _headerData; private Comparator _sorting; private boolean _sortDir = true; @SuppressWarnings("unchecked") public void sort(Comparator cmpr, boolean ascending) { _sorting = cmpr; _sortDir = ascending; fireEvent(ListDataEvent.STRUCTURE_CHANGED, -1, -1); } public String getSortDirection(Comparator cmpr) { if (Objects.equals(_sorting, cmpr)) return _sortDir ? "ascending" : "descending"; return "natural"; } public FakerMatrixModel(int colSize, int rowSize) { _colSize = colSize; _rowSize = rowSize; _rowCache = new HashMap>(); _headerData = new FakerKeyList(colSize, 0, new Fun() { public Object apply(int index) { return "Header x = " + index; }}); } public void update(Integer[] axis, String value) { List list = _rowCache.get(String.valueOf(axis[1])); list.set(axis[0], value); this.fireEvent(ListDataEvent.CONTENTS_CHANGED, axis[0], axis[1]); } public void setSize(int colSize, int rowSize) { _colSize = colSize; _rowSize = rowSize; this.fireEvent(ListDataEvent.STRUCTURE_CHANGED, -1, -1); } @SuppressWarnings("unchecked") public Row getElementAt(int index) { final int rowIndex = _sortDir ? index : getSize() - index - 1; // handle the sorting final String key = String.valueOf(rowIndex); List value = _rowCache.get(key); if (value == null) { value = new FakerKeyList(_colSize, rowIndex, new Fun() { public Object apply(int index) { return "y = " + rowIndex; }}); _rowCache.put(key, value); } return (Row) value; } public int getSize() { return _rowSize; } public int getColumnSize() { return _colSize; } public int getHeadSize() { return 1; } @SuppressWarnings("unchecked") public Head getHeadAt(int rowIndex) { return (Head) _headerData; } @SuppressWarnings("unchecked") public Cell getCellAt(Row rowData, int columnIndex) { return (Cell) rowData.get(columnIndex); } @SuppressWarnings("unchecked") public Header getHeaderAt(Head headData, int columnIndex) { return (Header) headData.get(columnIndex); } }