package jamel.gui;
import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.text.NumberFormat;
import java.util.Locale;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.TickUnitSource;
import org.jfree.chart.plot.DefaultDrawingSupplier;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import jamel.util.Simulation;
/**
* A collection of utility methods for creating charts.
*
* TODO / WORK IN PROGRESS
*/
public class JamelChartFactory {
/**
* The font used for axis labels.
*/
private static final Font axisLabelFont = new Font("Tahoma", Font.PLAIN, 12);
/**
* The font used for tick labels.
*/
private static final Font tickLabelFont = new Font("Tahoma", Font.PLAIN, 10);
/**
* Creates and returns a new Y axis.
*
* @param description
* an XML element with the description of the axis.
* @param source
* the tick unit source.
* @return a new Y axis.
*/
private static NumberAxis createAxis(Element description, TickUnitSource source) {
final NumberAxis axis;
if (description != null && description.getAttribute("logarithmic").equals("true")) {
axis = new LogarithmicAxis(null);
} else {
axis = new NumberAxis(null);
if (source != null) {
axis.setStandardTickUnits(source);
}
}
axis.setAutoRangeIncludesZero(false);
if (description != null && !description.getAttribute("label").equals("")) {
axis.setLabel(description.getAttribute("label"));
}
axis.setTickLabelFont(tickLabelFont);
if (description != null) {
final Double max = parseDouble(getTagText("max", description));
// final Double max = parseDouble(description.getAttribute("max"));
if (max != null) {
axis.setUpperBound(max);
}
final Double min = parseDouble(getTagText("min", description));
// final Double min = parseDouble(description.getAttribute("min"));
if (min != null) {
/*if (max == null && min == 0) {
axis.setAutoRangeIncludesZero(true);
} else*/ {
axis.setLowerBound(min);
}
}
}
axis.setNumberFormatOverride(NumberFormat.getInstance(Locale.US));
return axis;
}
/**
* Creates and returns a new plot with the specified dataset, axes and
* renderer.
*
* @param dataset
* the dataset (<code>null</code> permitted).
* @param xAxis
* the x axis (<code>null</code> permitted).
* @param yAxis
* the y axis (<code>null</code> permitted).
* @param renderer
* the renderer (<code>null</code> permitted).
* @return a new plot.
*/
private static XYPlot createXYPlot(XYDataset dataset, NumberAxis xAxis, NumberAxis yAxis, XYItemRenderer renderer) {
final XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setOrientation(PlotOrientation.VERTICAL);
plot.setDomainGridlinesVisible(false);
plot.setDomainMinorGridlinesVisible(false);
plot.setRangeGridlinesVisible(false);
plot.setRangeMinorGridlinesVisible(false);
plot.setRangeCrosshairVisible(false);
plot.setDomainCrosshairVisible(false);
plot.setBackgroundPaint(Color.WHITE);
plot.getRangeAxis().setLabelFont(axisLabelFont);
plot.getDomainAxis().setLabelFont(axisLabelFont);
return plot;
}
/**
* Creates and returns a new XY chart.
*
* @param description
* the description of the XY chart to create.
* @param simulation
* the parent simulation.
* @return a new XY chart.
*/
private static JamelChart getNewXYChart(final Element description, final Simulation simulation) {
final String name = description.getAttribute("title");
final NodeList seriesList = description.getElementsByTagName("series");
final int nbSeries = seriesList.getLength();
final XYSeriesCollection dataset = new XYSeriesCollection();
final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
final DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier();
final LegendItemCollection defaultLegendItemCollection = new LegendItemCollection();
for (int k = 0; k < nbSeries; k++) {
final Element seriesElement = (Element) seriesList.item(k);
final String x = getTagText("x", seriesElement);
final String y = getTagText("y", seriesElement);
// final Integer start = null;// parseIntegerAttribute(serieXML,
// "start");
// final Integer end = null;// parseIntegerAttribute(serieXML,
// "end");
// TODO uses start, end, mod
final XYSeries newSeries = simulation.getSeries(x, y);
if (newSeries != null) {
dataset.addSeries(simulation.getSeries(x, y));
// renderer.setSeriesLinesVisible(k,
// (seriesElement.getAttribute("linesVisible").equals("true")));
// renderer.setSeriesPaint(k, lineColor);
// final String color = seriesElement.getAttribute("color");
final Paint seriesPaint;
{
final String color = getTagText("color", seriesElement);
if (color == null) {
seriesPaint = drawingSupplier.getNextPaint();
} else {
seriesPaint = JamelColor.getColor(color);
}
}
renderer.setSeriesPaint(k, seriesPaint);
// Shapes
if (seriesElement.getAttribute("shapesVisible").equals("true")) {
renderer.setSeriesShapesVisible(k, true);
final String fillColor = getTagText("fillColor", seriesElement);
if (fillColor == null) {
renderer.setUseFillPaint(false);
} else {
renderer.setUseFillPaint(true);
renderer.setSeriesFillPaint(k, JamelColor.getColor(fillColor));
}
} else {
renderer.setSeriesShapesVisible(k, false);
}
// Preparing default legend:
final String legendLabel;
final String tooltip;
final String seriesKey = "x=" + x + ", y=" + y;
if (seriesElement.getAttribute("label").equals("")) {
legendLabel = seriesKey;
tooltip = null;
} else {
legendLabel = seriesElement.getAttribute("label");
tooltip = seriesKey;
}
/* TODO IMPLEMENT final LegendItem legendItem = new LegendItem(legendLabel, null, tooltip, null, true, square, true, paint,
true, lineColor, basicStroke, false, null, basicStroke, null);
defaultLegendItemCollection.add(legendItem);*/
}
}
final NumberAxis xAxis = createAxis((Element) description.getElementsByTagName("xAxis").item(0), null);
final NumberAxis yAxis = createAxis((Element) description.getElementsByTagName("yAxis").item(0), null);
// xAxis.setTickUnit(new NumberTickUnit(120));
final XYPlot plot = createXYPlot(dataset, xAxis, yAxis, renderer);
// TODO addZeroBaselines(plot, description);
final NodeList legends = description.getElementsByTagName("legend");
if (legends.getLength() > 0) {
Element legend = (Element) legends.item(0);
final NodeList items = legend.getElementsByTagName("item");
final LegendItemCollection legendItemCollection = new LegendItemCollection();
for (int i = 0; i < items.getLength(); i++) {
final Element item = (Element) items.item(i);
final String legendLabel = item.getAttribute("value");
final Paint paint = JamelColor.getColor(item.getAttribute("color"));
final String tooltip = item.getAttribute("tooltip");
/*TODO final LegendItem legendItem = new LegendItem(legendLabel, null, tooltip, null, true, square, true,
paint, true, lineColor, basicStroke, false, null, basicStroke, null);
try {
legendItemCollection.add(legendItem);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
// ???
}*/
}
plot.setFixedLegendItems(legendItemCollection);
} else {
plot.setFixedLegendItems(defaultLegendItemCollection);
}
final String background = description.getAttribute("background");
if (!"".equals(background)) {
plot.setBackgroundPaint(JamelColor.getColor(background));
}
// TODO addMarkers(plot, description.getElementsByTagName("marker"));
final JamelChart result = new JamelChart(name, plot) {
@Override
public void addTimeMarker(ValueMarker marker) {
// Does nothing. TODO implement
}
};
return result;
}
/**
* Returns the text content of the first node with the given tag name within
* the specified element. If there no node with given name, this returns
* <code>null</code>.
*
* @param tagName
* the name of the tag to match on. The special value "*"
* matches all tags.
* @param element
* the parent element.
* @return the text content of first node with the given tag name.
*/
private static String getTagText(final String tagName, final Element element) {
final String result;
final Node node = element.getElementsByTagName(tagName).item(0);
if (node == null) {
result = null;
} else {
result = node.getTextContent().trim();
}
return result;
}
/**
* Returns a new double initialized to the value represented by the
* specified String.
*
* @param s
* the string to be parsed.
* @return the double value represented by the string argument.
*/
private static Double parseDouble(String s) {
final Double result;
if (s == null || s.isEmpty()) {
result = null;
} else {
result = Double.parseDouble(s);
}
return result;
}
/**
* Creates and returns a new chart panel.
*
* @param elem
* the description of the chart panel to create.
* @param simulation
* the parent simulation.
* @return a new chart panel.
*/
public static JamelChartPanel createChartPanel(final Element elem, final Simulation simulation) {
// final String type = elem.getAttribute("type");
final JamelChart chart = getNewXYChart(elem, simulation);
/*if ("".equals(type)) {
throw new InitializationException("Chart " + elem.getAttribute("title") + ": Chart type is missing.");
} else if ("scatter chart".equals(type)) {
chart = createScatterChart(elem, dataManager);
} else if ("time scatter chart".equals(type)) {
chart = createTimeScatterChart(elem, dataManager);
} else if ("time chart".equals(type)) {
chart = createTimeChart(elem, dataManager);
} else if ("histogram".equals(type)) {
chart = createHistogram(elem, dataManager);
} else {
throw new InitializationException("Unexpected chart type: " + type);
}*/
// chart.getLegend().setItemFont(axisLabelFont);
// TODO l'ajustement des fonts n'a pas sa place ici
final JamelChartPanel chartPanel = new JamelChartPanel(chart);
return chartPanel;
}
}