package com.aggrepoint.utils.linkedtable; import java.util.Hashtable; import java.util.Vector; import com.aggrepoint.utils.TypeCast; /** * @param <TColumn> * Type of column key * @param <TRow> * Type of row key * @param <TValue> * Table content type */ public class LinkedTable<TColumn, TRow, TValue> extends PropObject { ColumnHead<TColumn, TRow, TValue> m_colHead; ColumnHead<TColumn, TRow, TValue> m_colTail; RowHead<TColumn, TRow, TValue> m_rowHead; RowHead<TColumn, TRow, TValue> m_rowTail; public TValue[][] toArray(TValue[][] arr) { if (m_rowHead == null || m_colHead == null) return null; int rc = getRowCount(); int cc = getColumnCount(); boolean bCreate = true; if (arr.length != rc) bCreate = true; else for (int i = 0; i < arr.length; i++) if (arr[i].length != cc) { bCreate = true; break; } if (bCreate) arr = TypeCast.cast(java.lang.reflect.Array.newInstance(arr .getClass().getComponentType().getComponentType(), rc, cc)); RowHead<TColumn, TRow, TValue> row = m_rowHead; for (int r = 0; r < rc; r++) { Node<TColumn, TRow, TValue> node = row.m_firstNode; int c = 0; while (node != null) { arr[r][c++] = node.m_value; node = node.m_nextInRow; } row = row.m_nextHead; } return arr; } RowHead<TColumn, TRow, TValue> getRow(TRow row, boolean createIfNotExists) { if (m_rowHead == null) { if (!createIfNotExists) return null; // This is the first row // Create the row m_rowHead = m_rowTail = new RowHead<TColumn, TRow, TValue>(row); // { create nodes in the row ColumnHead<TColumn, TRow, TValue> col = m_colHead; Node<TColumn, TRow, TValue> nodePrev = null; while (col != null) { Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( col, m_rowHead, null); col.m_firstNode = col.m_lastNode = node; if (nodePrev == null) m_rowHead.m_firstNode = node; else nodePrev.m_nextInRow = node; nodePrev = node; col = col.m_nextHead; } m_rowHead.m_lastNode = nodePrev; // } return m_rowHead; } RowHead<TColumn, TRow, TValue> r = m_rowHead.find(row); if (r != null) return r; if (!createIfNotExists) return null; // append a row r = new RowHead<TColumn, TRow, TValue>(row); m_rowTail.m_nextHead = r; m_rowTail = r; // { create nodes in the row ColumnHead<TColumn, TRow, TValue> col = m_colHead; Node<TColumn, TRow, TValue> nodePrev = null; while (col != null) { Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( col, r, null); col.m_lastNode.m_nextInColumn = node; col.m_lastNode = node; if (nodePrev == null) r.m_firstNode = node; else nodePrev.m_nextInRow = node; nodePrev = node; col = col.m_nextHead; } r.m_lastNode = nodePrev; // } return r; } ColumnHead<TColumn, TRow, TValue> getColumn(TColumn column, boolean createIfNotExists) { if (m_colHead == null) { if (!createIfNotExists) return null; // This is the first column // Create the column m_colHead = m_colTail = new ColumnHead<TColumn, TRow, TValue>( column); // { create nodes in the column RowHead<TColumn, TRow, TValue> row = m_rowHead; Node<TColumn, TRow, TValue> nodePrev = null; while (row != null) { Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( m_colHead, row, null); row.m_firstNode = row.m_lastNode = node; if (nodePrev == null) m_colHead.m_firstNode = node; else nodePrev.m_nextInColumn = node; nodePrev = node; row = row.m_nextHead; } m_colHead.m_lastNode = nodePrev; // } return m_colHead; } ColumnHead<TColumn, TRow, TValue> col = m_colHead.find(column); if (col != null) return col; if (!createIfNotExists) return null; // append a column col = new ColumnHead<TColumn, TRow, TValue>(column); m_colTail.m_nextHead = col; m_colTail = col; // { create nodes in the column RowHead<TColumn, TRow, TValue> row = m_rowHead; Node<TColumn, TRow, TValue> nodePrev = null; while (row != null) { Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( col, row, null); row.m_lastNode.m_nextInRow = node; row.m_lastNode = node; if (nodePrev == null) col.m_firstNode = node; else nodePrev.m_nextInColumn = node; nodePrev = node; row = row.m_nextHead; } col.m_lastNode = nodePrev; // } return col; } public ColumnHead<TColumn, TRow, TValue> addColumn(TColumn col) { return getColumn(col, true); } public RowHead<TColumn, TRow, TValue> addRow(TRow row) { return getRow(row, true); } public void insertColumn(TColumn col, int posi) { ColumnHead<TColumn, TRow, TValue> newCol = new ColumnHead<TColumn, TRow, TValue>( col); if (posi == 0) { newCol.m_nextHead = m_colHead; m_colHead = newCol; if (m_colTail == null) m_colTail = newCol; // create nodes in the column RowHead<TColumn, TRow, TValue> row = m_rowHead; Node<TColumn, TRow, TValue> nodePrev = null; while (row != null) { Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( newCol, row, null); node.m_nextInRow = row.m_firstNode; row.m_firstNode = node; if (row.m_lastNode == null) row.m_lastNode = node; if (nodePrev == null) newCol.m_firstNode = node; else nodePrev.m_nextInColumn = node; nodePrev = node; row = row.m_nextHead; } newCol.m_lastNode = nodePrev; return; } ColumnHead<TColumn, TRow, TValue> prev = m_colHead; for (int i = 0; i < posi - 1 && prev != null; i++) prev = prev.m_nextHead; if (prev == null) return; newCol.m_nextHead = prev.m_nextHead; prev.m_nextHead = newCol; if (newCol.m_nextHead == null) m_colTail = newCol; // create nodes in the column RowHead<TColumn, TRow, TValue> row = m_rowHead; Node<TColumn, TRow, TValue> nodePrev = null; while (row != null) { Node<TColumn, TRow, TValue> pn = row.m_firstNode; for (int i = 0; i < posi - 1 && pn != null; i++) pn = pn.m_nextInRow; Node<TColumn, TRow, TValue> node = new Node<TColumn, TRow, TValue>( newCol, row, null); node.m_nextInRow = pn.m_nextInRow; pn.m_nextInRow = node; if (node.m_nextInRow == null) row.m_lastNode = node; if (nodePrev == null) newCol.m_firstNode = node; else nodePrev.m_nextInColumn = node; nodePrev = node; row = row.m_nextHead; } newCol.m_lastNode = nodePrev; } public void set(TColumn column, TRow row, TValue value) { ColumnHead<TColumn, TRow, TValue> col = getColumn(column, true); RowHead<TColumn, TRow, TValue> r = getRow(row, true); Node<TColumn, TRow, TValue> node = col.m_firstNode; while (node != null) { if (node.m_row == r) { node.m_value = value; return; } node = node.m_nextInColumn; } } Object add(TValue a, TValue b) { if (a instanceof Double) return ((Double) a).doubleValue() + ((Double) b).doubleValue(); else if (a instanceof Float) return ((Float) a).doubleValue() + ((Float) b).doubleValue(); else if (a instanceof Long) return ((Long) a).doubleValue() + ((Long) b).doubleValue(); else if (a instanceof Integer) return ((Integer) a).doubleValue() + ((Integer) b).doubleValue(); else if (a instanceof Short) return ((Short) a).doubleValue() + ((Short) b).doubleValue(); return b; } @SuppressWarnings("unchecked") public void add(TColumn column, TRow row, TValue value) { ColumnHead<TColumn, TRow, TValue> col = getColumn(column, true); RowHead<TColumn, TRow, TValue> r = getRow(row, true); Node<TColumn, TRow, TValue> node = col.m_firstNode; while (node != null) { if (node.m_row == r) { node.m_value = (TValue) add(node.m_value, value); return; } node = node.m_nextInColumn; } } public TValue get(TColumn column, TRow row) { if (m_colHead == null || m_rowHead == null) return null; ColumnHead<TColumn, TRow, TValue> col = m_colHead.find(column); RowHead<TColumn, TRow, TValue> r = m_rowHead.find(row); if (col == null || r == null) return null; Node<TColumn, TRow, TValue> node = col.m_firstNode; while (node != null) { if (node.m_row == r) { return node.m_value; } node = node.m_nextInColumn; } return null; } public TColumn getFirstColumn() { if (m_colHead == null) return null; return m_colHead.m_value; } public TRow getFirstRow() { if (m_rowHead == null) return null; return m_rowHead.m_value; } public Vector<TColumn> getColumns() { if (m_colHead == null) return null; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { col = col.m_nextHead; } Vector<TColumn> cols = new Vector<TColumn>(); col = m_colHead; while (col != null) { cols.add(col.m_value); col = col.m_nextHead; } return cols; } public Vector<Hashtable<String, Object>> getColumnProps() { if (m_colHead == null) return null; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { col = col.m_nextHead; } Vector<Hashtable<String, Object>> cols = new Vector<Hashtable<String, Object>>(); col = m_colHead; while (col != null) { cols.add(col.getProps()); col = col.m_nextHead; } return cols; } public Vector<TRow> getRows() { if (m_rowHead == null) return null; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { row = row.m_nextHead; } Vector<TRow> rows = new Vector<TRow>(); row = m_rowHead; while (row != null) { rows.add(row.m_value); row = row.m_nextHead; } return rows; } public Vector<Hashtable<String, Object>> getRowProps() { if (m_rowHead == null) return null; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { row = row.m_nextHead; } Vector<Hashtable<String, Object>> rows = new Vector<Hashtable<String, Object>>(); row = m_rowHead; while (row != null) { rows.add(row.getProps()); row = row.m_nextHead; } return rows; } public int getColumnCount() { if (m_colHead == null) return 0; int c = 0; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { c++; col = col.m_nextHead; } return c; } public int getRowCount() { if (m_rowHead == null) return 0; int c = 0; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { c++; row = row.m_nextHead; } return c; } int findRowPosi(TRow row) { if (row == null) return -1; RowHead<TColumn, TRow, TValue> r = m_rowHead; int posi = 0; while (r != null) { if (r.m_value.equals(row)) return posi; posi++; r = r.m_nextHead; } return -1; } RowHead<TColumn, TRow, TValue> getRowHead(int posi) { if (posi < 0) return null; RowHead<TColumn, TRow, TValue> r = m_rowHead; while (r != null) { if (posi == 0) return r; posi--; r = r.m_nextHead; } return null; } public TRow getRow(int posi) { RowHead<TColumn, TRow, TValue> head = getRowHead(posi); if (head == null) return null; return head.m_value; } int findColumnPosi(TColumn column) { if (column == null) return -1; ColumnHead<TColumn, TRow, TValue> col = m_colHead; int posi = 0; while (col != null) { if (col.m_value.equals(column)) return posi; posi++; col = col.m_nextHead; } return -1; } ColumnHead<TColumn, TRow, TValue> getColumnHead(int posi) { if (posi < 0) return null; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { if (posi == 0) return col; posi--; col = col.m_nextHead; } return null; } public TColumn getColumn(int posi) { ColumnHead<TColumn, TRow, TValue> head = getColumnHead(posi); if (head == null) return null; return head.m_value; } public void deleteColumn(TColumn column) { int posi = findColumnPosi(column); if (posi == -1) return; if (posi == 0) { if (m_colTail == m_colHead) m_colHead = m_colTail = null; else m_colHead = m_colHead.m_nextHead; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { if (m_colHead == null) row.m_firstNode = row.m_lastNode = null; else row.m_firstNode = row.m_firstNode.m_nextInRow; row = row.m_nextHead; } } else { ColumnHead<TColumn, TRow, TValue> col = m_colHead; posi--; for (int i = 0; i < posi; i++) col = col.m_nextHead; if (m_colTail == col.m_nextHead) m_colTail = col; col.m_nextHead = col.m_nextHead.m_nextHead; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { Node<TColumn, TRow, TValue> node = row.m_firstNode; for (int i = 0; i < posi; i++) node = node.m_nextInRow; if (row.m_lastNode == node.m_nextInRow) row.m_lastNode = node; node.m_nextInRow = node.m_nextInRow.m_nextInRow; row = row.m_nextHead; } } } public void deleteRow(TRow row) { int posi = findRowPosi(row); if (posi == -1) return; if (posi == 0) { if (m_rowTail == m_rowHead) m_rowHead = m_rowTail = null; else m_rowHead = m_rowHead.m_nextHead; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { if (m_rowHead == null) col.m_firstNode = col.m_lastNode = null; else col.m_firstNode = col.m_firstNode.m_nextInColumn; col = col.m_nextHead; } } else { RowHead<TColumn, TRow, TValue> r = m_rowHead; posi--; for (int i = 0; i < posi; i++) r = r.m_nextHead; if (m_rowTail == r.m_nextHead) m_rowTail = r; r.m_nextHead = r.m_nextHead.m_nextHead; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { Node<TColumn, TRow, TValue> node = col.m_firstNode; for (int i = 0; i < posi; i++) node = node.m_nextInColumn; if (col.m_lastNode == node.m_nextInColumn) col.m_lastNode = node; node.m_nextInColumn = node.m_nextInColumn.m_nextInColumn; col = col.m_nextHead; } } } TValue[] getValuesInColumn(ColumnHead<TColumn, TRow, TValue> col, TValue[] vals) { if (col == null) return null; int count = getRowCount(); if (vals.length != count) vals = TypeCast.cast(java.lang.reflect.Array.newInstance(vals .getClass().getComponentType(), count)); Node<TColumn, TRow, TValue> node = col.m_firstNode; int c = 0; while (node != null) { vals[c++] = node.m_value; node = node.m_nextInColumn; } return vals; } public TValue[] getValuesInColumn(TColumn column, TValue[] vals) { return getValuesInColumn(getColumn(column, false), vals); } public TValue[] getValuesInColumn(int posi, TValue[] vals) { return getValuesInColumn(getColumnHead(posi), vals); } TValue[] getValuesInRow(RowHead<TColumn, TRow, TValue> r, TValue[] vals) { if (r == null) return null; int count = getColumnCount(); if (vals.length != count) vals = TypeCast.cast(java.lang.reflect.Array.newInstance(vals .getClass().getComponentType(), count)); Node<TColumn, TRow, TValue> node = r.m_firstNode; int c = 0; while (node != null) { vals[c++] = node.m_value; node = node.m_nextInRow; } return vals; } public TValue[] getValuesInRow(TRow row, TValue[] vals) { return getValuesInRow(getRow(row, false), vals); } public TValue[] getValuesInRow(int posi, TValue[] vals) { return getValuesInRow(getRowHead(posi), vals); } @SuppressWarnings("unchecked") public void sortColumns(TColumn[] order) { if (order == null || m_colHead == null) return; // { calculate the order by position id int[] orders = new int[getColumnCount()]; TColumn[] cols = TypeCast.cast(new Object[orders.length]); int i = 0; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { cols[i++] = col.m_value; col = col.m_nextHead; } i = 0; for (TColumn c : order) { for (int j = 0; j < cols.length; j++) { if (cols[j] == null) continue; if (cols[j].equals(c)) { orders[i++] = j; cols[j] = null; continue; } } } for (int j = 0; j < cols.length; j++) if (cols[j] != null) orders[i++] = j; // } // {sort header Object[] heads = new Object[orders.length]; Object[] newHeads = new Object[orders.length]; i = 0; col = m_colHead; while (col != null) { heads[i++] = col; col = col.m_nextHead; } for (int j = 0; j < orders.length; j++) newHeads[j] = heads[orders[j]]; m_colHead = (ColumnHead<TColumn, TRow, TValue>) newHeads[0]; m_colTail = (ColumnHead<TColumn, TRow, TValue>) newHeads[orders.length - 1]; m_colTail.m_nextHead = null; for (int j = 0; j < orders.length - 1; j++) ((ColumnHead<TColumn, TRow, TValue>) newHeads[j]).m_nextHead = TypeCast .cast(newHeads[j + 1]); // } // { sort data Object[] nodes = new Object[orders.length]; Object[] newNodes = new Object[orders.length]; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { Node<TColumn, TRow, TValue> node = row.m_firstNode; i = 0; while (node != null) { nodes[i++] = node; node = node.m_nextInRow; } for (int j = 0; j < orders.length; j++) newNodes[j] = nodes[orders[j]]; row.m_firstNode = (Node<TColumn, TRow, TValue>) newNodes[0]; row.m_lastNode = (Node<TColumn, TRow, TValue>) newNodes[orders.length - 1]; row.m_lastNode.m_nextInRow = null; for (int j = 0; j < orders.length - 1; j++) ((Node<TColumn, TRow, TValue>) newNodes[j]).m_nextInRow = (Node<TColumn, TRow, TValue>) newNodes[j + 1]; row = row.m_nextHead; } // } } @SuppressWarnings("unchecked") public void sortRows(TRow[] order) { if (order == null || m_rowHead == null) return; // { calculate the order by position id int[] orders = new int[getRowCount()]; TRow[] rows = TypeCast.cast(new Object[orders.length]); int i = 0; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { rows[i++] = row.m_value; row = row.m_nextHead; } i = 0; for (TRow r : order) { for (int j = 0; j < rows.length; j++) { if (rows[j] == null) continue; if (rows[j].equals(r)) { orders[i++] = j; rows[j] = null; continue; } } } for (int j = 0; j < rows.length; j++) if (rows[j] != null) orders[i++] = j; // } // {sort header Object[] heads = new Object[orders.length]; Object[] newHeads = new Object[orders.length]; i = 0; row = m_rowHead; while (row != null) { heads[i++] = row; row = row.m_nextHead; } for (int j = 0; j < orders.length; j++) newHeads[j] = heads[orders[j]]; m_rowHead = (RowHead<TColumn, TRow, TValue>) newHeads[0]; m_rowTail = (RowHead<TColumn, TRow, TValue>) newHeads[orders.length - 1]; m_rowTail.m_nextHead = null; for (int j = 0; j < orders.length - 1; j++) ((RowHead<TColumn, TRow, TValue>) newHeads[j]).m_nextHead = TypeCast .cast(newHeads[j + 1]); // } // { sort data Object[] nodes = new Object[orders.length]; Object[] newNodes = new Object[orders.length]; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { Node<TColumn, TRow, TValue> node = col.m_firstNode; i = 0; while (node != null) { nodes[i++] = node; node = node.m_nextInColumn; } for (int j = 0; j < orders.length; j++) newNodes[j] = nodes[orders[j]]; col.m_firstNode = (Node<TColumn, TRow, TValue>) newNodes[0]; col.m_lastNode = (Node<TColumn, TRow, TValue>) newNodes[orders.length - 1]; col.m_lastNode.m_nextInColumn = null; for (int j = 0; j < orders.length - 1; j++) ((Node<TColumn, TRow, TValue>) newNodes[j]).m_nextInColumn = (Node<TColumn, TRow, TValue>) newNodes[j + 1]; col = col.m_nextHead; } // } } public void mergeColumn(TColumn from, TColumn to, INodeMerger merger) { if (m_colHead == null) return; ColumnHead<TColumn, TRow, TValue> colFrom = m_colHead.find(from); ColumnHead<TColumn, TRow, TValue> colTo = m_colHead.find(to); if (colFrom == null) return; if (colTo == null) { colFrom.m_value = to; return; } Node<TColumn, TRow, TValue> ndFrom = colFrom.m_firstNode; Node<TColumn, TRow, TValue> ndTo = colTo.m_firstNode; while (ndFrom != null && ndTo != null) { ndTo.m_value = merger.merge(ndFrom.m_value, ndTo.m_value); ndFrom = ndFrom.m_nextInColumn; ndTo = ndTo.m_nextInColumn; } deleteColumn(from); } public void mergeColumn(TColumn[] froms, TColumn to, INodeMerger merger) { if (m_colHead == null) return; for (int i = 0; i < froms.length; i++) mergeColumn(froms[i], to, merger); } public void mergeRow(TRow from, TRow to, INodeMerger merger) { if (m_rowHead == null) return; RowHead<TColumn, TRow, TValue> rowFrom = m_rowHead.find(from); RowHead<TColumn, TRow, TValue> rowTo = m_rowHead.find(to); if (rowFrom == rowTo) return; if (rowFrom == null) return; if (rowTo == null) { rowFrom.m_value = to; return; } Node<TColumn, TRow, TValue> ndFrom = rowFrom.m_firstNode; Node<TColumn, TRow, TValue> ndTo = rowTo.m_firstNode; while (ndFrom != null && ndTo != null) { ndTo.m_value = merger.merge(ndFrom.m_value, ndTo.m_value); ndFrom = ndFrom.m_nextInRow; ndTo = ndTo.m_nextInRow; } deleteRow(from); } public void mergeRow(TRow[] froms, TRow to, INodeMerger merger) { if (m_rowHead == null) return; for (int i = 0; i < froms.length; i++) mergeRow(froms[i], to, merger); } public void updateColumns(Hashtable<TColumn, TColumn> map) { if (m_colHead == null) return; ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { TColumn c = map.get(col.m_value); if (c != null) col.m_value = c; col = col.m_nextHead; } } public void updateRows(Hashtable<TRow, TRow> map) { if (m_rowHead == null) return; RowHead<TColumn, TRow, TValue> row = m_rowHead; while (row != null) { TRow r = map.get(row.m_value); if (r != null) row.m_value = r; row = row.m_nextHead; } } public boolean setColumnProp(TColumn col, String name, Object value) { ColumnHead<TColumn, TRow, TValue> head = getColumn(col, false); if (head == null) return false; head.setProp(name, value); return true; } public boolean setColumnProp(int col, String name, Object value) { ColumnHead<TColumn, TRow, TValue> head = getColumnHead(col); if (head == null) return false; head.setProp(name, value); return true; } public void copyColumnToProp(String name) { ColumnHead<TColumn, TRow, TValue> col = m_colHead; while (col != null) { col.setProp(name, col.m_value); col = col.m_nextHead; } } public Object getColumnProp(TColumn col, String name) { ColumnHead<TColumn, TRow, TValue> c = getColumn(col, false); if (c == null) return null; if (name == null) return c.getProps(); else return c.getProp(name); } public Object getColumnProp(int posi, String name) { ColumnHead<TColumn, TRow, TValue> c = getColumnHead(posi); if (c == null) return null; if (name == null) return c.getProps(); else return c.getProp(name); } public boolean setRowProp(TRow row, String name, Object value) { RowHead<TColumn, TRow, TValue> head = getRow(row, false); if (head == null) return false; head.setProp(name, value); return true; } public boolean setRowProp(int row, String name, Object value) { RowHead<TColumn, TRow, TValue> head = getRowHead(row); if (head == null) return false; head.setProp(name, value); return true; } public void copyRowToProp(String name) { RowHead<TColumn, TRow, TValue> r = m_rowHead; while (r != null) { r.setProp(name, r.m_value); r = r.m_nextHead; } } public Object getRowProp(TRow row, String name) { RowHead<TColumn, TRow, TValue> head = getRow(row, false); if (head == null) return false; if (name == null) return head.getProps(); else return head.getProp(name); } public Object getRowProp(int posi, String name) { RowHead<TColumn, TRow, TValue> head = getRowHead(posi); if (head == null) return false; if (name == null) return head.getProps(); else return head.getProp(name); } }