/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.plugin.charts; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import org.apache.batik.apps.rasterizer.SVGConverterException; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.Plot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.xwiki.environment.Environment; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.api.Api; import com.xpn.xwiki.plugin.XWikiDefaultPlugin; import com.xpn.xwiki.plugin.XWikiPluginInterface; import com.xpn.xwiki.plugin.charts.exceptions.DataSourceException; import com.xpn.xwiki.plugin.charts.exceptions.GenerateException; import com.xpn.xwiki.plugin.charts.params.ChartParams; import com.xpn.xwiki.plugin.charts.plots.PlotFactory; import com.xpn.xwiki.plugin.charts.source.DataSource; import com.xpn.xwiki.plugin.charts.source.MainDataSourceFactory; import com.xpn.xwiki.plugin.svg.SVGPlugin; import com.xpn.xwiki.web.Utils; import com.xpn.xwiki.web.XWikiResponse; /** * TODO: Document this * * @version $Id: 8f42639e4f578d8189062a6974bfa30c60cf9aa9 $ */ public class ChartingPlugin extends XWikiDefaultPlugin implements XWikiPluginInterface { /** * Used to get the temporary directory. */ private Environment environment = Utils.getComponent((Type) Environment.class); public ChartingPlugin(String name, String className, XWikiContext context) { super(name, className, context); init(context); } @Override public void init(XWikiContext context) { super.init(context); LOGGER.info("Charting Plugin - init"); File dir = this.environment.getTemporaryDirectory(); tempDir = new File(dir, "charts"); try { tempDir.mkdirs(); } catch (Exception e1) { LOGGER.warn("Could not create charts temporary directory: " + tempDir, e1); dir = new File(context.getWiki().Param("xwiki.upload.tempdir")); try { tempDir = new File(dir, "charts"); } catch (Exception e2) { LOGGER.error("Could not create charts temporary directory: " + tempDir, e2); } } } @Override public String getName() { return "charting"; } public Chart generateChart(ChartParams params, XWikiContext context) throws GenerateException { try { // Obtain the corresponding data source and wrap it into a data source object DataSource dataSource = MainDataSourceFactory.getInstance().create(params.getMap(ChartParams.SOURCE), context); String type = params.getString(ChartParams.TYPE); Plot plot; try { String factoryClassName = ChartingPlugin.class.getPackage().getName() + ".plots." + Character.toUpperCase(type.charAt(0)) + type.toLowerCase().substring(1) + "PlotFactory"; Class factoryClass = Class.forName(factoryClassName); Method method = factoryClass.getMethod("getInstance", new Class[] {}); PlotFactory factory = (PlotFactory) method.invoke(null, new Object[] {}); plot = factory.create(dataSource, params); } catch (InvocationTargetException e) { throw new GenerateException(e.getTargetException()); } catch (Throwable e) { throw new GenerateException(e); } ChartCustomizer.customizePlot(plot, params); JFreeChart jfchart = new JFreeChart(plot); ChartCustomizer.customizeChart(jfchart, params); return generatePngChart(jfchart, params, context); } catch (IOException ioe) { throw new GenerateException(ioe); } catch (DataSourceException dse) { throw new GenerateException(dse); } } private Chart generateSvgChart(JFreeChart jfchart, ChartParams params, XWikiContext context) throws IOException, GenerateException { // Get a DOMImplementation DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Create an instance of org.w3c.dom.Document Document document = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null); // Create an instance of the SVG Generator SVGGraphics2D svgGenerator = new SVGGraphics2D(document); // Ask the chart to render into the SVG Graphics2D implementation Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, params.getInteger(ChartParams.WIDTH).intValue(), params.getInteger( ChartParams.HEIGHT).intValue()); jfchart.draw(svgGenerator, rect); boolean useCSS = false; StringWriter swriter = new StringWriter(); svgGenerator.stream(swriter, useCSS); String svgText = swriter.toString(); String pageURL = null; SVGPlugin svgPlugin = (SVGPlugin) context.getWiki().getPlugin("svg", context); if (svgPlugin == null) { throw new GenerateException("SVGPlugin not loaded"); } String imageURL; try { imageURL = svgPlugin.getSVGImageURL(svgText, params.getInteger(ChartParams.HEIGHT).intValue(), params.getInteger(ChartParams.WIDTH).intValue(), context); } catch (SVGConverterException sce) { throw new GenerateException(sce); } return new ChartImpl(params, imageURL, pageURL); } private Chart generatePngChart(JFreeChart jfchart, ChartParams params, XWikiContext context) throws IOException, GenerateException { File file = getTempFile(params.hashCode(), "png"); ChartUtilities.saveChartAsPNG(file, jfchart, params.getInteger(ChartParams.WIDTH).intValue(), params .getInteger(ChartParams.HEIGHT).intValue()); String imageURL = context.getDoc().getAttachmentURL(file.getName(), "charting", context); String pageURL = imageURL; return new ChartImpl(params, imageURL, pageURL); } public void outputFile(String filename, XWikiContext context) throws IOException { File ofile = getTempFile(filename); byte[] bytes = readFile(ofile); XWikiResponse response = context.getResponse(); context.setFinished(true); response.setDateHeader("Last-Modified", ofile.lastModified()); response.setContentLength(bytes.length); response.setContentType(context.getEngineContext().getMimeType(filename)); OutputStream os = response.getOutputStream(); os.write(bytes); } public byte[] readFile(File ofile) throws IOException { FileInputStream fis = new FileInputStream(ofile); byte[] result = new byte[(int) ofile.length()]; fis.read(result); return result; } @Override public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context) { return new ChartingPluginApi((ChartingPlugin) plugin, context); } private File getTempFile(int hashcode, String extension) { return getTempFile(hashcode + "." + extension); } private File getTempFile(String filename) { return new File(tempDir, filename); } private static Logger LOGGER = LoggerFactory.getLogger(ChartingPlugin.class); private File tempDir; }