/*==========================================================================*\ | $Id: JFreeChartComponent.java,v 1.3 2010/10/19 18:37:37 aallowat Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2010 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.grader.graphs; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.log4j.Logger; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.xy.XYDataset; import org.webcat.core.Application; import org.webcat.core.WCComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WOElement; import com.webobjects.foundation.NSData; //------------------------------------------------------------------------- /** * An abstract base class for the other components that render JFreeChart * images (StackedAreaChart and HistogramChart). * * @author Tony Allevato * @version $Id: JFreeChartComponent.java,v 1.3 2010/10/19 18:37:37 aallowat Exp $ */ public abstract class JFreeChartComponent extends WCComponent { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Initializes a new instance of the JFreeChartComponent class. * * @param context the context */ public JFreeChartComponent(WOContext context) { super(context); } //~ Methods ............................................................... // ---------------------------------------------------------- /** * Automatically provides the component template for subclasses. * * @return the component template */ @Override public WOElement template() { if (cachedTemplate == null) { String htmlTemplate = "<webobject name=\"ShouldDisplay\">" + "<webobject name=\"Chart\"/>" + "</webobject>"; String bindingDefinitions = "Chart: WOImage {" + "data = pngChart;" + "mimeType = \"image/png\";"; if (!sizesToFit()) { bindingDefinitions += "width = chartWidth;" + "height = chartHeight;"; } if (style != null) { bindingDefinitions += "style = \"" + style + "\";"; } bindingDefinitions += "title = title;" + "alt = title;" + "}" + "ShouldDisplay: WOConditional {" + "condition = shouldDisplay;" + "}"; cachedTemplate = templateWithHTMLString(null, null, htmlTemplate, bindingDefinitions, null, Application.application().associationFactoryRegistry(), Application.application().namespaceProvider()); } return cachedTemplate; } // ---------------------------------------------------------- /** * Gets the dataset used to generate this chart. * * @return the dataset */ public XYDataset dataset() { return dataset; } // ---------------------------------------------------------- /** * Sets the dataset used to generate this chart. * * @param aDataset the dataset */ public void setDataset(XYDataset aDataset) { dataset = aDataset; // Clear the cached chart so that it has to be regenerated with the // new data. pngChart = null; } // ---------------------------------------------------------- /** * Gets the title to display at the top of this chart. * * @return the chart title */ public String title() { return title; } // ---------------------------------------------------------- /** * Sets the title to display at the top of this chart. * * @param aTitle the chart title */ public void setTitle(String aTitle) { title = aTitle; // Clear the cached chart so that it has to be regenerated with the // new labels. pngChart = null; } // ---------------------------------------------------------- /** * Gets the label to display on the x-axis of this chart. * * @return the x-axis label */ public String xAxisLabel() { return xAxisLabel; } // ---------------------------------------------------------- /** * Sets the label to display on the x-axis of this chart. * * @param aLabel the x-axis label */ public void setXAxisLabel(String aLabel) { xAxisLabel = aLabel; // Clear the cached chart so that it has to be regenerated with the // new labels. pngChart = null; } // ---------------------------------------------------------- /** * Gets the label to display on the y-axis of this chart. * * @return the y-axis label */ public String yAxisLabel() { return yAxisLabel; } // ---------------------------------------------------------- /** * Sets the label to display on the y-axis of this chart. * * @param aLabel the y-axis label */ public void setYAxisLabel(String aLabel) { yAxisLabel = aLabel; // Clear the cached chart so that it has to be regenerated with the // new labels. pngChart = null; } // ---------------------------------------------------------- /** * Gets the orientation of this chart. * * @return the chart orientation */ public PlotOrientation orientation() { return orientation; } // ---------------------------------------------------------- /** * Sets the orientation of this chart. * * @param anOrientation the chart orientation */ public void setOrientation(PlotOrientation anOrientation) { orientation = anOrientation; // Clear the cached chart so that it has to be regenerated with the // new orientation. pngChart = null; } // ---------------------------------------------------------- /** * Gets the width of the chart. * * @return the width of the chart */ public int chartWidth() { // Force generation of the cached chart, which may force the size of // the image to change. pngChart(); return chartWidth; } // ---------------------------------------------------------- /** * Sets the width of the chart. * * @param value the width of the chart */ public void setChartWidth(int value) { chartWidth = value; // Clear the cached chart so that it has to be regenerated with the // new size. pngChart = null; } // ---------------------------------------------------------- /** * Gets the height of the chart. * * @return the height of the chart */ public int chartHeight() { // Force generation of the cached chart, which may force the size of // the image to change. pngChart(); return chartHeight; } // ---------------------------------------------------------- /** * Sets the height of the chart. * * @param value the height of the chart */ public void setChartHeight(int value) { chartHeight = value; // Clear the cached chart so that it has to be regenerated with the // new size. pngChart = null; } // ---------------------------------------------------------- /** * Gets the label to display on the x-axis of this chart. * * @return the x-axis label */ public String style() { return style; } // ---------------------------------------------------------- /** * Sets the label to display on the x-axis of this chart. * * @param aLabel the x-axis label */ public void setStyle(String aStyle) { style = aStyle; } // ---------------------------------------------------------- /** * Gets a value indicating whether the specified chart width and height * values should be ignored and the image tag should automatically size to * fit the chart. * * @return true to ignore the chart width/height; false to force the * specified dimensions */ public boolean sizesToFit() { return false; } // ---------------------------------------------------------- /** * Gets a value indicating whether this chart should be displayed. * * @return true to display the chart; false to hide it */ public boolean shouldDisplay() { return dataset != null; } // ---------------------------------------------------------- /** * Gets the PNG image data for this chart. * * @return an NSData object containing the PNG image data */ public NSData pngChart() { if (pngChart != null) { return pngChart; } // We synchronize on the ChartFactory class since the chart theme that // we use is based on the user's settings, and we want to ensure that // only one user is modifying it at a time. synchronized (ChartFactory.class) { WCChartTheme chartTheme = new WCChartTheme(wcSession().theme()); ChartFactory.setChartTheme(chartTheme); JFreeChart chart = generateChart(chartTheme); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Adjust the chart height, if necessary. double maxItems = 2; for (int i = 0; i < dataset.getItemCount(0); i++) { double x = dataset.getYValue(0, i); if (x > maxItems) { maxItems = x; } } int minHeight = minimumHeight(); if (maxItems < 10 && chartHeight > minHeight) { chartHeight -= minHeight; maxItems -= 2; chartHeight = (int) (chartHeight * maxItems / 10 + 0.5); chartHeight += minHeight; } try { ChartUtilities.writeChartAsPNG( baos, chart, chartWidth, chartHeight); pngChart = new NSData(baos.toByteArray()); } catch (IOException e) { log.error("Exception creating chart", e); } } return pngChart; } // ---------------------------------------------------------- /** * Gets the minimum height that this chart can be sized. * * @return the minimum height of the chart */ protected int minimumHeight() { return 100; } // ---------------------------------------------------------- /** * Generates the JFreeChart object for the chart. Subclasses must override * this to provide the appropriate type of chart. * * @param chartTheme the chart theme used to determine colors * @return the JFreeChart object for the chart */ protected abstract JFreeChart generateChart(WCChartTheme chartTheme); //~ Static/instance variables ............................................. private WOElement cachedTemplate; private NSData pngChart; private XYDataset dataset; private int chartWidth = 400; private int chartHeight = 400; private String title; private String xAxisLabel; private String yAxisLabel; private String style; private PlotOrientation orientation = PlotOrientation.VERTICAL; protected static Logger log = Logger.getLogger(JFreeChartComponent.class); }