org.pentaho.platform.uifoundation.chart.FlashChartComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.uifoundation.chart.FlashChartComponent.java

Source

/*
 * This program is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License, version 2 as published by the Free Software 
 * Foundation.
 *
 * You should have received a copy of the GNU General Public License along with this 
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details.
 *
 *
 * Copyright 2006 - 2008 Pentaho Corporation.  All rights reserved. 
 * 
 * @created May 1, 2006 
 * @author James Dixon
 */

package org.pentaho.platform.uifoundation.chart;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

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.IPentahoMetaData;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.api.engine.IPentahoUrlFactory;
import org.pentaho.platform.api.repository.ISolutionRepository;
import org.pentaho.platform.engine.core.solution.ActionInfo;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.services.actionsequence.ActionSequenceResource;
import org.pentaho.platform.uifoundation.messages.Messages;
import org.pentaho.platform.util.xml.XmlHelper;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;

public class FlashChartComponent extends AbstractChartComponent {

    private static final long serialVersionUID = 925147871232129020L;

    protected Node chartNode;

    protected Node chartTemplate;

    protected String backgroundColor;

    protected String chartType = "bar"; //$NON-NLS-1$

    private String valueItem = null;

    private String nameItem = null;

    private int topX = -1;

    public FlashChartComponent(final String definitionPath, final int width, final int height,
            final IPentahoUrlFactory urlFactory, final List messages) {
        super(urlFactory, messages);
        this.definitionPath = definitionPath;
        this.width = width;
        this.height = height;
        ActionInfo info = ActionInfo.parseActionString(definitionPath);
        if (info != null) {
            setSourcePath(info.getSolutionName() + File.separator + info.getPath());
        }
        AbstractChartComponent.logger = LogFactory.getLog(this.getClass());
    }

    @Override
    public Log getLogger() {
        return AbstractChartComponent.logger;
    }

    @Override
    public boolean validate() {
        return true;
    }

    @Override
    public Document getXmlContent() {

        return null;

    }

    public void setTopX(final int topX) {
        this.topX = topX;
    }

    @Override
    public boolean setDataAction(final String chartDefinition) {
        IActionSequenceResource resource = new ActionSequenceResource("", //$NON-NLS-1$
                IActionSequenceResource.SOLUTION_FILE_RESOURCE, "text/xml", //$NON-NLS-1$
                chartDefinition);
        try {
            Document dataActionDocument = PentahoSystem.get(ISolutionRepository.class, getSession())
                    .getResourceAsDocument(resource, ISolutionRepository.ACTION_EXECUTE);
            if (dataActionDocument == null) {
                return false;
            }
            Node dataNode = dataActionDocument.selectSingleNode("chart/data"); //$NON-NLS-1$
            chartNode = dataActionDocument.selectSingleNode("chart"); //$NON-NLS-1$
            title = XmlDom4JHelper.getNodeText("title", chartNode); //$NON-NLS-1$
            chartType = XmlDom4JHelper.getNodeText("chart_type", chartNode, "bar"); //$NON-NLS-1$ //$NON-NLS-2$
            boolean isGauge = chartType.toLowerCase().startsWith("dial"); //$NON-NLS-1$
            chartTemplate = chartNode.selectSingleNode(isGauge ? "template/gauge" : "template/chart"); //$NON-NLS-1$ //$NON-NLS-2$
            if (chartTemplate == null) {
                String templatePath = XmlDom4JHelper.getNodeText("template-file", chartNode); //$NON-NLS-1$
                if (templatePath != null) {
                    // we have a reference to a document to look up
                    IActionSequenceResource templateResource = new ActionSequenceResource("", //$NON-NLS-1$
                            IActionSequenceResource.SOLUTION_FILE_RESOURCE, "text/xml", //$NON-NLS-1$
                            templatePath);
                    try {
                        Document templateDocument = PentahoSystem.get(ISolutionRepository.class, getSession())
                                .getResourceAsDocument(templateResource, ISolutionRepository.ACTION_EXECUTE);
                        chartTemplate = templateDocument.getRootElement();
                    } catch (Exception e) {
                        // the chart template document is not valid
                        error(Messages.getInstance()
                                .getErrorString("FlashChartComponent.ERROR_0001_CHART_TEMPLATE_INVALID"), e); //$NON-NLS-1$
                    }
                }
            }
            solution = XmlDom4JHelper.getNodeText("data-solution", dataNode); //$NON-NLS-1$
            actionPath = XmlDom4JHelper.getNodeText("data-path", dataNode); //$NON-NLS-1$
            actionName = XmlDom4JHelper.getNodeText("data-action", dataNode); //$NON-NLS-1$
            actionOutput = XmlDom4JHelper.getNodeText("data-output", dataNode); //$NON-NLS-1$
            valueItem = XmlDom4JHelper.getNodeText("data-value", dataNode); //$NON-NLS-1$
            nameItem = XmlDom4JHelper.getNodeText("data-name", dataNode); //$NON-NLS-1$
            backgroundColor = XmlDom4JHelper.getNodeText("chart/background-color", dataActionDocument, "#ffffff"); //$NON-NLS-1$ //$NON-NLS-2$
            byRow = XmlDom4JHelper.getNodeText("data-orientation", dataNode, "rows").equals("rows"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            if (width == -1) {
                width = (int) XmlDom4JHelper.getNodeText("chart/width", dataActionDocument, 400); //$NON-NLS-1$
            }
            if (height == -1) {
                height = (int) XmlDom4JHelper.getNodeText("chart/height", dataActionDocument, 300); //$NON-NLS-1$
            }
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString(
                    "CategoryDatasetChartComponent.ERROR_0001_INVALID_CHART_DEFINITION", chartDefinition), e); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    @Override
    public String getContent(final String mimeType) {
        if (chartType.toLowerCase().startsWith("dial")) { //$NON-NLS-1$
            return getDialContent(mimeType);
        } else {
            return getChartContent(mimeType);
        }

    }

    protected Object[] getValue() {
        Object result[] = new Object[2];

        IPentahoResultSet resultSet = null;
        try {
            resultSet = getActionData();
            if (resultSet == null) {
                return null;
            }
            IPentahoMetaData metaData = resultSet.getMetaData();
            // TODO support multiple column headers / row headers
            // TODO support an iteration across columns for a given row

            // find the column that we have been told to you
            Object columnHeaders[][] = metaData.getColumnHeaders();
            int nameColumnNo = -1;
            int valueColumnNo = -1;
            for (int idx = 0; idx < columnHeaders[0].length; idx++) {
                if (columnHeaders[0][idx].toString().equalsIgnoreCase(nameItem)) {
                    nameColumnNo = idx;
                }
                if (columnHeaders[0][idx].toString().equalsIgnoreCase(valueItem)) {
                    valueColumnNo = idx;
                }
            }
            if (nameColumnNo == -1) {
                // we did not find the specified name column
                error(Messages.getInstance().getErrorString("FlashDial.ERROR_0001_NAME_COLUMN_MISSING", nameItem)); //$NON-NLS-1$
                return null;
            }

            if (valueColumnNo == -1) {
                // we did not find the specified name column
                error(Messages.getInstance().getErrorString("FlashDial.ERROR_0002_VALUE_COLUMN_MISSING", //$NON-NLS-1$
                        valueItem));
                return null;
            }

            Object row[] = resultSet.next();
            while (row != null) {
                result[0] = row[nameColumnNo].toString();
                try {
                    result[1] = new Double(row[valueColumnNo].toString());
                } catch (Exception e) {
                }
                row = resultSet.next();
            }
        } finally {
            if (resultSet != null) {
                resultSet.dispose();
            }
        }
        return result;
    }

    public String getDialContent(final String mimeType) {
        return getDialContent(mimeType, true);
    }

    public String getDialContent(final String mimeType, boolean objectWrapper) {
        Object data[] = null;

        Object tmpVal = getParameter("value", null); //$NON-NLS-1$
        if (tmpVal != null) {
            data = fixupDialData(getParameter("title", title), tmpVal); //$NON-NLS-1$
        } else {
            data = getValue();
            if ((data != null) && (data.length > 0)) {
                if (data.length == 1) {
                    tmpVal = data[0];
                    data = fixupDialData(getParameter("title", title), data[0]); //$NON-NLS-1$
                } else {
                    tmpVal = data[1];
                    data = fixupDialData(data[0], data[1]);
                }
            }

            if (data == null) {
                return Messages.getInstance().getErrorString("FlashChartComponent.ERROR_0002_DIAL_DATA_INVALID", //$NON-NLS-1$
                        String.valueOf(tmpVal));
            }
        }

        title = (String) data[0];
        double value = ((Double) data[1]).doubleValue();
        double span = 0;
        if (chartType.equalsIgnoreCase("DialPct")) { //$NON-NLS-1$
            double fullSpan = XmlDom4JHelper.getNodeText("dial_limits/@full-span", chartNode, 360.0); //$NON-NLS-1$
            double minClamp = XmlDom4JHelper.getNodeText("dial_limits/@min-clamp", chartNode, 0); //$NON-NLS-1$
            double maxClamp = XmlDom4JHelper.getNodeText("dial_limits/@max-clamp", chartNode, 360.0); //$NON-NLS-1$

            span = value / 100.0 * fullSpan;
            span = Math.max(minClamp, Math.min(maxClamp, span));
        } else {
            span = value;
        }

        //    String solutionDir = "system/tmp/"; //$NON-NLS-1$
        String fileNamePrefix = "tmp_flash_"; //$NON-NLS-1$
        String extension = ".xml"; //$NON-NLS-1$
        String fileName = null;
        String template = ""; //$NON-NLS-1$
        String gaugeLicense = PentahoSystem.getSystemSetting("FlashChart/GaugeLicense", ""); //$NON-NLS-1$ //$NON-NLS-2$
        if (gaugeLicense.length() > 0) {
            gaugeLicense = "license=" + gaugeLicense + "&"; //$NON-NLS-1$ //$NON-NLS-2$
        }
        if (chartTemplate != null) {
            template = chartTemplate.asXML().replace('"', '\'');
            template = template.replaceFirst("\\{title\\}", title); //$NON-NLS-1$
            template = template.replaceFirst("\\{span\\}", String.valueOf(span).replace('"', '\'')); //$NON-NLS-1$
            template = template.replaceFirst("\\{data\\}", String.valueOf(value).replace('"', '\'')); //$NON-NLS-1$

            if (urlTemplate != null) {
                template = template.replaceFirst("\\{drill-url\\}", urlTemplate); //$NON-NLS-1$
            }

            // Do tick marks
            List nodeList = chartNode.selectNodes("radial_ticks"); //$NON-NLS-1$
            for (int i = 0; i < nodeList.size(); ++i) {
                template = setRadialTicks((Node) nodeList.get(i), template);
            }

            nodeList = chartNode.selectNodes("radial_numbers"); //$NON-NLS-1$
            for (int i = 0; i < nodeList.size(); ++i) {
                template = setRadialNumbers((Node) nodeList.get(i), template);
            }

        } else {
            StringBuffer sb = new StringBuffer();
            sb.append("<gauge>"); //$NON-NLS-1$
            sb.append("  <rotate x='").append(width / 2).append("' y='").append(height / 2).append("' span='") //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                    .append(String.valueOf(span)).append("'>\n"); //$NON-NLS-1$
            sb.append("    <line x1='").append(width / 2).append("' y1='0' x2='").append(width / 2).append("' y2='") //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                    .append(height / 2).append("' />\n"); //$NON-NLS-1$
            sb.append("  </rotate>\n"); //$NON-NLS-1$
            sb.append("</gauge>"); //$NON-NLS-1$
            template = sb.toString();
        }

        if (!objectWrapper) {
            return template;
        }

        // create a temporary file
        try {
            File file = PentahoSystem.getApplicationContext().createTempFile(getSession(), fileNamePrefix,
                    extension, true);
            fileName = file.getName();
            if (file.canWrite()) {
                Writer out = new FileWriter(file);
                out.write(template);
                out.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        String baseUrl = PentahoSystem.getApplicationContext().getBaseUrl();

        StringBuffer sb = new StringBuffer();
        sb.append("<OBJECT classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n") //$NON-NLS-1$
                .append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ") //$NON-NLS-1$
                .append("WIDTH=\"").append(width).append("\" HEIGHT=\"").append(height) //$NON-NLS-1$//$NON-NLS-2$
                .append("\" id=\"charts\" ALIGN=\"\">") //$NON-NLS-1$
                .append("<PARAM NAME=\"movie\" VALUE=\"").append(baseUrl).append("chart/gauge.swf?") //$NON-NLS-1$//$NON-NLS-2$
                .append(gaugeLicense).append("xml_source=").append(baseUrl).append("getImage?image=") //$NON-NLS-1$//$NON-NLS-2$
                .append(fileName).append("\">") //$NON-NLS-1$
                .append("<PARAM NAME=\"quality\" VALUE=\"high\"> ") //$NON-NLS-1$
                .append("<PARAM NAME=\"bgcolor\" VALUE=\"").append(backgroundColor).append("\"> ") //$NON-NLS-1$ //$NON-NLS-2$

                .append("<EMBED src=\"").append(baseUrl).append("chart/gauge.swf?").append(gaugeLicense) //$NON-NLS-1$//$NON-NLS-2$
                .append("xml_source=").append(baseUrl).append("getImage?image=").append(fileName).append("\" ") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 
                .append("quality=\"high\" bgcolor=\"").append(backgroundColor).append("\" WIDTH=\"").append(width) //$NON-NLS-1$//$NON-NLS-2$
                .append("\" HEIGHT=\"").append(height).append("\" NAME=\"charts\" ALIGN=\"\" ") //$NON-NLS-1$ //$NON-NLS-2$
                .append("TYPE=\"application/x-shockwave-flash\" PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\"></EMBED>") //$NON-NLS-1$
                .append(" </OBJECT>"); //$NON-NLS-1$

        return sb.toString();
    }

    private Object[] fixupDialData(Object inTitle, final Object value) {
        Object[] data = null;
        if (value == null) {
            return data;
        }

        if (inTitle == null) {
            inTitle = ""; //$NON-NLS-1$
        }
        inTitle = inTitle.toString();

        try {
            if (value instanceof Double) {
                data = new Object[] { inTitle, value };
            } else {
                data = new Object[] { inTitle, new Double(value.toString()) };
            }
        } catch (Throwable t) {
            error(Messages.getInstance().getErrorString("FlashChartComponent.ERROR_0002_DIAL_DATA_INVALID", //$NON-NLS-1$
                    value.toString()));
        }
        return (data);
    }

    //function that generates the XML code to draw radial ticks
    private String setRadialTicks(final Node node, final String template) {
        try {
            String xml = setRadialTicks(XmlDom4JHelper.getNodeText("@x", node, (width / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@y", node, (height / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@radius", node, (height / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@length", node, 8.0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@start_angle", node, 0.0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@end_angle", node, 350.0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@tick_count", node, 36), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@thickness", node, 2), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@color", node, "000000") //$NON-NLS-1$ //$NON-NLS-2$
            );
            String param_name = XmlDom4JHelper.getNodeText("@param_name", node, null); //$NON-NLS-1$
            return (template.replaceAll("\\{" + param_name + "\\}", xml)); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (Throwable t) {
            error(Messages.getInstance().getString("FlashChartComponent.ERROR_0003_INVALID_XML_ATTRIBUTES_FOR_DIAL", //$NON-NLS-1$
                    "radial_ticks", node.asXML())); //$NON-NLS-1$
        }
        return (template);
    }

    private String setRadialTicks(final double x_center, final double y_center, final double radius,
            final double length, final double start_angle, final double end_angle, final long ticks_count,
            final long thickness, final String color) {
        StringBuffer sb = new StringBuffer();
        for (double i = start_angle; i <= end_angle; i += (end_angle - start_angle) / (ticks_count - 1)) {
            sb.append("<line x1='").append(x_center + Math.sin(Math.toRadians(i)) * radius).append("' y1='") //$NON-NLS-1$//$NON-NLS-2$
                    .append(y_center - Math.cos(Math.toRadians(i)) * radius).append("'"); //$NON-NLS-1$
            sb.append(" x2='").append(x_center + Math.sin(Math.toRadians(i)) * (radius + length)).append("' y2='") //$NON-NLS-1$//$NON-NLS-2$
                    .append(y_center - Math.cos(Math.toRadians(i)) * (radius + length)).append("'"); //$NON-NLS-1$
            sb.append(" thickness='").append(thickness).append("' color='").append(color).append("' />\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
        return (sb.toString());
    }

    //function that generates the XML code to draw radial ticks
    private String setRadialNumbers(final Node node, final String template) {
        try {
            String xml = setRadialNumbers(XmlDom4JHelper.getNodeText("@x", node, (width / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@y", node, (height / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@radius", node, (height / 2.0)), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@start_angle", node, 0.0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@end_angle", node, 350.0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@start_number", node, 0), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@end_number", node, 340), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@tick_count", node, 18), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@font_size", node, 12), //$NON-NLS-1$
                    XmlDom4JHelper.getNodeText("@color", node, "000000") //$NON-NLS-1$ //$NON-NLS-2$
            );
            String param_name = XmlDom4JHelper.getNodeText("@param_name", node, null); //$NON-NLS-1$
            return (template.replaceAll("\\{" + param_name + "\\}", xml)); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (Throwable t) {
            error(Messages.getInstance().getString("FlashChartComponent.ERROR_0003_INVALID_XML_ATTRIBUTES_FOR_DIAL", //$NON-NLS-1$
                    "radial_numbers", node.asXML())); //$NON-NLS-1$
        }
        return (template);
    }

    //function that generates the XML code to draw radial numbers
    private String setRadialNumbers(final double x_center, final double y_center, final double radius,
            final double start_angle, final double end_angle, final long start_number, final long end_number,
            final long ticks_count, final long font_size, final String color) {
        StringBuffer sb = new StringBuffer();
        long number = start_number;

        for (double i = start_angle; i <= end_angle; i += (end_angle - start_angle) / (ticks_count - 1)) {
            sb.append("<text x='").append(x_center + Math.sin(Math.toRadians(i)) * radius).append("' y='") //$NON-NLS-1$//$NON-NLS-2$
                    .append(y_center - Math.cos(Math.toRadians(i)) * radius).append("'"); //$NON-NLS-1$
            sb.append(" width='200' size='").append(font_size).append("' color='").append(color) //$NON-NLS-1$//$NON-NLS-2$
                    .append("' align='left' rotation='").append(i).append("'>"); //$NON-NLS-1$ //$NON-NLS-2$
            sb.append(number).append("</text>\n"); //$NON-NLS-1$
            number += (end_number - start_number) / (ticks_count - 1);
        }
        return (sb.toString());
    }

    public String getChartContent(final String mimeType) {
        return getChartContent(mimeType, true);
    }

    public String getChartContent(final String mimeType, boolean objectWrapper) {

        IPentahoResultSet data = getActionData();

        if ((data == null) || (data.getRowCount() == 0)) {
            // there is no data to display, prevent the default chart from showing
            // TODO surface any error to the UI
            return Messages.getInstance().getString("FlashChartComponent.USER_NO_DATA"); //$NON-NLS-1$
        }

        ArrayList nameList = new ArrayList();
        ArrayList series1 = new ArrayList();
        ArrayList series2 = new ArrayList();
        ArrayList series3 = new ArrayList();
        ArrayList series4 = new ArrayList();
        StringBuffer dataXml = new StringBuffer();
        Object row[];
        if (data != null) {
            row = data.next();
            int n = 0;
            while (row != null) {
                if (row[0] == null) {
                    nameList.add("null"); //$NON-NLS-1$
                } else {
                    nameList.add(row[0].toString());
                }
                if (row.length > 1) {
                    if (row[1] == null) {
                        series1.add("<null/>"); //$NON-NLS-1$
                    } else {
                        series1.add(row[1].toString());
                    }
                }
                if (row.length > 2) {
                    if (row[2] == null) {
                        series2.add("<null/>"); //$NON-NLS-1$
                    } else {
                        series2.add(row[2].toString());
                    }
                }
                if (row.length > 3) {
                    if (row[3] == null) {
                        series3.add("<null/>"); //$NON-NLS-1$
                    } else {
                        series3.add(row[3].toString());
                    }
                }
                if (row.length > 4) {
                    if (row[4] == null) {
                        series4.add("<null/>"); //$NON-NLS-1$
                    } else {
                        series4.add(row[4].toString());
                    }
                }
                row = data.next();
                n++;
                if (n == topX) {
                    row = null;
                }
            }
            dataXml.append("<row>"); //$NON-NLS-1$
            dataXml.append("<null/>"); //$NON-NLS-1$
            int start = 0;
            int end = nameList.size();
            int step = 1;
            if (topX != -1) {
                start = end - 1;
                end = -1;
                step = -1;
            }
            if (data != null) {
                for (int i = start; i != end; i += step) {
                    dataXml.append("<string>"); //$NON-NLS-1$
                    dataXml.append(XmlHelper.encode((String) nameList.get(i)));
                    dataXml.append("</string>"); //$NON-NLS-1$
                }
            }
            dataXml.append("</row>"); //$NON-NLS-1$
            if (series1.size() > 0) {
                dataXml.append("<row>"); //$NON-NLS-1$
                dataXml.append("<string>"); //$NON-NLS-1$
                dataXml.append(XmlHelper.encode((String) data.getMetaData().getColumnHeaders()[0][1]));
                dataXml.append("</string>"); //$NON-NLS-1$
                for (int i = start; i != end; i += step) {
                    dataXml.append("<number>"); //$NON-NLS-1$
                    dataXml.append((String) series1.get(i));
                    dataXml.append("</number>"); //$NON-NLS-1$
                }
                dataXml.append("</row>"); //$NON-NLS-1$
            }
            if (series2.size() > 0) {
                dataXml.append("<row>"); //$NON-NLS-1$
                dataXml.append("<string>"); //$NON-NLS-1$
                dataXml.append(XmlHelper.encode((String) data.getMetaData().getColumnHeaders()[0][2]));
                dataXml.append("</string>"); //$NON-NLS-1$
                for (int i = start; i != end; i += step) {
                    dataXml.append("<number>"); //$NON-NLS-1$
                    dataXml.append((String) series2.get(i));
                    dataXml.append("</number>"); //$NON-NLS-1$
                }
                dataXml.append("</row>"); //$NON-NLS-1$
            }
            if (series3.size() > 0) {
                dataXml.append("<row>"); //$NON-NLS-1$
                dataXml.append("<string>"); //$NON-NLS-1$
                dataXml.append(XmlHelper.encode((String) data.getMetaData().getColumnHeaders()[0][3]));
                dataXml.append("</string>"); //$NON-NLS-1$
                for (int i = start; i != end; i += step) {
                    dataXml.append("<number>"); //$NON-NLS-1$
                    dataXml.append((String) series3.get(i));
                    dataXml.append("</number>"); //$NON-NLS-1$
                }
                dataXml.append("</row>"); //$NON-NLS-1$
            }
            if (series4.size() > 0) {
                dataXml.append("<row>"); //$NON-NLS-1$
                dataXml.append("<string>"); //$NON-NLS-1$
                dataXml.append(XmlHelper.encode((String) data.getMetaData().getColumnHeaders()[0][4]));
                dataXml.append("</string>"); //$NON-NLS-1$
                for (int i = start; i != end; i += step) {
                    dataXml.append("<number>"); //$NON-NLS-1$
                    dataXml.append((String) series4.get(i));
                    dataXml.append("</number>"); //$NON-NLS-1$
                }
                dataXml.append("</row>"); //$NON-NLS-1$
            }
        }
        String baseUrl = PentahoSystem.getApplicationContext().getBaseUrl();

        //    String solutionDir = "system/tmp/"; //$NON-NLS-1$
        String fileNamePrefix = "tmp_flash_"; //$NON-NLS-1$
        String extension = ".xml"; //$NON-NLS-1$
        String fileName = null;
        String template = ""; //$NON-NLS-1$
        String chartLicense = PentahoSystem.getSystemSetting("FlashChart/ChartLicense", ""); //$NON-NLS-1$ //$NON-NLS-2$
        if (chartLicense.length() > 0) {
            chartLicense = "license=" + chartLicense + "&"; //$NON-NLS-1$ //$NON-NLS-2$ 
        }
        if (chartTemplate != null) {
            template = chartTemplate.asXML().replace('"', '\'');
            if (title != null) {
                template = template.replaceFirst("\\{title\\}", title); //$NON-NLS-1$
            } else {
                template = template.replaceFirst("\\{title\\}", ""); //$NON-NLS-1$ //$NON-NLS-2$
            }
            template = template.replaceFirst("\\{data\\}", dataXml.toString().replace('"', '\'')); //$NON-NLS-1$
            if (urlTemplate != null) {
                template = template.replaceFirst("\\{drill-url\\}", urlTemplate); //$NON-NLS-1$
            }
        } else {
            StringBuffer sb = new StringBuffer();
            sb.append("<chart>"); //$NON-NLS-1$
            sb.append("<chart_data>"); //$NON-NLS-1$
            sb.append(dataXml.toString().replace('"', '\''));
            sb.append("</chart_data>"); //$NON-NLS-1$
            sb.append("<chart_grid_h alpha='20' color='000000' thickness='1' type='solid' />"); //$NON-NLS-1$
            sb.append(
                    "<chart_rect positive_color='ffffff' positive_alpha='20' negative_color='ff0000' negative_alpha='10' />"); //$NON-NLS-1$
            sb.append("<chart_type>"); //$NON-NLS-1$
            sb.append(chartType);
            sb.append("</chart_type>"); //$NON-NLS-1$
            sb.append(
                    "<chart_value color='ffffff' alpha='90' font='arial' bold='true' size='12' position='inside' prefix='' suffix='' decimals='0' separator='' as_percentage='true' />"); //$NON-NLS-1$

            sb.append(
                    "<legend_label layout='horizontal' bullet='circle' font='arial' bold='true' size='13' color='ffffff' alpha='85' />"); //$NON-NLS-1$
            sb.append(
                    "<legend_rect fill_color='aaaaaa' fill_alpha='100' line_color='000000' line_alpha='0' line_thickness='0' />"); //$NON-NLS-1$

            sb.append("</chart>"); //$NON-NLS-1$
            template = sb.toString();
        }

        if (!objectWrapper) {
            return template;
        }

        /*
         <OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
         codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" 
         WIDTH="400" 
         HEIGHT="250" 
         id="gauge" 
         ALIGN="">                                                      
         <PARAM NAME=movie VALUE="gauge.swf?xml_source=sample.xml&license=XXXXXXXXXXXXXXXXXX.XXXXXXXXXXX">
         <PARAM NAME=quality VALUE=high>
         <PARAM NAME=bgcolor VALUE=#888888>
            
         <EMBED src="gauge.swf?xml_source=sample.xml&license=XXXXXXXXXXXXXXXXXX.XXXXXXXXXXX" 
         quality=high 
         bgcolor=#888888  
         WIDTH="400" 
         HEIGHT="250" 
         NAME="gauge" 
         ALIGN="" 
         TYPE="application/x-shockwave-flash" 
         PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
         </EMBED>
         </OBJECT>
         */
        if (template.length() < 0) {
            // this is a small chart file, lets assume all the browsers are ok with this size
            // TODO verify the maximum size of parameters for the major browsers
            StringBuffer sb = new StringBuffer();
            sb.append("<OBJECT classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n") //$NON-NLS-1$
                    .append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ") //$NON-NLS-1$
                    .append("WIDTH=\"").append(width).append("\" HEIGHT=\"").append(height) //$NON-NLS-1$//$NON-NLS-2$
                    .append("\" id=\"charts\" ALIGN=\"\">") //$NON-NLS-1$
                    .append("<PARAM NAME=\"movie\" VALUE=\"").append(baseUrl).append("chart/charts.swf?") //$NON-NLS-1$//$NON-NLS-2$
                    .append(chartLicense).append("library_path=").append(baseUrl).append("chart/charts_library\">") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("<PARAM NAME=\"quality\" VALUE=\"high\"> ") //$NON-NLS-1$
                    .append("<PARAM NAME=\"bgcolor\" VALUE=\"").append(backgroundColor).append("\"> ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("<PARAM NAME=\"FlashVars\" VALUE=\"source_data=").append(template).append("\">") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("<EMBED src=\"").append(baseUrl).append("chart/charts.swf?").append(chartLicense) //$NON-NLS-1$//$NON-NLS-2$
                    .append("library_path=").append(baseUrl).append("chart/charts_library\" ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("FlashVars=\"source_data=").append(template).append("\" ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("quality=\"high\" bgcolor=\"").append(backgroundColor).append("\" WIDTH=\"") //$NON-NLS-1$//$NON-NLS-2$
                    .append(width).append("\" HEIGHT=\"").append(height).append("\" NAME=\"charts\" ALIGN=\"\" ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("TYPE=\"application/x-shockwave-flash\" PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\"></EMBED>") //$NON-NLS-1$
                    .append("  </OBJECT>"); //$NON-NLS-1$
            return sb.toString();
        } else {
            // create a temporary file
            try {
                File file = PentahoSystem.getApplicationContext().createTempFile(getSession(), fileNamePrefix,
                        extension, true);
                fileName = file.getName();
                if (file.canWrite()) {
                    Writer out = new FileWriter(file);
                    out.write(template);
                    out.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            StringBuffer sb = new StringBuffer();
            sb.append("<OBJECT classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n") //$NON-NLS-1$
                    .append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ") //$NON-NLS-1$
                    .append("WIDTH=\"").append(width).append("\" HEIGHT=\"").append(height) //$NON-NLS-1$//$NON-NLS-2$
                    .append("\" id=\"charts\" ALIGN=\"\">") //$NON-NLS-1$
                    .append("<PARAM NAME=\"movie\" VALUE=\"").append(baseUrl).append("chart/charts.swf?") //$NON-NLS-1$//$NON-NLS-2$
                    .append(chartLicense).append("library_path=").append(baseUrl) //$NON-NLS-1$
                    .append("chart/charts_library&xml_source=").append(baseUrl).append("getImage?image=") //$NON-NLS-1$//$NON-NLS-2$
                    .append(fileName).append("\">") //$NON-NLS-1$
                    .append("<PARAM NAME=\"quality\" VALUE=\"high\"> ") //$NON-NLS-1$
                    .append("<PARAM NAME=\"bgcolor\" VALUE=\"").append(backgroundColor).append("\"> ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("<EMBED src=\"").append(baseUrl).append("chart/charts.swf?").append(chartLicense) //$NON-NLS-1$//$NON-NLS-2$
                    .append("library_path=").append(baseUrl).append("chart/charts_library&xml_source=") //$NON-NLS-1$//$NON-NLS-2$
                    .append(baseUrl).append("getImage?image=").append(fileName).append("\" ") //$NON-NLS-1$ //$NON-NLS-2$ 
                    .append("quality=\"high\" bgcolor=\"").append(backgroundColor).append("\" WIDTH=\"") //$NON-NLS-1$//$NON-NLS-2$
                    .append(width).append("\" HEIGHT=\"").append(height).append("\" NAME=\"charts\" ALIGN=\"\" ") //$NON-NLS-1$ //$NON-NLS-2$
                    .append("TYPE=\"application/x-shockwave-flash\" PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\"></EMBED>") //$NON-NLS-1$
                    .append("  </OBJECT>"); //$NON-NLS-1$
            return sb.toString();
        }

    }

    @Override
    public void dispose() {

    }

}