package com.revolsys.swing.table.counts; import java.awt.Component; import java.util.ArrayList; import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.JTable; import javax.swing.RowSorter; import javax.swing.RowSorter.SortKey; import javax.swing.SortOrder; import javax.swing.event.TableModelListener; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import org.jdesktop.swingx.decorator.ColorHighlighter; import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.table.TableColumnExt; import com.revolsys.awt.WebColors; import com.revolsys.io.PathNameProxy; import com.revolsys.swing.parallel.Invoke; import com.revolsys.swing.table.AbstractTableModel; import com.revolsys.swing.table.BaseJTable; import com.revolsys.util.Counter; import com.revolsys.util.count.CategoryLabelCountMap; import com.revolsys.util.count.LabelCountMap; public class LabelCountMapTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; private String selectedCountName; private String selectedLabel; private CategoryLabelCountMap categoryLabelCountMap = new CategoryLabelCountMap(); private final List<String> countNames = new ArrayList<>(); private final List<String> labels = new ArrayList<>(); private int columnCount = 1; public LabelCountMapTableModel() { } public void addCount(final CharSequence label, final CharSequence countName) { addCount(label, countName, 1); } public void addCount(final CharSequence label, final CharSequence countName, final long count) { if (label != null && countName != null) { final LabelCountMap labelCountMap = getLabelCountMap(label, countName); labelCountMap.addCount(label, count); } } public void addCount(final PathNameProxy pathNameProxy, final CharSequence countName) { addCount(pathNameProxy, countName, 1); } public void addCount(final PathNameProxy pathNameProxy, final CharSequence countName, final long count) { if (pathNameProxy != null) { final CharSequence label = pathNameProxy.getPathName(); addCount(label, countName, count); } } private void addCountColumn(final CharSequence name) { Invoke.later(() -> { this.columnCount++; final int columnIndex = 1 + this.countNames.indexOf(name); fireTableStructureChanged(); final BaseJTable table = getTable(); if (table != null) { final TableColumn column = new TableColumnExt(columnIndex); setColumnWidth(columnIndex, column); table.addColumn(column); } }); } public void addCountNameColumn(final CharSequence name) { getLabelCountMap(name); } public boolean addCountNameColumn(final int index, final CharSequence countName) { boolean added = false; synchronized (this.countNames) { if (!this.countNames.contains(countName)) { added = true; this.countNames.add(index, countName.toString()); } } if (added) { addCountColumn(countName); } return added; } public void addCountNameColumns(final CharSequence... names) { for (final CharSequence name : names) { getLabelCountMap(name); } fireTableStructureChanged(); } public void clearCounts() { this.categoryLabelCountMap.clear(); } public void clearCounts(final CharSequence countName) { final LabelCountMap labelCountMap = getLabelCountMap(countName); labelCountMap.clearCounts(); } @Override public Class<?> getColumnClass(final int columnIndex) { if (columnIndex == 0) { return String.class; } else { return Long.class; } } @Override public int getColumnCount() { return this.columnCount; } @Override public String getColumnName(final int columnIndex) { if (columnIndex < 1) { return getLabelTitle(); } else { final int index = columnIndex - 1; if (index < this.countNames.size()) { return this.countNames.get(index); } return null; } } public Long getCount(final CharSequence label, final CharSequence countName) { return this.categoryLabelCountMap.getCount(countName, label); } public Counter getCounter(final CharSequence label, final CharSequence countName) { final LabelCountMap labelCountMap = getLabelCountMap(label, countName); return labelCountMap.getCounter(label); } public LabelCountMap getLabelCountMap(final CharSequence countName) { boolean added = false; try { synchronized (this.countNames) { if (!this.countNames.contains(countName)) { added = true; this.countNames.add(countName.toString()); } return this.categoryLabelCountMap.getLabelCountMap(countName); } } finally { if (added) { addCountColumn(countName); } } } public LabelCountMap getLabelCountMap(final CharSequence label, final CharSequence countName) { final LabelCountMap labelCountMap = getLabelCountMap(countName); newTypePathRow(label); return labelCountMap; } public String getLabelTitle() { return this.categoryLabelCountMap.getLabelTitle(); } @Override public int getRowCount() { return this.labels.size(); } public CategoryLabelCountMap getStatistics() { return this.categoryLabelCountMap; } @Override public Object getValueAt(final int rowIndex, final int columnIndex) { final String label = this.labels.get(rowIndex); switch (columnIndex) { case 0: return label; default: final String countName = this.countNames.get(columnIndex - 1); final Long value = getCount(label, countName); return value; } } @Override public boolean isCellEditable(final int rowIndex, final int columnIndex) { return false; } public void newStatistics(final CharSequence countName, final CharSequence label) { addCountNameColumn(countName); newTypePathRow(label); } public BaseJTable newTable() { final BaseJTable table = new BaseJTable(this); setTable(table); final TableColumnModel columnModel = table.getColumnModel(); for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) { final int columnIndex1 = columnIndex; final TableColumn column = columnModel.getColumn(columnIndex1); setColumnWidth(columnIndex1, column); } table.setAutoCreateColumnsFromModel(false); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); final RowSorter<? extends TableModel> rowSorter = table.getRowSorter(); final SortKey sortKey = new SortKey(0, SortOrder.ASCENDING); rowSorter.setSortKeys(Arrays.asList(sortKey)); table.addHighlighter( new ColorHighlighter((final Component renderer, final ComponentAdapter adapter) -> { final int row = adapter.convertRowIndexToModel(adapter.row); if (getValueAt(row, 0).equals(LabelCountMapTableModel.this.selectedLabel)) { return true; } return false; }, WebColors.ForestGreen, WebColors.Yellow, WebColors.DarkGreen, WebColors.Yellow)); table.addHighlighter( new ColorHighlighter((final Component renderer, final ComponentAdapter adapter) -> { final int column = adapter.convertColumnIndexToModel(adapter.column); final int row = adapter.convertRowIndexToModel(adapter.row); if (getValueAt(row, 0).equals(LabelCountMapTableModel.this.selectedLabel)) { if (getColumnName(column).equals(LabelCountMapTableModel.this.selectedCountName)) { return true; } } return false; }, WebColors.Yellow, WebColors.DarkGreen, WebColors.Gold, WebColors.DarkGreen)); return table; } public void newTypePathRow(final CharSequence label) { int index = -1; try { synchronized (this.labels) { final String labelString = label.toString(); if (!this.labels.contains(labelString)) { index = this.labels.size(); Invoke.andWait(() -> { this.labels.add(labelString); }); } } } finally { if (index != -1) { fireTableRowsInserted(index, index); } } } public void refresh() { final int rowCount = getRowCount(); if (rowCount > 0) { try { fireTableRowsUpdated(0, rowCount - 1); if (this.selectedCountName != null && this.selectedLabel != null) { selectLabelCountCell(this.selectedLabel, this.selectedCountName); } } catch (final Throwable t) { } } } @Override public void removeTableModelListener(final TableModelListener l) { } public void selectLabelCountCell(final CharSequence label, final CharSequence countName) { this.selectedLabel = label.toString(); this.selectedCountName = countName.toString(); final BaseJTable table = getTable(); table.repaint(); } public void selectLabelCountCell(final PathNameProxy pathNameProxy, final String countName) { final CharSequence label = pathNameProxy.getPathName(); selectLabelCountCell(label, countName); } private void setColumnWidth(final int columnIndex, final TableColumn column) { int minWidth = 80; if (columnIndex == 0) { minWidth = 270; } final String columnName = getColumnName(columnIndex); final int width = Math.max(minWidth, columnName.length() * 8); column.setMinWidth(width); column.setPreferredWidth(width); } public LabelCountMapTableModel setLabelTitle(final String labelTitle) { this.categoryLabelCountMap.setLabelTitle(labelTitle); fireTableStructureChanged(); return this; } public void setStatistics(final Map<String, LabelCountMap> statisticsMap) { try { this.categoryLabelCountMap = new CategoryLabelCountMap(statisticsMap); for (final Entry<String, LabelCountMap> entry : statisticsMap.entrySet()) { final String countName = entry.getKey(); addCountNameColumn(countName); final LabelCountMap labelCountMap = entry.getValue(); for (final String label : labelCountMap.getLabels()) { newStatistics(countName, label); } } } catch (final ConcurrentModificationException e) { } refresh(); } public void setStatistics(final String statisticName, final LabelCountMap labelCountMap) { addCountNameColumn(statisticName); this.categoryLabelCountMap.setLabelCountMap(statisticName, labelCountMap); } @Override public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) { } public void writeCounts(final Object target) { final String labelTitle = getLabelTitle(); this.categoryLabelCountMap.writeCounts(target, labelTitle, this.countNames); } }