/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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. * * Copyright 2008 - 2009 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.plugin.action.chartbeans; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.chart.AbstractChartThemeFactory; import org.pentaho.chart.ChartBeanFactory; import org.pentaho.chart.ChartBoot; import org.pentaho.chart.InvalidChartDefinition; import org.pentaho.chart.model.ChartModel; import org.pentaho.chart.model.DialPlot; import org.pentaho.chart.model.PiePlot; import org.pentaho.chart.model.Theme; import org.pentaho.chart.model.util.ChartSerializer; import org.pentaho.chart.model.util.ChartSerializer.ChartSerializationFormat; import org.pentaho.chart.plugin.ChartDataOverflowException; import org.pentaho.chart.plugin.ChartProcessingException; import org.pentaho.chart.plugin.NoChartDataException; import org.pentaho.chart.plugin.api.PersistenceException; import org.pentaho.chart.plugin.api.IOutput.OutputTypes; import org.pentaho.chart.plugin.jfreechart.JFreeChartPlugin; import org.pentaho.chart.plugin.openflashchart.OpenFlashChartPlugin; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.reporting.libraries.resourceloader.ResourceException; /** * This is a bean that permits easy access to the ChartBeans functionality and was * specifically designed to be run from within the Pentaho Platform as an Action Sequence * Component. * * @author cboyden * * @deprecated please use {@link ChartAction} * */ public class ChartComponent { protected static final int DEFAULT_CHART_WIDTH = 400; protected static final int DEFAULT_CHART_HEIGHT = 300; protected boolean convertNullsToZero = false; protected String seriesColumnName = null; protected int seriesColumn = -1; protected String categoryColumnName = null; protected int categoryColumn = -1; protected String valueColumnName = null; protected int valueColumn = -1; protected IPentahoResultSet resultSet = null; protected String chartEngine; protected Exception bootException = null; protected String outputType = ""; //$NON-NLS-1$ protected int chartWidth = -1; protected int chartHeight = -1; protected Number scalingFactor = new Double(1); protected OutputStream outputStream = null; protected String chartModelJson = null; protected String chartModelXml = null; protected ChartModel chartModel = null; protected String title = null; // private String flashPath = "openflashchart"; //$NON-NLS-1$ // private String flashSwf = "open-flash-chart-full-embedded-font.swf"; //$NON-NLS-1$s /** * Initialize ChartBeans engine */ { synchronized (ChartBoot.getInstance()) { while (!ChartBoot.getInstance().isBootDone()) { if (ChartBoot.getInstance().isBootInProgress()) { // Wait 1 second try { java.lang.Thread.sleep(1000); } catch (InterruptedException e) { // Do nothing } } else { if (!ChartBoot.getInstance().isBootFailed()) { ChartBoot.getInstance().start(); } } }// End while: boot is not done //Check for an error if (ChartBoot.getInstance().isBootFailed()) { bootException = ChartBoot.getInstance().getBootFailureReason(); } }// End thread synchronization } /** * Called to process the chart definition and data set to produce * a usable chart. * * @return state of execution. 'true' if execution was successful, otherwise false. * @throws ChartBootException * @throws ChartProcessingException * @throws ResourceException * @throws InvalidChartDefinition * @throws IOException * @throws PersistenceException */ public boolean execute() throws ChartBootException, ChartProcessingException, ResourceException, InvalidChartDefinition, IOException, PersistenceException { if (bootException != null) { throw new ChartBootException(bootException); } // Transform IPentahoResultSet to an object array Object[][] data = processChartData(resultSet, valueColumn); try{ if(chartModel.getTheme() != null){ AbstractChartThemeFactory chartThemeFactory = new AbstractChartThemeFactory() { protected List<File> getThemeFiles() { ArrayList<File> themeFiles = new ArrayList<File>(); themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme1.xml"))); //$NON-NLS-1$ themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme2.xml"))); //$NON-NLS-1$ themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme3.xml"))); //$NON-NLS-1$ themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme4.xml"))); //$NON-NLS-1$ themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme5.xml"))); //$NON-NLS-1$ themeFiles.add(new File(PentahoSystem.getApplicationContext().getSolutionPath("system/chartbeans/themes/Theme6.xml"))); //$NON-NLS-1$ return themeFiles; } }; if (!(chartModel.getPlot() instanceof DialPlot)) { Theme chartTheme = chartThemeFactory.getTheme(chartModel.getTheme()); if (chartTheme != null) { chartTheme.applyTo(chartModel); } } } // Make sure chart engine is loaded loadChartEngine(); // Set chart engine on chartModel for the ChartFactory to use chartModel.setChartEngineId(chartEngine); InputStream is = null; try { is = ChartBeanFactory.createChart(data, scalingFactor, convertNullsToZero, valueColumn, seriesColumn, categoryColumn, chartModel, chartWidth, chartHeight, getOutputType()); // Wrap output as necessary if(OpenFlashChartPlugin.PLUGIN_ID.equals(chartEngine)){ // Convert stream to string, insert into HTML fragment and re-stream it StringBuilder sb = new StringBuilder(); int c = 0; // Build string while((c = is.read()) >= 0){ sb.append((char)c); } String flashContent = ChartBeansGeneratorUtil.mergeOpenFlashChartHtmlTemplate(sb.toString().replaceAll("\"", "\\\\\""), //$NON-NLS-1$ //$NON-NLS-2$ PentahoSystem.getApplicationContext().getBaseUrl() + this.getSwfPath() + "/" + getSwfName()); //$NON-NLS-1$ is = new ByteArrayInputStream(flashContent.getBytes("utf-8")); //$NON-NLS-1$ } int val = 0; //TODO: Buffer for more efficiency while((val = is.read()) != -1){ outputStream.write(val); } } catch (NoChartDataException ex) { if (JFreeChartPlugin.PLUGIN_ID.equals(chartEngine)) { BufferedImage image = new BufferedImage(chartWidth, chartHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = image.createGraphics(); graphics.setFont(new Font("serif", Font.BOLD, 14)); //$NON-NLS-1$ graphics.setColor(Color.BLACK); graphics.drawString("The chart data query returned no data.", 5, 5); //$NON-NLS-1$ String outputType = getMimeType().equals("image/jpg") ? "jpeg" : "png"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ImageIO.write(image, outputType, outputStream); } else { String flashContent = ChartBeansGeneratorUtil.buildEmptyOpenFlashChartHtmlFragment("The chart data query returned no data."); //$NON-NLS-1$ is = new ByteArrayInputStream(flashContent.getBytes("utf-8")); //$NON-NLS-1$ int val = 0; //TODO: Buffer for more efficiency while((val = is.read()) != -1){ outputStream.write(val); } } } catch (ChartDataOverflowException ex) { if (JFreeChartPlugin.PLUGIN_ID.equals(chartEngine)) { BufferedImage image = new BufferedImage(chartWidth, chartHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = image.createGraphics(); graphics.setFont(new Font("serif", Font.BOLD, 14)); //$NON-NLS-1$ graphics.setColor(Color.BLACK); graphics.drawString(Messages.getInstance().getErrorString("ChartComponent.TOO_MANY_DATA_POINTS"), 5, 5); //$NON-NLS-1$ graphics.drawString(Messages.getInstance().getErrorString("ChartComponent.MAX_ALLOWED_DATA_POINTS", Integer.toString(ex.getMaxAllowedDataPoints())), 5, 25); //$NON-NLS-1$ String outputType = getMimeType().equals("image/jpg") ? "jpeg" : "png"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ImageIO.write(image, outputType, outputStream); } else { String flashContent = ChartBeansGeneratorUtil.buildEmptyOpenFlashChartHtmlFragment(Messages.getInstance().getErrorString("ChartComponent.TOO_MANY_DATA_POINTS_HTML", Integer.toString(ex.getMaxAllowedDataPoints()))); //$NON-NLS-1$ is = new ByteArrayInputStream(flashContent.getBytes("utf-8")); //$NON-NLS-1$ int val = 0; //TODO: Buffer for more efficiency while((val = is.read()) != -1){ outputStream.write(val); } } } } catch(SQLException e){ //No SQLException possible from this usage } return true; } /** * Transform the IPentahoResultSet into the data format suitable * for chart creation. * * @return Row / Column data table or null */ protected Object[][] processChartData(IPentahoResultSet resultSet, int valueColumnIndex){ if(resultSet == null){ return null; } Object[][] result = null; result = new Object[resultSet.getRowCount()][resultSet.getMetaData().getColumnCount()]; for(int r = 0; r < resultSet.getRowCount(); r++){ for(int c = 0; c < resultSet.getMetaData().getColumnCount(); c++){ result[r][c] = resultSet.getValueAt(r, c); } } return(result); } /** * Define the OutputStream to which the resulting chart shall be written * @param outStream Stream receive the chart */ public void setOutputStream(OutputStream outStream) { outputStream = outStream; } /** * Define the data set that will populate the chart * @param chartDataSet data set for charting */ public void setChartData(IPentahoResultSet chartDataSet){ resultSet = chartDataSet.memoryCopy(); } /** * Validate the current settings of the ChartComponent. If validate() returns true, * then execute may be called. If validate() returns false, a call to execute() is guaranteed * to fail. * @return state of validation * @throws Exception */ public boolean validate() throws Exception{ //Must have a valid result set if(resultSet == null){ return false; } //Default to the first three columns if no others are explicitly specified //Resolve column name to column ordinal if present if(seriesColumnName != null){ //Leave it at -1 if it is specified as blank (The charting engine will handle this properly) if(!seriesColumnName.equals("")){ //$NON-NLS-1$ seriesColumn = resultSet.getMetaData().getColumnIndex(seriesColumnName); } } else { //Set default ordering as no ordinal has been defined if(seriesColumn < 0){ seriesColumn = 0; } } if(categoryColumnName != null){ //Leave it at -1 if it is specified as blank (The charting engine will handle this properly) if(!categoryColumnName.equals("")){ //$NON-NLS-1$ categoryColumn = resultSet.getMetaData().getColumnIndex(categoryColumnName); } } else { //Set default ordering as no ordinal has been defined if(categoryColumn < 0){ categoryColumn = 1; } } if(valueColumnName != null){ //Leave it at -1 if it is specified as blank (The charting engine will handle this properly) if(!valueColumnName.equals("")){ //$NON-NLS-1$ valueColumn = resultSet.getMetaData().getColumnIndex(valueColumnName); } } else { //Set default ordering as no ordinal has been defined if(valueColumn < 0){ valueColumn = 2; } } loadChartEngine(); if(chartModel == null){ return false; } //Verify that all columns required for a given chart type are present if(chartModel.getPlot() instanceof DialPlot){ if(valueColumn < 0){ return false; } } else if(chartModel.getPlot() instanceof PiePlot){ if((seriesColumn < 0) || (valueColumn < 0)){ return false; } } else { if((seriesColumn < 0) || (categoryColumn < 0) || (valueColumn < 0)){ return false; } } if(chartWidth <= 0){ chartWidth = DEFAULT_CHART_WIDTH; } if(chartHeight <= 0){ chartHeight = DEFAULT_CHART_HEIGHT; } return true; } /** * Define the column in the data set that contains the Series/Domain data * @param seriesCol name of column that contains the Series/Domain for the chart */ public void setSeriesColumn(String seriesCol){ seriesColumnName = seriesCol; } public void setConvertNullsToZero(boolean convert){ this.convertNullsToZero = convert; } public boolean getConvertNullsToZero(){ return convertNullsToZero; } /** * Define the column in the data set that contains the Category data * @param seriesCol name of column that contains the Category for the chart */ public void setCategoryColumn(String categoryCol) { categoryColumnName = categoryCol; } /** * Define the column in the data set that contains the Value/Range data * @param seriesCol name of column that contains the Value/Range for the chart */ public void setValueColumn(String valueCol) { valueColumnName = valueCol; } /** * Fetch the desired output type * @return output type */ protected OutputTypes getOutputType(){ if(outputType.equals("jpg")){ //$NON-NLS-1$ return OutputTypes.FILE_TYPE_JPEG; } else if (outputType.equals("png")){ //$NON-NLS-1$ return OutputTypes.FILE_TYPE_PNG; } return null; } /** * Fetch the desired MimeType * @return mime type */ public String getMimeType(){ loadChartEngine(); if (JFreeChartPlugin.PLUGIN_ID.equals(chartEngine)) { if(outputType.equalsIgnoreCase("jpg")){ //$NON-NLS-1$ return "image/jpg"; //$NON-NLS-1$ } else if(outputType.equalsIgnoreCase("png")){ //$NON-NLS-1$ return "image/png"; //$NON-NLS-1$ } //Default JFREE action outputType = "png"; //$NON-NLS-1$ return "image/png"; //$NON-NLS-1$ } else if (OpenFlashChartPlugin.PLUGIN_ID.equals(chartEngine)) { outputType = "html"; //$NON-NLS-1$ return "text/html"; //$NON-NLS-1$ } // Final component default is OFC return "text/html"; //$NON-NLS-1$ } /** * Sets the chart engine based on the order of precedence: * 1) Chart Definition * 2) Action Sequence * 3) System Setting * 4) Hard Coded */ protected void loadChartEngine(){ loadChartModel(); if(chartModel != null){ if(chartModel.getChartEngineId() != null){ this.chartEngine = chartModel.getChartEngineId(); // Defined in ChartModel, escape return; } } if(this.chartEngine != null){ // Engine set on Action Sequence, escape return; } // Load default value from system setting or take hard coded // Hard coded final fall back is Open Flash Chart String defaultChartEngine = PentahoSystem.getSystemSetting("chartbeans/chartbeans_config.xml", "default-chart-engine", OpenFlashChartPlugin.PLUGIN_ID); //$NON-NLS-1$ //$NON-NLS-2$ if(defaultChartEngine == null){ defaultChartEngine = OpenFlashChartPlugin.PLUGIN_ID; } this.chartEngine = defaultChartEngine; } protected void loadChartModel(){ if(chartModel == null){ if(chartModelJson != null){ chartModel = ChartSerializer.deSerialize(chartModelJson, ChartSerializationFormat.JSON); } else { if(chartModelXml != null){ chartModel = ChartSerializer.deSerialize(chartModelXml, ChartSerializationFormat.XML); } } } } /** * Set the JSON representation of the ChartModel * @param chartModelJson JSON serialized representation of the ChartModel */ public void setChartModelJson(String chartModelJson) { this.chartModelJson = chartModelJson; } /** * Set the XML representation of the ChartModel * @param chartStyleXml XML serialized representation of the ChartModel */ public void setChartModelXml(String chartModelXml){ this.chartModelXml = chartModelXml; } /** * Set the ChartModel * @param chartModel model of the chart to be generated */ public void setChartModel(ChartModel chartModel){ this.chartModel = chartModel; } /** * Set the width of the chart in units specific to the ChartPlugin * @param chartWidth width of the chart */ public void setChartWidth(int chartWidth){ this.chartWidth = chartWidth; } /** * Set the height of the chart in units specific to the ChartPlugin * @param chartHeight height of the chart */ public void setChartHeight(int chartHeight){ this.chartHeight = chartHeight; } /** * Set the width of the chart in units specific to the ChartPlugin * @param chartWidth width of the chart */ public void setChartWidth(String chartWidth){ this.chartWidth = Integer.valueOf(chartWidth); } /** * Set the height of the chart in units specific to the ChartPlugin * @param chartHeight height of the chart */ public void setChartHeight(String chartHeight){ this.chartHeight = Integer.valueOf(chartHeight); } /** * Get the chart engine that the resulting chart was created through * @return */ public String getChartEngine() { loadChartEngine(); return chartEngine; } /** * Set the chart engine to render the chart * @param chartEngine Value of "JFreeChart" or "OpenFlashChart" */ public void setChartEngine(String chartEngine) { this.chartEngine = chartEngine; } public void setOutputType(String outputType) { this.outputType = outputType; } public void setScalingFactor(Double scalingFactor) { this.scalingFactor = scalingFactor; } public String getSwfPath(){ return "openflashchart"; //$NON-NLS-1$ } public String getSwfName(){ return "open-flash-chart-full-embedded-font.swf"; //$NON-NLS-1$ } }