/*
* 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 2009 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.plugin.action.openflashchart;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Node;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.api.util.XmlParseException;
import org.pentaho.platform.engine.services.runtime.TemplateUtil;
import org.pentaho.platform.engine.services.solution.ComponentBase;
import org.pentaho.platform.engine.services.solution.PentahoEntityResolver;
import org.pentaho.platform.plugin.action.messages.Messages;
import org.pentaho.platform.plugin.action.openflashchart.factory.PentahoOFC4JChartHelper;
import org.pentaho.platform.util.UUIDUtil;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;
/**
* This component is a contribution from Nick Goodman and Tom Qin,
* it conforms to the ChartComponent API. Please see the wiki for details on this
* component.
*
* The component uses open-flash-chart-full-embedded-font.swf, found at
* http://www.ofc2dz.com/, which is a patched version of Open Flash Chart 2, found at
* http://teethgrinder.co.uk/open-flash-chart-2/
*
* note: onclick events are only partially implemented, due to limitations of
* OFC. In future releases of OFC, this should be revisited.
*
* Backlog:
* - y2_legend - barline right axis title, would need to update OFC4J
* - horizontal stacked bars
* http://www.ofc2dz.com/OFC2/examples/HorizontalStackedBars.html
* - styled stacked bars (would require impl in OFC)
* - dial chart (would require impl in OFC)
* - XY Line Chart
* - XY Area Chart
* - onclick / link support (would require enhancements to OFC)
* - allow override of dataFunction name
* - review colors, move into config file for both jfree and ofc?
* - area stacked
*
* @author Nick Goodman
* @author Tom Qin
* @author Will Gorman (wgorman@pentaho.com)
*/
public class OpenFlashChartComponent extends ComponentBase {
private static final long serialVersionUID = 825147871232129168L;
private static final Log log = LogFactory.getLog(OpenFlashChartComponent.class);
private static final String DEFAULT_FLASH_LOC = "openflashchart"; //$NON-NLS-1$
private static final String DEFAULT_FLASH_SWF = "open-flash-chart-full-embedded-font.swf"; //$NON-NLS-1$s
private static final String CHART_DATA = "chart-data"; //$NON-NLS-1$
private static final String CHART_WIDTH = "width"; //$NON-NLS-1$
private static final String CHART_HEIGHT = "height"; //$NON-NLS-1$
private static final String OFC_URL = "ofc_url"; //$NON-NLS-1$
// Static declarations
public static String CHART_NODE_LOC = "chart"; //$NON-NLS-1$
private static final String CHART_ATTRIBUTES = "chart-attributes"; //$NON-NLS-1$
private static final String OFC_LIB_NAME = "ofc_lib_name"; //$NON-NLS-1$
private static final String BY_ROW_PROP = "by-row"; //$NON-NLS-1$
private static final String DEFAULT_WIDTH = "100%"; //$NON-NLS-1$
private static final String DEFAULT_HEIGHT = "100%"; //$NON-NLS-1$
protected String template = null;
protected IPentahoResultSet data = null;
protected static String flashFragment =
"<script>function {dataFunction}() { return \"{chartJson}\";}</script>" //$NON-NLS-1$
+ "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" " //$NON-NLS-1$
+ "codebase=\"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0\" " //$NON-NLS-1$
+ "width=\"{chart-width}\" height=\"{chart-height}\" id=\"ofco{chartId}\" align=\"middle\"> " //$NON-NLS-1$
+ "<param name=\"allowScriptAccess\" value=\"sameDomain\" /> " //$NON-NLS-1$
+ "<param name=\"wmode\" value=\"opaque\">" //$NON-NLS-1$
+ "<param name=\"movie\" value=\"{ofc-url}/{ofc-libname}?get-data={dataFunction}\" /> " //$NON-NLS-1$
+ "<param name=\"quality\" value=\"high\" /> " //$NON-NLS-1$
+ "<embed src=\"{ofc-url}/{ofc-libname}?get-data={dataFunction}\" wmode=\"opaque\" quality=\"high\" bgcolor=\"#FFFFFF\" " //$NON-NLS-1$
+ "width=\"{chart-width}\" height=\"{chart-height}\" id=\"ofce{chartId}\" align=\"middle\" allowScriptAccess=\"sameDomain\" type=\"application/x-shockwave-flash\" " //$NON-NLS-1$
+ "pluginspage=\"http://www.macromedia.com/go/getflashplayer\" /></object>"; //$NON-NLS-1$
public OpenFlashChartComponent() {
// default constructor
}
protected boolean validateAction() {
// See if we have the chart data
if (!isDefinedInput(CHART_DATA)) {
inputMissingError(CHART_DATA);
return false;
}
// See if we have chart attributes
if (!isDefinedInput(CHART_ATTRIBUTES)) {
if (!isDefinedResource(CHART_ATTRIBUTES)) {
inputMissingError(CHART_ATTRIBUTES);
return false;
}
}
return true;
}
protected boolean executeAction() {
log.debug("start to run open flash chart component......"); //$NON-NLS-1$
// input data
IPentahoResultSet data = (IPentahoResultSet) getInputValue(CHART_DATA);
// chart width
String chartWidth = null;
String inputWidth = getInputStringValue(CHART_WIDTH);
if (inputWidth == null) {
chartWidth = DEFAULT_WIDTH;
} else {
chartWidth = inputWidth;
}
// chart height
String chartHeight = null;
String inputHeight = getInputStringValue(CHART_HEIGHT);
if (null == inputHeight) {
chartHeight = DEFAULT_HEIGHT;
} else {
chartHeight = inputHeight;
}
// swf file location
String ofcURL = getInputStringValue(OFC_URL);
if (ofcURL == null || "".equals(ofcURL)) { //$NON-NLS-1$
ofcURL = DEFAULT_FLASH_LOC;
}
// swf file name
String ofclibname = getInputStringValue(OFC_LIB_NAME);
if (ofclibname == null || "".equals(ofclibname)) { //$NON-NLS-1$
ofclibname = DEFAULT_FLASH_SWF;
}
// chart definition
String chartAttributeString = null;
if (getInputNames().contains(CHART_ATTRIBUTES)) {
chartAttributeString = getInputStringValue(CHART_ATTRIBUTES);
} else if (isDefinedResource(CHART_ATTRIBUTES)) {
IActionSequenceResource resource = getResource(CHART_ATTRIBUTES);
chartAttributeString = getResourceAsString(resource);
}
Node chartNode = null;
if (chartAttributeString != null) {
// apply any additional inputs to the chart definition
chartAttributeString = applyInputsToFormat(chartAttributeString);
// parse the xml
try {
Document chartDocument = XmlDom4JHelper.getDocFromString(chartAttributeString, new PentahoEntityResolver());
chartNode = chartDocument.selectSingleNode(CHART_NODE_LOC);
if (chartNode == null) {
chartNode = chartDocument.selectSingleNode(CHART_ATTRIBUTES);
}
} catch (XmlParseException e) {
getLogger().error(Messages.getInstance().getErrorString("OpenFlashChartComponent.ERROR_0001_CANT_DOCUMENT_FROM_STRING"), e);//$NON-NLS-1$
return false;
}
} else {
// see if the chart-attributes node is available in the component definition
chartNode = getComponentDefinition(true).selectSingleNode(CHART_ATTRIBUTES);
}
// if the chart def isn't available, exit
if (chartNode == null) {
getLogger().error(Messages.getInstance().getErrorString("OpenFlashChartComponent.ERROR_0002_CHART_DEFINITION_NOT_FOUND"));//$NON-NLS-1$
return false;
}
// Determine if we are going to read the chart data set by row or by column
boolean byRow = false;
if (getInputStringValue(BY_ROW_PROP) != null) {
byRow = Boolean.valueOf(getInputStringValue(BY_ROW_PROP)).booleanValue();
}
String chartJson = PentahoOFC4JChartHelper.generateChartJson(chartNode, data, byRow, log);
// generate a unique name for the function
String chartId = UUIDUtil.getUUIDAsString().replaceAll("[^\\w]",""); //$NON-NLS-1$ //$NON-NLS-2$
// populate the flash html template
Properties props = new Properties();
props.setProperty("chartId", chartId); //$NON-NLS-1$
props.setProperty("dataFunction", "getData" + chartId); //$NON-NLS-1$ //$NON-NLS-2$
props.setProperty("chart-width", chartWidth); //$NON-NLS-1$
props.setProperty("chart-height", chartHeight); //$NON-NLS-1$
props.setProperty("ofc-url", ofcURL); //$NON-NLS-1$
props.setProperty("ofc-libname", ofclibname); //$NON-NLS-1$
props.setProperty("chartJson", chartJson.replaceAll("\"", "\\\\\"")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
String flashContent = TemplateUtil.applyTemplate(getFlashFragment(), props, null);
// set output value
log.debug("html_fragment=" + flashContent); //$NON-NLS-1$
if (this.isDefinedOutput("html_fragment")) { //$NON-NLS-1$
this.setOutputValue("html_fragment", flashContent); //$NON-NLS-1$
}
if (this.isDefinedOutput("image-tag")) { //$NON-NLS-1$
this.setOutputValue("image-tag", flashContent); //$NON-NLS-1$
}
return true;
}
protected String getFlashFragment() {
return flashFragment;
}
public void done() {
// nothing to do here
}
public boolean init() {
// nothing to do here
return true;
}
public boolean validateSystemSettings() {
return true;
}
public Log getLogger() {
return log;
}
}