package com.revolsys.swing.table.counts; import java.io.Writer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import com.revolsys.collection.map.Maps; import com.revolsys.record.io.format.tsv.Tsv; import com.revolsys.record.io.format.tsv.TsvWriter; import com.revolsys.swing.parallel.Invoke; import com.revolsys.swing.table.AbstractTableModel; import com.revolsys.util.Counter; import com.revolsys.util.LongCounter; public class TypeMessageCountsTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; private static final String[] COLUMN_NAMES = { "Type", "Message", "Count" }; private final List<Counter> counters = new ArrayList<>(); private final List<String> types = new ArrayList<>(); private final Map<String, Map<String, Integer>> indexByTypeAndMessage = new TreeMap<>(); private int rowCount = 0; public void addCount(final CharSequence type, final CharSequence message) { addCount(type, message, 1); } public synchronized void addCount(final CharSequence type, final CharSequence message, final long count) { int newIndex = -1; try { synchronized (this.indexByTypeAndMessage) { final String typeName = type.toString(); final String messageName = message.toString(); final Map<String, Integer> indexesByMessage = Maps.get(this.indexByTypeAndMessage, typeName, Maps.<String, Integer> factoryLinkedHash()); final Integer index = indexesByMessage.get(messageName); if (index == null) { newIndex = this.counters.size(); indexesByMessage.put(messageName, newIndex); this.types.add(typeName); final LongCounter counter = new LongCounter(messageName, count); this.counters.add(counter); } else { final Counter counter = this.counters.get(index); final long newCount = counter.add(count); // fireTableCellUpdated(index, 2); } } } finally { if (newIndex != -1) { final int index = newIndex; Invoke.later(() -> { this.rowCount = index + 1; fireTableRowsInserted(index, index); }); } } } public void clearCounts() { synchronized (this.indexByTypeAndMessage) { this.counters.clear(); this.types.clear(); this.indexByTypeAndMessage.clear(); } } @Override public Class<?> getColumnClass(final int columnIndex) { switch (columnIndex) { case 0: return String.class; case 1: return String.class; case 2: return Long.class; default: return String.class; } } @Override public int getColumnCount() { return 3; } @Override public String getColumnName(final int columnIndex) { return COLUMN_NAMES[columnIndex]; } @Override public int getRowCount() { return this.rowCount; } @Override public Object getValueAt(final int rowIndex, final int columnIndex) { final int rowCount = getRowCount(); if (rowIndex >= 0 && rowIndex < rowCount) { final Counter counter = this.counters.get(rowIndex); switch (columnIndex) { case 0: return this.types.get(rowIndex); case 1: return counter.getName(); case 2: return counter.get(); default: return null; } } return null; } @Override public void toTsv(final Writer out) { try ( TsvWriter tsv = Tsv.plainWriter(out)) { tsv.write((Object[])COLUMN_NAMES); long total = 0; for (final Entry<String, Map<String, Integer>> typeEntry : this.indexByTypeAndMessage .entrySet()) { final String type = typeEntry.getKey(); final Map<String, Integer> indexByMessage = typeEntry.getValue(); for (final Entry<String, Integer> messageEntry : indexByMessage.entrySet()) { final String message = messageEntry.getKey(); final Integer index = messageEntry.getValue(); final Counter counter = this.counters.get(index); final long count = counter.get(); total += count; tsv.write(type, message, count); } } tsv.write(null, "Total", total); } } }