package jadex.tools.comanalyzer.chart; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.SubCategoryAxis; import org.jfree.chart.renderer.category.GroupedStackedBarRenderer; import org.jfree.data.KeyToGroupMap; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; /** * This class provides methods for automatic grouping of data for the * GroupedStackedBarRenderer. This class acts as a dataset itselves, but * retrievs the actual data from datasets, that can be added to in order to * group data from the datasets with equal row keys. <br>(e.g. two datasets are * added named "sent" and "received". Both datasets have the same structure like * performatives as rowkeys and agents as columnkeys. The grouping is done by * dividing for example "inform" and "agent1" in group rowkeys like * "inform_sent" for the first dataset and "inform_received" for the second" so * that the bar chart has two bars for "agent1" with the sub categories "sent" * and "received" displaying respectivly either of the data. The different names * for the data in a group are requested from the GroupedStackedBarRenderer) */ public class GroupedCategoryDataset extends AbstractDataset implements CategoryDataset, DatasetChangeListener, Serializable { // -------- attributes -------- /** The datasets for the Group */ protected Map datasets; /** The category axis for groups */ protected SubCategoryAxis axis; /** The bar renderer for groups. */ protected GroupedStackedBarRenderer renderer; /** The map for assigning series to groups */ protected KeyToGroupMap keytogroupmap; /** The list of assigned groups */ protected List keyToGroup; /** * The map with group rowkeys and original rowkeys for easy and fast access * to the original datasets */ protected Map keyToRowKey; /** * The map with group rowkeys and datasets for easy and fast access to the * original datasets */ protected Map keyToDataset; // -------- constructors -------- /** * Creates an initial dataset. */ public GroupedCategoryDataset() { this.datasets = new HashMap(); // this.keytogroupmap = new KeyToGroupMap(); this.keyToGroup = new ArrayList(); this.keyToRowKey = new HashMap(); this.keyToDataset = new HashMap(); } /** * Creates a dataset, that is linked to a GroupedStackedBarRenderer for * assigne the groupmaps and to the SubCategoryAxis to create subcategories. * * @param renderer The GroupedStackedBarRenderer. * @param axis The SubCategoryAxis. */ public GroupedCategoryDataset(GroupedStackedBarRenderer renderer, SubCategoryAxis axis) { this.datasets = new HashMap(); // this.keytogroupmap = new KeyToGroupMap(); this.keyToGroup = new ArrayList(); this.keyToRowKey = new HashMap(); this.keyToDataset = new HashMap(); this.renderer = renderer; this.axis = axis; } /** * Creates a dataset and retrieves the GroupedStackedBarRenderer and the * SubCategoryAxis from the chart. * * @param chart */ public GroupedCategoryDataset(JFreeChart chart) { this.datasets = new HashMap(); // this.keytogroupmap = new KeyToGroupMap(); this.keyToGroup = new ArrayList(); this.keyToRowKey = new HashMap(); this.keyToDataset = new HashMap(); this.renderer = (GroupedStackedBarRenderer) chart.getCategoryPlot().getRenderer(); this.axis = (SubCategoryAxis) chart.getCategoryPlot().getDomainAxis(); } // -------- GroupedCategoryDataset methods -------- /** * Adds a dataset and (if the dataset is not empty) assigning groups for * each rowkey to the renderer and adding subcategories to the axis. * * @param dataset The dataset. * @param name The name for the dataset. */ public void addCategoryDataset(CategoryDataset dataset, String name) { this.datasets.put(dataset, name); boolean changed = false; for (Iterator iter = dataset.getRowKeys().iterator(); iter.hasNext();) { Comparable key = (Comparable) iter.next(); // group name is key + "_" + datasetname String groupKey = key + "_" + name; if (!keyToRowKey.containsKey(groupKey)) { changed = true; keyToRowKey.put(groupKey, key); keyToDataset.put(groupKey, dataset); // Hack: create groupmap with first group name as default // otherwise there is an empty series if (keytogroupmap == null) { keytogroupmap = new KeyToGroupMap(name); } keytogroupmap.mapKeyToGroup(groupKey, name); } } if (changed) { renderer.setSeriesToGroupMap(keytogroupmap); } axis.addSubCategory(name); // notify about changes in the added dataset dataset.addChangeListener(this); } /** * Removes all references to assigned objects. */ public void cleanup() { for (Iterator iter = datasets.keySet().iterator(); iter.hasNext();) { CategoryDataset dataset = (CategoryDataset) iter.next(); dataset.removeChangeListener(this); } datasets.clear(); renderer = null; axis = null; } // -------- CategoryDataset interface -------- // all the methods have group rowkeys as parameters /** * Returns the total number of all rows in the datasets. * * @return The row count. */ public int getRowCount() { return getRowKeys().size(); } /** * Returns the total number of different columns in the datasets. * * @return The column count. */ public int getColumnCount() { return getColumnKeys().size(); } /** * Returns the column keys. * * @return The keys. */ public List getColumnKeys() { Set colKeys = new HashSet(); for (Iterator iter = datasets.keySet().iterator(); iter.hasNext();) { CategoryDataset dataset = (CategoryDataset) iter.next(); colKeys.addAll(dataset.getColumnKeys()); } return new ArrayList(colKeys); } /** * Returns the row keys. * * @return The keys. */ public List getRowKeys() { return new ArrayList(keyToRowKey.keySet()); } /** * Returns the row index for a given key. * * @param key the row keys. * * @return The row index. */ public int getRowIndex(Comparable key) { return getRowKeys().indexOf(key); } /** * Returns the column index for a given key. * * @param key the column key. * * @return The column index. */ public int getColumnIndex(Comparable key) { return getColumnKeys().indexOf(key); } /** * Returns a row key. * * @param row the row index (zero-based). * * @return The row key. */ public Comparable getRowKey(int row) { return (Comparable) getRowKeys().get(row); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. */ public Comparable getColumnKey(int column) { return (Comparable) getColumnKeys().get(column); } /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly <code>null</code>). */ public Number getValue(int row, int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); Comparable orgRowKey = (Comparable) keyToRowKey.get(rowKey); CategoryDataset dataset = (CategoryDataset) keyToDataset.get(rowKey); if (dataset.getColumnKeys().contains(columnKey) && dataset.getRowKeys().contains(orgRowKey)) { return dataset.getValue(orgRowKey, columnKey); } else { return null; } } /** * Returns the value for a pair of keys. * * @param rowKey the row key (<code>null</code> not permitted). * @param columnKey the column key (<code>null</code> not permitted). * * @return The value (possibly <code>null</code>). */ public Number getValue(Comparable rowKey, Comparable columnKey) { CategoryDataset dataset = (CategoryDataset) keyToDataset.get(rowKey); Comparable orgRowKey = (Comparable) keyToRowKey.get(rowKey); return dataset.getValue(orgRowKey, columnKey); } // -------- DatasetChangeListener -------- /** * A dataset has changed. Assign all new rowkeys as groups to the renderer * and delete no longer existing rowkeys from the maps. * * @param event The event. */ public void datasetChanged(DatasetChangeEvent event) { CategoryDataset dataset = (CategoryDataset) event.getDataset(); if (dataset == null) { return; } boolean changed = false; List newKeys = new ArrayList(); for (Iterator iter = dataset.getRowKeys().iterator(); iter.hasNext();) { Comparable key = (Comparable) iter.next(); String name = (String)datasets.get(dataset); String groupKey = key + "_" + name; newKeys.add(groupKey); if (!keyToRowKey.containsKey(groupKey)) { changed = true; keyToRowKey.put(groupKey, key); keyToDataset.put(groupKey, dataset); if (keytogroupmap == null) { keytogroupmap = new KeyToGroupMap(name); } keytogroupmap.mapKeyToGroup(groupKey, name); } } // remove all the missing rowKeys from the maps, // since they are no longer contained by the dataset Collection collection = new ArrayList(keyToDataset.entrySet()); for (Iterator iter = collection.iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); // key is not in newKey for the same dataset if (!newKeys.contains(entry.getKey()) && dataset.equals(entry.getValue())) { keyToDataset.remove(entry.getKey()); keyToRowKey.remove(entry.getKey()); // has the same keyset } } if (changed) { renderer.setSeriesToGroupMap(keytogroupmap); } else { // notify plot fireDatasetChanged(); } } // -------- helper methods to provide access to the original datasets /** * Returns the List for a given group rowkey and column key. The proper * dataset and original rowkey is retrieved from the maps. * * @param rowKey The group rowkey. * @param columnKey The columnkey. * @return The list. */ public List getList(Comparable rowKey, Comparable columnKey) { CategoryDataset dataset = (CategoryDataset) keyToDataset.get(rowKey); Comparable orgRowKey = (Comparable) keyToRowKey.get(rowKey); if (dataset instanceof CategoryPieDataset) { CategoryPieDataset cpdataset = (CategoryPieDataset) dataset; return cpdataset.getList(orgRowKey, columnKey); } return null; } /** * Returns the original rowkey from a group rowkey * * @param rowKey * @return */ public Comparable getOriginalRowKey(Comparable rowKey) { return (Comparable) keyToRowKey.get(rowKey); } }