package jrex.ui.model; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ListMultimap; import com.google.common.collect.Table; import com.google.common.collect.Tables; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.table.AbstractTableModel; /** * A table model that can be pivoted. * * @author Mark George <mark.george@otago.ac.nz> */ public class PivotTableModel extends AbstractTableModel { private Table<Integer, Integer, String> table = HashBasedTable.create(); @Override public int getRowCount() { return table.rowKeySet().size(); } @Override public int getColumnCount() { return table.columnKeySet().size(); } @Override public String getColumnName(int columnIndex) { return String.valueOf(columnIndex + 1); } @Override public Class<?> getColumnClass(int columnIndex) { return Object.class; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } @Override public Object getValueAt(int rowIndex, int columnIndex) { return table.get(rowIndex, columnIndex); } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { table.put(rowIndex, columnIndex, aValue.toString()); fireTableCellUpdated(rowIndex, columnIndex); } public void addRow(String[] values) { int rowCount = getRowCount(); for (int i = 0; i < values.length; i++) { table.put(rowCount, i, values[i]); } fireTableRowsInserted(rowCount, rowCount); } public Collection<String> getHeaders() { return table.row(0).values(); } public void transpose() { table = Tables.transpose(table); fireTableStructureChanged(); } public void pivotWideToLong(List<String> preservedColumns, List<String> valueColumns) { Table<Integer, Integer, String> pivoted = HashBasedTable.create(); //### create multimaps so that we can easily get the data by header ###// ListMultimap<String, String> allMap = ArrayListMultimap.create(); Map<Integer, String> headers = table.row(0); for (Map.Entry<Integer, String> col : headers.entrySet()) { for (Integer row : table.rowKeySet()) { if (!row.equals(0)) { allMap.put(col.getValue(), table.get(row, col.getKey())); } } } // create multimap for preserved columns only ListMultimap<String, String> preservedMap = ArrayListMultimap.create(); for (String preservedColumn : preservedColumns) { preservedMap.putAll(preservedColumn, allMap.get(preservedColumn)); } // create multimap for value columns only ListMultimap<String, String> valueMap = ArrayListMultimap.create(); for (String valueColumn : valueColumns) { valueMap.putAll(valueColumn, allMap.get(valueColumn)); } //### add headers to privoted table ###// int col = 0; // preserved column headers for (String preservedColumn : preservedColumns) { pivoted.put(0, col, preservedColumn); col++; } // value column headers (key value pairs) pivoted.put(0, col, "Key"); pivoted.put(0, col + 1, "Value"); int row = 1; // headers are row 0, so start at 1 int pCol = 0; int pColSize = preservedColumns.size(); int index = 0; // original row index //### add preserved columns ###/ for (String preservedCol : preservedColumns) { for (String preservedVal : preservedMap.get(preservedCol)) { for (String valueColumn : valueColumns) { // write preserved column pivoted.put(row, pCol, preservedVal); // write value columns List<String> values = new ArrayList<>(valueMap.get(valueColumn)); String value = values.get(index); pivoted.put(row, pColSize, valueColumn); pivoted.put(row++, pColSize + 1, value); } index++; } index = 0; row = 1; pCol++; } table = pivoted; fireTableStructureChanged(); } public void pivotLongToWide(List<String> repeatingColumns, String headerColumn, String valueColumn) { //### create multimaps so that we can easily get the data by header ###// ListMultimap<String, String> allMap = ArrayListMultimap.create(); Map<Integer, String> headers = table.row(0); for (Map.Entry<Integer, String> col : headers.entrySet()) { for (Integer row : table.rowKeySet()) { if (!row.equals(0)) { allMap.put(col.getValue(), table.get(row, col.getKey())); } } } Table<String, String, String> unique = HashBasedTable.create(); for (Integer rowIdx : table.rowKeySet()) { if (rowIdx == 0) { continue; // header row so ignore } StringBuilder key = new StringBuilder(); for (String repeatingColumn : repeatingColumns) { key.append(allMap.get(repeatingColumn).get(rowIdx - 1)); } for (String repeatingColumn : repeatingColumns) { unique.put(key.toString(), repeatingColumn, allMap.get(repeatingColumn).get(rowIdx - 1)); } unique.put(key.toString(), allMap.get(headerColumn).get(rowIdx - 1), allMap.get(valueColumn).get(rowIdx - 1)); } // create a new indexed table for the table model Table<Integer, Integer, String> pivoted = HashBasedTable.create(); int rowIdx = 0; for (Map.Entry<String, Map<String, String>> row : unique.rowMap().entrySet()) { if (rowIdx == 0) { // write headers Set<String> colHeaders = row.getValue().keySet(); int colIdx = 0; for (String header : colHeaders) { pivoted.put(rowIdx, colIdx, header); colIdx++; } } int colIdx = 0; for (Map.Entry<String, String> entry : row.getValue().entrySet()) { pivoted.put(rowIdx + 1, colIdx, entry.getValue()); colIdx++; } rowIdx++; } table = pivoted; fireTableStructureChanged(); } public Table<Integer, Integer, String> getTable() { return table; } }