/* * Licensed Materials - Property of Rogue Wave Software, Inc. * © Copyright Rogue Wave Software, Inc. 2014, 2017 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ package treemap; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.tree.TreePath; import ilog.views.chart.IlvChart; import ilog.views.chart.IlvChartLayout; import ilog.views.chart.data.IlvTreeTableDataSource; import ilog.views.chart.datax.IlvColumnUtilities; import ilog.views.chart.datax.IlvDataColumnInfo; import ilog.views.chart.datax.IlvDefaultDataColumnInfo; import ilog.views.chart.datax.adapter.IlvClusterNode; import ilog.views.chart.datax.adapter.IlvFlatTableToTableModel; import ilog.views.chart.datax.adapter.partition.IlvPartitionerFactory; import ilog.views.chart.datax.adapter.partition.IlvStringPartitionerFactory; import ilog.views.chart.datax.adapter.partition.IlvUniformScalePartitionerFactory; import ilog.views.chart.datax.flat.table.IlvDefaultFlatTableModel; import ilog.views.chart.datax.flat.table.IlvFlatTableModel; import ilog.views.chart.event.TreemapFocusEvent; import ilog.views.chart.event.TreemapFocusListener; import ilog.views.chart.renderer.IlvColorScheme; import ilog.views.chart.renderer.IlvTreemapChartRenderer; import ilog.views.util.data.IlvCSVReader; import ilog.views.util.swing.IlvJSplitPane; import ilog.views.util.swing.layout.IlvBetterFlowLayout; import ilog.views.util.swing.table.IlvJTableHeaderWithToolTips; import ilog.views.util.swing.table.IlvJTableWithToolTips; import shared.AbstractChartExample; /** * Demonstrates the treemap's main features. */ public class TreemapDemo extends AbstractChartExample { // The data source containing the data to be displayed. IlvTreeTableDataSource dataSource; // The table displaying the raw data. JTable rawTable; JScrollPane rawTableView; // The columns display used for partitioning. JTableHeader columnsTable; int separatorIndex; // The chooser for the area and color columns. JComboBox<String> areaColumnChooser; JComboBox<String> colorColumnChooser; // The chooser for the labeling mode. JComboBox<String> labelingChooser; // The combined treemap and tree-table. TreemapWithTreeTable trees; // Intermediate containers. JSplitPane treesPanel; static final double treesPanelDefaultDivision = 0.35; JSplitPane wholePanel; static final double wholePanelDefaultDivision = 0.6; // Flags for inhibiting selection propagation. boolean inhibitSelectionPropToRawTable; boolean inhibitSelectionPropToTreeTable; /** * Initializes the example user interface in the specified container. * * @param container * The <code>contentPane</code> of the <code>JFrame</code>. */ Override public void init(Container container) { super.init(container); initDataSource(); initRawTable(); initTreemapWithTreeTable(); initPartitioningControl(); initAreaColumnControl(); initColorColumnControl(); initLabelingControl(); // Initialize the status of toolbar buttons. trees.treemapPanel.updateEnabledStatuses(); initTableSelectionSynchronization(); // Create some auxiliary labels. JLabel columnsTableDescription = new JLabel( "Partition by: (Drag one or two items to the left, before the black separator.)"); JLabel areaColumnChooserDescription = new JLabel("Show as important:"); JLabel colorColumnChooserDescription = new JLabel("Show through color:"); JLabel labelingChooserDescription = new JLabel("Labels:"); // Put the pieces together. Box columnsTableBox = new Box(BoxLayout.PAGE_AXIS); { Box row = new Box(BoxLayout.LINE_AXIS); row.add(columnsTableDescription); row.add(Box.createHorizontalGlue()); columnsTableBox.add(row); } columnsTableBox.add(columnsTable); Box areaColumnChooserBox = new Box(BoxLayout.PAGE_AXIS); { Box row = new Box(BoxLayout.LINE_AXIS); row.add(areaColumnChooserDescription); row.add(Box.createHorizontalGlue()); areaColumnChooserBox.add(row); } areaColumnChooserBox.add(areaColumnChooser); Box colorColumnChooserBox = new Box(BoxLayout.PAGE_AXIS); { Box row = new Box(BoxLayout.LINE_AXIS); row.add(colorColumnChooserDescription); row.add(Box.createHorizontalGlue()); colorColumnChooserBox.add(row); } colorColumnChooserBox.add(colorColumnChooser); Box labelingChooserBox = new Box(BoxLayout.PAGE_AXIS); { Box row = new Box(BoxLayout.LINE_AXIS); row.add(labelingChooserDescription); row.add(Box.createHorizontalGlue()); labelingChooserBox.add(row); } labelingChooserBox.add(labelingChooser); Box chooserBoxes = new Box(BoxLayout.LINE_AXIS); chooserBoxes.add(areaColumnChooserBox); chooserBoxes.add(Box.createHorizontalStrut(5)); chooserBoxes.add(colorColumnChooserBox); chooserBoxes.add(Box.createHorizontalStrut(5)); chooserBoxes.add(labelingChooserBox); JPanel controlPanel = new JPanel(); controlPanel.setLayout(new IlvBetterFlowLayout(FlowLayout.LEADING)); controlPanel.add(columnsTableBox); controlPanel.add(chooserBoxes); treesPanel = new IlvJSplitPane(JSplitPane.HORIZONTAL_SPLIT, trees.treetableView, trees.treemapPanel); treesPanel.setResizeWeight(treesPanelDefaultDivision); JPanel upperHalf = new JPanel(); upperHalf.setLayout(new BorderLayout()); upperHalf.add(treesPanel, BorderLayout.CENTER); upperHalf.add(controlPanel, BorderLayout.NORTH); wholePanel = new IlvJSplitPane(JSplitPane.VERTICAL_SPLIT, upperHalf, rawTableView); wholePanel.setResizeWeight(wholePanelDefaultDivision); container.setLayout(new BorderLayout()); container.add(wholePanel, BorderLayout.CENTER); treesPanel.setDividerLocation(treesPanelDefaultDivision); wholePanel.setDividerLocation(wholePanelDefaultDivision); } // The file containing the data. // private final String dataFileXML = "data/energy.xml"; private final String dataFileCSV = "data/energy.csv"; // Information about the columns in the Excel data. private final String[] columnNames = { "Country", "Region", "Population", "GDP", "GDP per Capita", "Ranking", "Year", "Energy Type", "Production", "Consumption" }; private final Class<?>[] columnTypes = { String.class, String.class, Double.class, Double.class, Double.class, Integer.class, String.class, String.class, Double.class, Double.class }; /** * Loads the data into memory and makes it accessible through a data source. */ private void initDataSource() { // Load the data. IlvFlatTableModel tableData; // if (false) { // // Load from the XML file. // // This takes more memory, but is useful if the original file is an // // Excel file. // Workbook data; // try { // data = new Workbook(getResourceURL(dataFileXML)); // } catch (IOException e) { // e.printStackTrace(); // return; // } // // // Extract the first worksheet. // tableData = data.getWorksheet(0, columnNames, columnTypes, 1); // } else { // Load from the CSV file. // This takes less memory. TableModel table; try { Reader reader = new InputStreamReader(new BufferedInputStream(openStream(getResourceURL(dataFileCSV))), "UTF-8"); try { IlvCSVReader parser = IlvCSVReader.getInstance('\t', 0); table = parser.read(reader); } finally { reader.close(); } } catch (IOException e) { e.printStackTrace(); return; } // Copy the data into an IlvFlatTableModel, with appropriate // conversion of values. int numColumns = table.getColumnCount(); if (numColumns != columnNames.length) throw new RuntimeException("The columnNames array is not up-to-date."); IlvDataColumnInfo[] columns = new IlvDataColumnInfo[numColumns]; for (int col = 0; col < numColumns; col++) { columns[col] = new IlvDefaultDataColumnInfo(columnNames[col], columnTypes[col]); } int numRows = table.getRowCount(); tableData = new IlvDefaultFlatTableModel(numRows, columns); for (int col = 0; col < numColumns; col++) { if (columnTypes[col] == Double.class) { for (int row = 0; row < numRows; row++) { double value = Double.parseDouble(table.getValueAt(row, col).toString()); tableData.setDoubleAt(value, row, col); } } else if (columnTypes[col] == Integer.class) { for (int row = 0; row < numRows; row++) { int value = (int) Double.parseDouble(table.getValueAt(row, col).toString()); tableData.setValueAt(new Integer(value), row, col); } } else if (columnTypes[col] == String.class) { for (int row = 0; row < numRows; row++) { String value = table.getValueAt(row, col).toString(); tableData.setValueAt(value, row, col); } } else { for (int row = 0; row < numRows; row++) { Object value = table.getValueAt(row, col); tableData.setValueAt(value, row, col); } } } // } // Set the types of some columns to enumerated. This is a hint to the // QUALITATIVE color scheme how to distribute the colors. ((IlvDefaultDataColumnInfo) IlvColumnUtilities.getColumnByName(tableData, "Country")).setEnumerated(true); ((IlvDefaultDataColumnInfo) IlvColumnUtilities.getColumnByName(tableData, "Region")).setEnumerated(true); // Create a data source taking the worksheet as input. dataSource = new IlvTreeTableDataSource(tableData); } /** * Initializes the raw table view of the data. */ private void initRawTable() { // Create the table view. IlvFlatTableModel tableData = dataSource.getFlatTableModel(); rawTable = new IlvJTableWithToolTips(new IlvFlatTableToTableModel(tableData)); // Specify the names and proportions of the columns. TableColumnModel rawTableColumns = new DefaultTableColumnModel(); for (int col = 0; col < columnNames.length; col++) { TableColumn column = new TableColumn(col, 80); column.setHeaderValue(columnNames[col]); rawTableColumns.addColumn(column); } rawTable.setColumnModel(rawTableColumns); // Enable tooltips on the header. rawTable.setTableHeader(new IlvJTableHeaderWithToolTips(rawTable.getColumnModel())); // Add scroll bars around it. rawTableView = new JScrollPane(rawTable); } /** * Initializes the treemap and tree-table views of the data. */ private void initTreemapWithTreeTable() { trees = new TreemapWithTreeTable(dataSource, "Country", new String[0], getResourceURL("resources/treemap.css")); } /** * Creates the columns display used for partitioning. */ private void initPartitioningControl() { columnsTable = new ReorderableTableHeader() { Override public void columnDragged(int fromIndex, int toIndex) { // Enforce a rule that at most 3 columns are used // for partitioning. More precisely, at most 2 // enumerated columns and at most 1 numeric column. // Otherwise the partitioned tree model takes up // too much memory, leading to OutOfMemoryError. // The limits 2 and 1 are arbitrary. final int enumColumnsMax = 2; final int numericColumnsMax = 1; // We don't want to interfere with the column the user is // moving. Therefore we count how many columns of each type // to keep before the separator, excluding toIndex. int enumColumnsToKeep = enumColumnsMax; int numericColumnsToKeep = numericColumnsMax; IlvFlatTableModel model = dataSource.getFlatTableModel(); int col; for (col = 0; col < getColumnModel().getColumnCount(); col++) { // Consider only the columns left of the separator. if ("".equals(getColumnModel().getColumn(col).getIdentifier())) break; // Look which column the user is moving. if (col == toIndex) { String columnName = (String) getColumnModel().getColumn(col).getIdentifier(); IlvDataColumnInfo column = IlvColumnUtilities.getColumnByName(model, columnName); if (Number.class.isAssignableFrom(column.getType())) numericColumnsToKeep -= 1; else enumColumnsToKeep -= 1; } } int separator = col; int moveTarget = separator; for (col = 0; col < separator; col++) { // Don't interfere with the column the user is moving. if (col != toIndex) { String columnName = (String) getColumnModel().getColumn(col).getIdentifier(); IlvDataColumnInfo column = IlvColumnUtilities.getColumnByName(model, columnName); if (Number.class.isAssignableFrom(column.getType())) { if (numericColumnsToKeep > 0) { numericColumnsToKeep--; continue; } } else { if (enumColumnsToKeep > 0) { enumColumnsToKeep--; continue; } } // Move col behind the separator. getColumnModel().moveColumn(col, moveTarget); if (col < toIndex) toIndex--; col--; separator--; } } // Now update the partitioning accordingly. updatePartitioners(); } }; final TableColumnModel columnsTableColumns = new DefaultTableColumnModel(); { TableColumn separatorColumn = new TableColumn(-1, 10); separatorColumn.setHeaderValue(""); DefaultTableCellRenderer separatorRenderer = new DefaultTableCellRenderer() { Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { return this; } }; separatorRenderer.setBorder(BorderFactory.createLineBorder(Color.black, 3)); separatorColumn.setHeaderRenderer(separatorRenderer); columnsTableColumns.addColumn(separatorColumn); } DefaultTableCellRenderer columnHeaderRenderer = // Similar to what JTableHeader.UIResourceTableCellRenderer does. new DefaultTableCellRenderer() { private Font plainFont = getFont().deriveFont(Font.PLAIN); private Font boldFont = getFont().deriveFont(Font.BOLD); Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setText(value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); // Emphasize the columns before the black separator block. int separator; for (separator = 0; separator < columnsTableColumns.getColumnCount(); separator++) if ("".equals(columnsTableColumns.getColumn(separator).getIdentifier())) break; setFont(column < separator ? boldFont : plainFont); return this; } }; for (int col = 0; col < columnNames.length; col++) { String columnName = columnNames[col]; int width = Math.max((new JLabel(columnName)).getPreferredSize().width + 5, 75); TableColumn column = new TableColumn(col, width); column.setHeaderValue(columnName); column.setHeaderRenderer(columnHeaderRenderer); columnsTableColumns.addColumn(column); } columnsTable.setColumnModel(columnsTableColumns); // Create an invisible JTable with 0 rows so that columnsTable becomes // functional in JDK >= 1.6. new JTable(new DefaultTableModel(), columnsTableColumns).setTableHeader(columnsTable); // Initially select the "Year" and "Country" columns. { int chosenColumnIndex = -1; for (int i = 0; i < columnsTableColumns.getColumnCount(); i++) if ("Year".equals(columnsTableColumns.getColumn(i).getIdentifier())) chosenColumnIndex = i; assert (chosenColumnIndex >= 0); if (chosenColumnIndex > 0) columnsTableColumns.moveColumn(chosenColumnIndex, 0); } { int chosenColumnIndex = -1; for (int i = 1; i < columnsTableColumns.getColumnCount(); i++) if ("Country".equals(columnsTableColumns.getColumn(i).getIdentifier())) chosenColumnIndex = i; assert (chosenColumnIndex >= 1); if (chosenColumnIndex > 1) columnsTableColumns.moveColumn(chosenColumnIndex, 1); } updatePartitioners(); } /** * Creates the chooser for the area column. */ private void initAreaColumnControl() { ArrayList<String> areaColumnChoices = new ArrayList<String>(); areaColumnChoices.add("(all equal area)"); for (int col = 0; col < columnNames.length; col++) if (columnTypes[col] == Double.class) areaColumnChoices.add(columnNames[col]); areaColumnChooser = new JComboBox<String>(areaColumnChoices.toArray(new String[areaColumnChoices.size()])); areaColumnChooser.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { updateAreaColumn(); } }); areaColumnChooser.setSelectedItem("Consumption"); } /** * Creates the chooser for the color column. */ private void initColorColumnControl() { ArrayList<String> colorColumnChoices = new ArrayList<String>(); colorColumnChoices.add("(all same color)"); for (int col = 0; col < columnNames.length; col++) if (!columnNames[col].equals("Name")) colorColumnChoices.add(columnNames[col]); colorColumnChooser = new JComboBox<String>(colorColumnChoices.toArray(new String[colorColumnChoices.size()])); colorColumnChooser.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { updateColorColumn(); } }); colorColumnChooser.setSelectedItem("Energy Type"); } /** * Creates the chooser for the labeling mode. */ private void initLabelingControl() { String[] labelingChoices = { "off", "smart", "1 level", "2 levels", "all" }; labelingChooser = new JComboBox<String>(labelingChoices); labelingChooser.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent event) { updateLabeling(); } }); labelingChooser.setSelectedItem("smart"); // When the labeling is set to "on", it actually depends on the // focus level. IlvChart chart = trees.treemapPanel.getChart(); chart.addTreemapFocusListener(new TreemapFocusListener() { Override public void focusChanged(TreemapFocusEvent event) { updateLabeling(); } }); } /** * Initializes a synchronization between the selection of the table and the * selection of the tree-table (and thus also with the selection of the * treemap - it is synchronized through * <code>TreemapWithTreeTable.initSelectionSynchronization()</code>. */ private void initTableSelectionSynchronization() { final ListSelectionModel tableSelectionModel = rawTable.getSelectionModel(); final ListSelectionModel treetableSelectionModel = trees.treetable.getSelectionModel(); // Ensure that the treetable's selection display is in sync with the // raw table's selection, as far as single-selection is concerned. tableSelectionModel.addListSelectionListener(new ListSelectionListener() { Override public void valueChanged(ListSelectionEvent event) { if (inhibitSelectionPropToTreeTable) return; if (tableSelectionModel.isSelectionEmpty()) treetableSelectionModel.clearSelection(); else { TreePath commonPath = null; int min = tableSelectionModel.getMinSelectionIndex(); int max = tableSelectionModel.getMaxSelectionIndex(); for (int i = min; i <= max; i++) if (tableSelectionModel.isSelectedIndex(i)) { // Get the object corresponding to this row by // accessing the flat-model view. Object object = dataSource.getFlatModel().getObjects().get(i); // Get the tree path from the root to that object. TreePath path = dataSource.getTreeModel().getPath(object); assert (path != null); if (commonPath == null) commonPath = path; else commonPath = computeCommonAncestor(commonPath, path); } assert (commonPath != null); TreePath newSelection = commonPath; try { inhibitSelectionPropToRawTable = true; trees.expandParents(newSelection); int row = trees.treetable.getTree().getRowForPath(newSelection); trees.ensureVisible(row); treetableSelectionModel.setSelectionInterval(row, row); } finally { inhibitSelectionPropToRawTable = false; } } } }); // Ensure that the raw table's selection display is in sync with the // treetable's selection, projecting from cluster nodes onto the tree // leaves. treetableSelectionModel.addListSelectionListener(new ListSelectionListener() { Override public void valueChanged(ListSelectionEvent event) { if (inhibitSelectionPropToRawTable) return; if (treetableSelectionModel.isSelectionEmpty()) tableSelectionModel.clearSelection(); else { int selectedRow = treetableSelectionModel.getMinSelectionIndex(); TreePath selectedObjectPath = trees.treetable.getTree().getPathForRow(selectedRow); assert (selectedObjectPath != null); try { inhibitSelectionPropToTreeTable = true; tableSelectionModel.clearSelection(); // Recurse down to the tree leaves below selectedObjectPath. selectLeavesInTable(selectedObjectPath); } finally { inhibitSelectionPropToTreeTable = false; } // Now ensure at least the beginning of the selection is visible. int viewHeight = rawTableView.getViewport().getHeight(); // = rawTableView.getViewport().getExtentSize().height; int minIndex = tableSelectionModel.getMinSelectionIndex(); int maxIndex = tableSelectionModel.getMaxSelectionIndex(); Rectangle topRect = rawTable.getCellRect(minIndex, 0, true); Rectangle bottomRect = rawTable.getCellRect(maxIndex, 0, true); int combinedHeight = bottomRect.y + bottomRect.height - topRect.y; topRect.height = Math.min(combinedHeight, viewHeight); rawTable.scrollRectToVisible(topRect); } } private void selectLeavesInTable(TreePath path) { Object object = path.getLastPathComponent(); if (object instanceof IlvClusterNode) { // A tree node with children. for (Iterator<?> it = dataSource.getTreeModel().getChildren(object).iterator(); it.hasNext();) { Object child = it.next(); selectLeavesInTable(path.pathByAddingChild(child)); } } else { // A leaf. assert (dataSource.getTreeModel().getChildren(object).isEmpty()); int i = dataSource.getFlatModel().getObjects().indexOf(object); tableSelectionModel.addSelectionInterval(i, i); } } }); } /** * Updates the data source and with it also the treemap and tree-table when a * particular partitioning has been chosen. */ private void updatePartitioners() { TableColumnModel columnsTableColumns = columnsTable.getColumnModel(); IlvFlatTableModel model = dataSource.getFlatTableModel(); // Take only the columns before the black separator block. int separator; for (separator = 0; separator < columnsTableColumns.getColumnCount(); separator++) if ("".equals(columnsTableColumns.getColumn(separator).getIdentifier())) break; separatorIndex = separator; int count = separator; IlvPartitionerFactory<?>[] newPartitionerFactories = new IlvPartitionerFactory[count]; for (int col = 0; col < count; col++) { String columnName = (String) columnsTableColumns.getColumn(col).getIdentifier(); IlvDataColumnInfo column = IlvColumnUtilities.getColumnByName(model, columnName); // Set the corresponding partitioner. newPartitionerFactories[col] = (Number.class.isAssignableFrom(column.getType()) ? new IlvUniformScalePartitionerFactory(column, 4, 5) : new IlvStringPartitionerFactory(column)); } // No need to disturb the data source if nothing has changed. if (!Arrays.equals(dataSource.getPartitionerFactories(), newPartitionerFactories)) { dataSource.setPartitionerFactories(newPartitionerFactories); StringBuffer headerLabel = new StringBuffer(); if (count == 0) { headerLabel.append("No partitioning"); } else { headerLabel.append("By "); for (int col = 0; col < count; col++) { if (col > 0) headerLabel.append("/"); String columnName = (String) columnsTableColumns.getColumn(col).getIdentifier(); headerLabel.append(columnName); } } trees.treeColumn.setHeaderValue(headerLabel.toString()); trees.treetable.getTableHeader().repaint(); // Clear the history, since it likely contains focus choices that are // assume the previous partitioning, not this one. trees.treemapPanel.clearHistory(); } } /** * Updates the treemap when a particular area column has been chosen. */ private void updateAreaColumn() { String columnName = (String) areaColumnChooser.getSelectedItem(); IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) trees.treemapPanel.getChart().getRenderer(0); if (columnName != null && IlvColumnUtilities.getColumnByName(dataSource.getTreeModel(), columnName) != null) renderer.setAreaColumnName(columnName); else renderer.setAreaColumnName(null); } /** * Updates the treemap when a particular color column has been chosen. */ private void updateColorColumn() { String columnName = (String) colorColumnChooser.getSelectedItem(); IlvChart chart = trees.treemapPanel.getChart(); IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0); // Reset the LegendLabelFormat, since the previous LegendLabelFormat // may be a DecimalFormat and therefore not suitable for non-numeric // columns. renderer.setLegendLabelFormat(null); // Set the color column and color scheme. // Also make a legend appear when suitable. IlvDataColumnInfo column; if (columnName != null && (column = IlvColumnUtilities.getColumnByName(dataSource.getTreeModel(), columnName)) != null) { renderer.setColorColumnName(columnName); if (column.getType() == String.class) // For non-numeric columns, use "qualitative" colors. renderer.setColorScheme(IlvTreemapChartRenderer.COLORSCHEME_QUALITATIVE); else if (column.getName().equals("Profits")) // For numeric columns with positive and negative values, use // red for negative and green for positive values. Take care // that frontier between red and green is exactly at 0. renderer.setColorScheme( IlvColorScheme.createAdjustedColorScheme(IlvTreemapChartRenderer.COLORSCHEME_DIVERGING_RED_GREEN, 0.0)); else // For other numeric columns, use red for low and green for // high values. renderer.setColorScheme( IlvColorScheme.createAdjustedColorScheme(IlvTreemapChartRenderer.COLORSCHEME_DIVERGING_RED_GREEN)); if (Number.class.isAssignableFrom(column.getType())) { // Ensure there is a legend. renderer.setLegendLabelFormat(new DecimalFormat("#.##")); chart.setLegendPosition(IlvChartLayout.EAST); chart.setLegendVisible(true); } else { // Ensure there is no legend. chart.setLegendVisible(false); } } else { renderer.setColorColumnName(null); renderer.setColorScheme(IlvTreemapChartRenderer.COLORSCHEME_DEPTH); // Ensure there is no legend. chart.setLegendVisible(false); } } /** * Updates the treemap when a particular labeling mode has been chosen. */ private void updateLabeling() { String labeling = (String) labelingChooser.getSelectedItem(); IlvChart chart = trees.treemapPanel.getChart(); IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0); if (labeling == "off") renderer.setAnnotationVisibility(IlvTreemapChartRenderer.VISIBILITY_NONE); else if (labeling == "smart") { renderer.setAnnotationVisibility(IlvTreemapChartRenderer.VISIBILITY_SMART); renderer.setClippedAnnotationVisibility(false); } else if (labeling == "1 level") { renderer.setAnnotationVisibility(renderer.getFocusLevel() + 1); renderer.setClippedAnnotationVisibility(true); } else if (labeling == "2 levels") { renderer.setAnnotationVisibility(renderer.getFocusLevel() + 2); renderer.setClippedAnnotationVisibility(true); } else if (labeling == "all") { renderer.setAnnotationVisibility(Integer.MAX_VALUE); renderer.setClippedAnnotationVisibility(true); } } /** * Computes and returns the longest common initial segment of two paths * starting at the same root. */ private static TreePath computeCommonAncestor(TreePath path1, TreePath path2) { int len1 = path1.getPathCount(); int len2 = path2.getPathCount(); if (len1 > len2) do path1 = path1.getParentPath(); while (--len1 > len2); else if (len2 > len1) do path2 = path2.getParentPath(); while (--len2 > len1); assert (path1.getPathCount() == path2.getPathCount()); while (path1.getLastPathComponent() != path2.getLastPathComponent()) { path1 = path1.getParentPath(); path2 = path2.getParentPath(); } assert (path1 != null && path1.equals(path2)); return path1; } /** * Opens an URL for read-only access. Like url.openStream(), except that it * also works for Windows UNC pathnames. See Sun BR #4671171. */ private static InputStream openStream(URL url) throws IOException { if ("file".equals(url.getProtocol()) && !"".equals(url.getHost())) // Do it directly, avoiding a "Connection refused: connect" error. return new BufferedInputStream(new FileInputStream("//" + url.getHost() + url.getPath())); else { // But note that URLs of the form "jar:file://HOST/...!..." work // and don't need special treatment. return url.openStream(); } } /** * Starts the demo, when run as an application. */ public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { Override public void run() { JFrame frame = new JFrame("Treemap sample"); TreemapDemo demo = new TreemapDemo(); demo.init(frame.getContentPane()); demo.setFrameGeometry(1000, 900, true); frame.setVisible(true); demo.treesPanel.setDividerLocation(treesPanelDefaultDivision); demo.wholePanel.setDividerLocation(wholePanelDefaultDivision); } }); } }