/*
* Licensed Materials - Property of Perforce Software, Inc.
* © Copyright Perforce Software, Inc. 2014, 2021
* © 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.Font;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.DataModel;
import javax.faces.model.SelectItem;
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.partition.IlvClusterId;
import ilog.views.chart.datax.adapter.partition.IlvColumnValueClusterId;
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.datax.tree.list.IlvTreeListModel;
import ilog.views.chart.graphic.IlvDataLabelAnnotation;
import ilog.views.chart.renderer.IlvColorScheme;
import ilog.views.chart.renderer.IlvTreemapChartRenderer;
import ilog.views.util.IlvResourceUtil;
import ilog.views.util.data.IlvCSVReader;
import treemap.table.FilteredFlatTableModel;
/**
* The application bean
*/
public class TreemapBean implements Serializable {
// The datasource
private IlvTreeTableDataSource dataSource;
// The file containing the data.
private final String dataFile = "/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" };
// Information about the columns type in the Excel data.
private final Class<?>[] columnTypes = { String.class, String.class, Double.class, Double.class, Double.class,
Integer.class, String.class, String.class, Double.class, Double.class };
// The current color model
private String colorColumn = columnNames[5];
// The current area
private String areaColumn = columnNames[4];
// The color chooser model
private List<SelectItem> colorColumnModel = null;
// The area chooser model
private List<SelectItem> areaColumnModel = null;
// The current partition
private String partitionLabel;
// The partition names.
private String partitionNames[] = { "Country", "Region", "Population", "GDP", "GDP per Capita", "Ranking", "Year",
"Energy Type", "Production", "Consumption" };
// The current partition separator
private String partitionValue = partitionNames[1];
private final static String NONE = "<none>";
// The first partitionning criteria
private String partitionCriterion1 = partitionNames[0];
// The second partitionning criteria
private String partitionCriterion2 = NONE;
// The treemap chart
private TreemapWithHistory treemap;
// The treepath finder
private TreemapPathFinder treepathFinder = new TreemapPathFinder();
// The table model
private FilteredFlatTableModel tableModel;
// The current selection path
private String selectionPath;
/**
* Creates a new <code>TreemapBean</code> instance.
*/
public TreemapBean() {
initDataSource();
initTableModel();
initTreemap("Country");
}
/**
* Loads the data into memory and makes it accessible through a data source.
*/
private void initDataSource() {
TableModel table;
try {
InputStream input = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(dataFile);
Reader reader = new InputStreamReader(new BufferedInputStream(input), "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) {
String msg = IlvResourceUtil.getCurrentLocaleString(TreemapBean.class, "columnNamesNotUpToDate");
throw new RuntimeException(msg);
}
IlvDataColumnInfo[] columns = new IlvDataColumnInfo[numColumns];
for (int col = 0; col < numColumns; col++) {
columns[col] = new IlvDefaultDataColumnInfo(columnNames[col], columnTypes[col]);
}
int numRows = table.getRowCount();
IlvDefaultFlatTableModel 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(Integer.valueOf(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 table model
*/
private void initTableModel() {
IlvFlatTableModel model = dataSource.getFlatTableModel();
tableModel = new FilteredFlatTableModel(model);
}
/**
* Initializes the treemap display.
*/
private void initTreemap(String nameColumn) {
// Start customizing it.
treemap = new TreemapWithHistory(dataSource);
treemap.addSelectionListener(new TreemapWithHistory.SelectionListener() {
Override
public void selectionChanged(TreePath treepath) {
Map<String, String> criteria = new HashMap<String, String>();
if (treepath != null) {
int nb = treepath.getPathCount() - 1; // -1 = root
if (nb == 0)
selectionPath = "";
Object object = treepath.getLastPathComponent();
if (object instanceof IlvClusterNode) {
StringBuffer tmpSelectionPath = new StringBuffer();
Object[] path = treepath.getPath();
for (int i = 1; i <= nb; i++) {
IlvClusterNode node = (IlvClusterNode) path[i];
IlvColumnValueClusterId clusterId = (IlvColumnValueClusterId) node.getId();
String columnName = clusterId.getColumn().getName();
criteria.put(columnName, path[i].toString());
tmpSelectionPath.append(path[i].toString());
if (i != nb)
tmpSelectionPath.append("/");
}
selectionPath = tmpSelectionPath.toString();
tableModel.setCriteria(criteria);
} else {
// A leaf = a single record.
int row = dataSource.getFlatModel().getObjects().indexOf(object);
Object companyName = tableModel.getValueAt(row, "Country");
selectionPath = companyName.toString();
tableModel.setRowVisible(row);
}
} else {
tableModel.setCriteria(new HashMap<String, String>());
selectionPath = null;
}
}
});
IlvChart chart = treemap.getChart();
IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0);
// Set the packing algorithm.
renderer.setPacking(IlvTreemapChartRenderer.SQUARIFIED_CORNER_PACKING);
// Add labels.
// The label visibility is set in class TreemapDemo,
// method updateLabeling().
renderer.setLabelColumnName(nameColumn);
renderer.setAnnotation(new IlvDataLabelAnnotation());
// Add tooltips.
renderer.setLabelColumnName(nameColumn);
// init colors
updateColorColumn();
updateAreaColumn();
updateLabeling();
updatePartitioners();
}
/**
* Returns <code>true</code> if the path specified is valid against the
* specified tree list model, <code>false</code> otherwise.
*
* @param path
* The tree path to test.
* @param tmodel
* The tree list model.
* @return <code>true</code> if the path is valid, <code>false</code>
* otherwise.
*/
public boolean isValidPath(TreePath path, IlvTreeListModel tmodel) {
TreePath tp = path;
Object[] chain = tp.getPath();
if (chain[0] != tmodel.getRoot())
return false;
for (int i = 1; i < chain.length; i++)
if (!tmodel.getChildren(chain[i - 1]).contains(chain[i]))
return false;
return true;
}
/**
* Updates the treemap when a particular color column has been chosen.
*/
private void updateColorColumn() {
String columnName = colorColumn;
IlvChart chart = treemap.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(true);
}
} else {
renderer.setColorColumnName(null);
renderer.setColorScheme(IlvTreemapChartRenderer.COLORSCHEME_DEPTH);
// Ensure there is no legend.
chart.setLegendVisible(true);
}
}
/**
* Updates the treemap when a particular area column has been chosen.
*/
private void updateAreaColumn() {
String columnName = areaColumn;
IlvChart chart = treemap.getChart();
IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0);
if (columnName != null && IlvColumnUtilities.getColumnByName(dataSource.getTreeModel(), columnName) != null)
renderer.setAreaColumnName(columnName);
else
renderer.setAreaColumnName(null);
}
/**
* Updates the treemap when a particular labeling mode has been chosen.
*/
private void updateLabeling() {
IlvChart chart = treemap.getChart();
IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0);
renderer.setAnnotationVisibility(IlvTreemapChartRenderer.VISIBILITY_SMART);
renderer.setClippedAnnotationVisibility(false);
IlvDataLabelAnnotation annotation = new IlvDataLabelAnnotation();
annotation.getLabelRenderer().setFont(new Font("SansSerif", Font.PLAIN, 12));
renderer.setAnnotation(annotation);
}
/**
* Updates the data source and with it also the treemap and tree-table when a
* particular partitioning has been chosen.
*/
private void updatePartitioners() {
IlvFlatTableModel model = dataSource.getFlatTableModel();
boolean criteria1Set = !NONE.equals(partitionCriterion1);
boolean criteria2Set = !NONE.equals(partitionCriterion2);
int count = criteria1Set ? 1 : 0;
count += criteria2Set ? 1 : 0;
SuppressWarnings("unchecked")
IlvPartitionerFactory<? extends IlvClusterId>[] newPartitionerFactories = new IlvPartitionerFactory[count];
if (criteria1Set)
newPartitionerFactories[0] = createPartitioner(model, partitionCriterion1);
if (criteria2Set)
newPartitionerFactories[count - 1] = createPartitioner(model, partitionCriterion2);
if (!Arrays.equals(dataSource.getPartitionerFactories(), newPartitionerFactories)) {
dataSource.setPartitionerFactories(newPartitionerFactories);
tableModel.setCriteria(new HashMap<String, String>());
TreePath selection = treemap.getSelection();
if (selection != null && !isValidPath(selection, dataSource.getTreeModel()))
treemap.setSelection(null);
treemap.clearHistory();
StringBuffer headerLabel = new StringBuffer();
if (count == 0) {
String msg = IlvResourceUtil.getCurrentLocaleString(TreemapBean.class, "noPartitioning");
headerLabel.append(msg);
} else {
if (criteria1Set) {
headerLabel.append(partitionCriterion1);
if (criteria2Set) {
headerLabel.append("/");
headerLabel.append(partitionCriterion2);
}
} else if (criteria2Set)
headerLabel.append(partitionCriterion2);
}
partitionLabel = headerLabel.toString();
}
}
private IlvPartitionerFactory<? extends IlvClusterId> createPartitioner(IlvFlatTableModel model, String columnName) {
IlvDataColumnInfo column = IlvColumnUtilities.getColumnByName(model, columnName);
// Set the corresponding partitioner.
IlvPartitionerFactory<? extends IlvClusterId> partitioner = (Number.class.isAssignableFrom(column.getType())
? new IlvUniformScalePartitionerFactory(column, 4, 5)
: new IlvStringPartitionerFactory(column));
return partitioner;
}
/**
* JSF action to change the color model.
*
* @param event
* The value change event.
*/
public void colorColumnChanged(ValueChangeEvent event) {
colorColumn = event.getNewValue().toString();
updateColorColumn();
}
/**
* JSF action to change the area.
*
* @param event
* The value change event.
*/
public void areaColumnChanged(ValueChangeEvent event) {
areaColumn = event.getNewValue().toString();
updateAreaColumn();
}
/**
* JSF action to change the partitionners.
*
* @param event
* The value change event.
*/
public void partitionCriterion1Changed(ValueChangeEvent event) {
partitionCriterion1 = event.getNewValue().toString();
updatePartitioners();
}
/**
* JSF action to change the partitionners.
*
* @param event
* The value change event.
*/
public void partitionCriterion2Changed(ValueChangeEvent event) {
partitionCriterion2 = event.getNewValue().toString();
updatePartitioners();
}
/**
* JSF action to change the partitionners.
*
* @param event
* The value change event.
*/
public void partitionColumnChanged(ValueChangeEvent event) {
partitionValue = event.getNewValue().toString();
updatePartitioners();
}
/**
* Returns the color select one chooser model.
*
* @return The color chooser model.
*/
public List<SelectItem> getColorColumnModel() {
if (colorColumnModel == null) {
colorColumnModel = new ArrayList<SelectItem>();
String msg = IlvResourceUtil.getCurrentLocaleString(TreemapBean.class, "allSameColorLabel");
colorColumnModel.add(new SelectItem("(all same color)", msg));
for (int col = 0; col < columnNames.length; col++)
if (!columnNames[col].equals("Country"))
colorColumnModel.add(new SelectItem(columnNames[col]));
}
return colorColumnModel;
}
/**
* Returns the area select one chooser model.
*
* @return The area chooser model.
*/
public List<SelectItem> getAreaColumnModel() {
if (areaColumnModel == null) {
areaColumnModel = new ArrayList<SelectItem>();
String msg = IlvResourceUtil.getCurrentLocaleString(TreemapBean.class, "allEqualAreaLabel");
areaColumnModel.add(new SelectItem("(all equal area)", msg));
for (int col = 0; col < columnNames.length; col++)
if (columnTypes[col] == Double.class)
areaColumnModel.add(new SelectItem(columnNames[col]));
}
return areaColumnModel;
}
private List<SelectItem> partitionModel;
/**
* Returns the area select one chooser model.
*
* @return The area chooser model.
*/
public List<SelectItem> getPartitionColumnModel() {
if (partitionModel == null) {
partitionModel = new ArrayList<SelectItem>();
partitionModel.add(new SelectItem(NONE));
for (int col = 0; col < partitionNames.length; col++)
partitionModel.add(new SelectItem(partitionNames[col]));
}
return partitionModel;
}
/**
* Returns the current area.
*
* @return The current area.
*/
public String getAreaColumn() {
return areaColumn;
}
/**
* Sets the current area
*
* @param areaColumn
* The area to set.
*/
public void setAreaColumn(String areaColumn) {
this.areaColumn = areaColumn;
}
/**
* Returns the current color scheme.
*
* @return The color scheme.
*/
public String getColorColumn() {
return colorColumn;
}
/**
* Sets the current color scheme.
*
* @param colorColumn
* The color scheme to set.
*/
public void setColorColumn(String colorColumn) {
this.colorColumn = colorColumn;
}
/**
* Returns the current partition.
*
* @return The current partition.
*/
public String getPartitionLabel() {
return partitionLabel;
}
/**
* Sets the current partition value separator
*
* @return The partition separator.
*/
public String getPartitionValue() {
return partitionValue;
}
/**
* Sets the partition separator
*
* @param partitionValue
* The paration separator to set.
*/
public void setPartitionValue(String partitionValue) {
this.partitionValue = partitionValue;
}
/**
* Returns the first criterion used to partition the treemap.
*
* @return The first partition criterion.
*/
public String getPartitionCriterion1() {
return partitionCriterion1;
}
/**
* Sets the first criterion used to partition the treemap.
*
* @param partitionCriteria1
* The criterion to set.
*/
public void setPartitionCriterion1(String partitionCriteria1) {
this.partitionCriterion1 = partitionCriteria1;
}
/**
* Returns the second criterion used to partition the treemap.
*
* @return The second partition criterion.
*/
public String getPartitionCriterion2() {
return partitionCriterion2;
}
/**
* Sets the second criterion used to partition the treemap.
*
* @param partitionCriteria2
* The criterion to set.
*/
public void setPartitionCriterion2(String partitionCriteria2) {
this.partitionCriterion2 = partitionCriteria2;
}
/**
* Returns the treemap object (the graphic component + the selection and
* history of selection)
*
* @return The treemap.
*/
public TreemapWithHistory getTreemap() {
return treemap;
}
/**
* Returns the object finder that returns the tree path of a picked element of
* the treemap chart.
*
* @return The tree path finder.
*/
public TreemapPathFinder getTreepathFinder() {
return treepathFinder;
}
/**
* Returns the table model.
*
* @return The table model
*/
public DataModel getTableModel() {
return tableModel;
}
/**
* Returns <code>true</code> if the table should be rendered
*
* @return <code>true</code> if the table should be rendered,
* <code>false</code> otherwise.
*/
public boolean getTableRendered() {
return getTableModel().getRowCount() > 0;
}
/**
* Returns the table column names.
*
* @return The table column names.
*/
public List<String> getColumnNames() {
return Arrays.asList(columnNames);
}
/**
*
* @return Return the column CSSs classes.
*/
public String getTableColumnCssClasses() {
String res = "";
// hack: in the page the 2 first column are inverted
res += "column-left,column-center,";
for (int i = 2; i < columnTypes.length; i++) {
Class<?> clazz = columnTypes[i];
if (String.class.equals(clazz))
res += "column-left,";
else
res += "column-right,";
}
res = res.substring(0, res.length() - 1);
return res;
}
/**
* Returns the current path selected.
*
* @return The selection path.
*/
public String getSelectionPath() {
return selectionPath;
}
/**
* Return <code>true</code> if the selection path should be displayed,
* <code>false</code> otherwise.
*
* @return The rendered state of the selection path display.
*/
public boolean isSelectionPathRendered() {
return selectionPath != null && !"".equals(selectionPath);
}
}