com.tonbeller.jpivot.chart.ChartComponent.java Source code

Java tutorial

Introduction

Here is the source code for com.tonbeller.jpivot.chart.ChartComponent.java

Source

/*
 * ====================================================================
 * This software is subject to the terms of the Common Public License
 * Agreement, available at the following URL:
 *   http://www.opensource.org/licenses/cpl.html .
 * Copyright (C) 2003-2004 TONBELLER AG.
 * All Rights Reserved.
 * You must accept the terms of that agreement to use this software.
 * ====================================================================
 */
package com.tonbeller.jpivot.chart;

import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;

import org.apache.log4j.Logger;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryLabelPosition;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.CategoryLabelWidthType;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.chart.title.Title;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.chart.urls.StandardCategoryURLGenerator;
import org.jfree.chart.urls.StandardPieURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.text.TextBlockAnchor;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.TextAnchor;
import org.jfree.ui.VerticalAlignment;
import org.jfree.util.TableOrder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.tonbeller.jpivot.core.ModelChangeEvent;
import com.tonbeller.jpivot.core.ModelChangeListener;
import com.tonbeller.jpivot.olap.model.Cell;
import com.tonbeller.jpivot.olap.model.Member;
import com.tonbeller.jpivot.olap.model.OlapModel;
import com.tonbeller.jpivot.olap.model.Position;
import com.tonbeller.jpivot.olap.model.Result;
import com.tonbeller.jpivot.olap.navi.DrillThrough;
import com.tonbeller.jpivot.olap.navi.MemberTree;
import com.tonbeller.wcf.component.Component;
import com.tonbeller.wcf.component.ComponentSupport;
import com.tonbeller.wcf.controller.Dispatcher;
import com.tonbeller.wcf.controller.DispatcherSupport;
import com.tonbeller.wcf.controller.RequestContext;
import com.tonbeller.wcf.controller.RequestListener;
import com.tonbeller.wcf.table.EditableTableComponent;
import com.tonbeller.wcf.table.ITableComponent;
import com.tonbeller.wcf.table.TableColumn;
import com.tonbeller.wcf.table.TableModel;
import com.tonbeller.wcf.utils.DomUtils;
import com.tonbeller.wcf.utils.XmlUtils;

/**
 * Contains a reference to an OlapModel.
 * Produces a chart image.
 *
 */
public class ChartComponent extends ComponentSupport implements ModelChangeListener {
    private static Logger logger = Logger.getLogger(ChartComponent.class);

    String ref;
    Document document;
    OlapModel olapModel;
    boolean dirty = true;
    Locale locale;

    Result result;
    Iterator cellIterator;
    int dimCount;
    Element rootElement;
    int colCount;

    // servlet mapping - optionally can be set as a servlet context parameter
    // with name "chartServlet"
    // for example:
    /*
    <context-param>
      <param-name>chartServlet</param-name>
      <param-value>/path/to/chartServlet</param-value>
    </context-param>
    */
    String CHART_SERVLET = "/DisplayChart";
    final String CHART_SERVLET_KEY = "chartServlet";
    boolean baseDisplayURLSet = false;
    String webControllerURL = "";

    String filename = null;
    // chart properties
    final int DEFAULT_CHART_WIDTH = 500;
    final int DEFAULT_CHART_HEIGHT = 300;
    String chartTitle = "";
    java.awt.Font titleFont = JFreeChart.DEFAULT_TITLE_FONT;
    String fontName = "SansSerif";
    int fontStyle = java.awt.Font.BOLD;
    int fontSize = 18;
    String slicerFontName = "SansSerif";
    int slicerFontStyle = java.awt.Font.PLAIN;
    int slicerFontSize = 12;
    String axisFontName = "SansSerif";
    int axisFontStyle = java.awt.Font.PLAIN;
    int axisFontSize = 12;
    String axisTickFontName = "SansSerif";
    int axisTickFontStyle = java.awt.Font.PLAIN;
    int axisTickFontSize = 12;
    String legendFontName = "SansSerif";
    int legendFontStyle = java.awt.Font.PLAIN;
    int legendFontSize = 10;
    int legendPosition = 3; //RectangleEdge.BOTTOM;
    int slicerPosition = 1; // BOTTOM - see chartpropertiesform.xsl
    int slicerAlignment = 3; // LEFT
    //java.awt.Color bgColor = java.awt.Color.white;
    int bgColorR = 255;
    int bgColorG = 255;
    int bgColorB = 255;
    int chartType = 1;
    int chartHeight = DEFAULT_CHART_HEIGHT;
    int chartWidth = DEFAULT_CHART_WIDTH;
    String horizAxisLabel = "";
    String vertAxisLabel = "";
    boolean showLegend = true;
    boolean showSlicer = true;
    boolean showTooltips = true;
    boolean drillThroughEnabled = false;
    int tickLabelRotate = 30; //default 30 degree rotation

    ChartRenderingInfo info = null;

    Dispatcher dispatcher = new DispatcherSupport();

    /**
     * Constructor
     * @param id the id of this component
     * @param parent parent component
     * @param ref a reference to an olap model
     * @param baseDisplayURL URL for chart servlet. Overrides default
     * @param controllerURL URL for web application where JPivot/WCF is running. Overrides default
     * @param context RequestContext
     */
    public ChartComponent(String id, Component parent, String ref, String baseDisplayURL, String controllerURL,
            RequestContext context) {
        super(id, parent);
        this.ref = ref;

        this.olapModel = (OlapModel) context.getModelReference(ref);
        this.olapModel.addModelChangeListener(this);
        this.locale = context.getLocale();
        // extend the controller
        getDispatcher().addRequestListener(null, null, dispatcher);
        // optional servlet context parameter for chart servlet location
        String chartServlet = baseDisplayURL;
        if (chartServlet == null) {
            chartServlet = context.getServletContext().getInitParameter(CHART_SERVLET_KEY);
        } else {
            baseDisplayURLSet = true;
        }
        if (chartServlet != null) {
            this.CHART_SERVLET = chartServlet;
        }
        if (controllerURL != null) {
            this.webControllerURL = controllerURL;
        }
    }

    public ChartComponent(String id, Component parent, String ref, RequestContext context) {
        this(id, parent, ref, null, null, context);
    }

    /**
     * called once by the creating tag
     */
    public void initialize(RequestContext context) throws Exception {
        super.initialize(context);
    }

    /**
     * Entry point for producing charts, called by wcf render tag.
     * Produces a jfreechart dataset from olap model, then creates a chart and
     * writes it to the servlet container temp directory.
     * Returns a DOM document for Renderer to transform into html.
     * Requires that jfreechart servlet is installed in this application context.
     */
    public Document render(RequestContext context) throws Exception {
        // check if we need to produce a new chart
        if (dirty) {
            // clear old listeners
            dispatcher.clear();
            this.result = olapModel.getResult();
            this.cellIterator = result.getCells().iterator();
            this.dimCount = result.getAxes().length;
            DefaultCategoryDataset dataset = null;
            switch (dimCount) {
            case 1:
                logger.info("1-dim data");
                dataset = build1dimDataset();
                break;
            case 2:
                logger.info("2-dim data");
                dataset = build2dimDataset();
                break;
            default:
                logger.error("less than 1 or more than 2 dimensions");
                throw new IllegalArgumentException("ChartRenderer requires a 1 or 2 dimensional result");
            }
            // re-set dirty flag
            dirty = false;

            // create font
            titleFont = new java.awt.Font(fontName, fontStyle, fontSize);

            CategoryURLGenerator urlGenerator = new jpivotCategoryURLGenerator(webControllerURL);

            // Create the chart object
            JFreeChart chart = null;
            switch (chartType) {
            case 1:
                chart = ChartFactory.createBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 2:
                chart = ChartFactory.createBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 3:
                chart = ChartFactory.createBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 4:
                chart = ChartFactory.createBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 5:
                chart = ChartFactory.createStackedBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;
            case 6:
                chart = ChartFactory.createStackedBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;
            case 7:
                chart = ChartFactory.createStackedBarChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;
            case 8:
                chart = ChartFactory.createStackedBarChart3D(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;
            case 9:
                chart = ChartFactory.createLineChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 10:
                chart = ChartFactory.createLineChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 11:
                chart = ChartFactory.createAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;
            case 12:
                chart = ChartFactory.createAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel, dataset,
                        PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled, urlGenerator);
                break;

            case 13:
                chart = ChartFactory.createStackedAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.VERTICAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;
            case 14:
                chart = ChartFactory.createStackedAreaChart(chartTitle, titleFont, horizAxisLabel, vertAxisLabel,
                        dataset, PlotOrientation.HORIZONTAL, showLegend, showTooltips, drillThroughEnabled,
                        urlGenerator);
                break;

            case 15:
                chart = ChartFactory.createPieChart(chartTitle, titleFont, dataset, TableOrder.BY_COLUMN,
                        showLegend, showTooltips, drillThroughEnabled,
                        new jpivotPieURLGenerator(TableOrder.BY_COLUMN, dataset, webControllerURL));
                break;
            case 16:
                chart = ChartFactory.createPieChart(chartTitle, titleFont, dataset, TableOrder.BY_ROW, showLegend,
                        showTooltips, drillThroughEnabled,
                        new jpivotPieURLGenerator(TableOrder.BY_ROW, dataset, webControllerURL));
                break;
            case 17:
                chart = ChartFactory.create3DPieChart(chartTitle, titleFont, dataset, TableOrder.BY_COLUMN,
                        showLegend, showTooltips, drillThroughEnabled,
                        new jpivotPieURLGenerator(TableOrder.BY_COLUMN, dataset, webControllerURL));

                break;
            case 18:
                chart = ChartFactory.create3DPieChart(chartTitle, titleFont, dataset, TableOrder.BY_ROW, showLegend,
                        showTooltips, drillThroughEnabled,
                        new jpivotPieURLGenerator(TableOrder.BY_ROW, dataset, webControllerURL));

                break;

            default:
                throw new Exception("An unknown Chart Type was requested");
            }
            try {
                chart.setBackgroundPaint(new java.awt.Color(bgColorR, bgColorG, bgColorB));

                java.awt.Font slicerFont = new java.awt.Font(slicerFontName, slicerFontStyle, slicerFontSize);
                java.awt.Font axisFont = new java.awt.Font(axisFontName, axisFontStyle, axisFontSize);
                java.awt.Font axisTickFont = new java.awt.Font(axisTickFontName, axisTickFontStyle,
                        axisTickFontSize);
                java.awt.Font legendFont = new java.awt.Font(legendFontName, legendFontStyle, legendFontSize);
                Plot plot = chart.getPlot();

                if (plot instanceof CategoryPlot) {
                    CategoryPlot catPlot = (CategoryPlot) plot;
                    catPlot.getDomainAxis().setLabelFont(axisFont);
                    catPlot.getRangeAxis().setLabelFont(axisFont);
                    catPlot.getDomainAxis().setTickLabelFont(axisTickFont);
                    catPlot.getRangeAxis().setTickLabelFont(axisTickFont);
                    catPlot.getDomainAxis().setMaximumCategoryLabelWidthRatio(100.0f);
                    double angle = -2.0 * Math.PI / 360.0 * (double) tickLabelRotate;
                    CategoryLabelPositions oldp = catPlot.getDomainAxis().getCategoryLabelPositions();
                    CategoryLabelPositions newp = new CategoryLabelPositions(
                            oldp.getLabelPosition(RectangleEdge.TOP),
                            new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT,
                                    TextAnchor.TOP_RIGHT, angle, CategoryLabelWidthType.RANGE, 0.0f),
                            oldp.getLabelPosition(RectangleEdge.LEFT), oldp.getLabelPosition(RectangleEdge.RIGHT));
                    catPlot.getDomainAxis().setCategoryLabelPositions(newp);
                } else if (plot instanceof PiePlot3D) {
                    PiePlot3D piePlot = (PiePlot3D) plot;
                    //piePlot.setSectionLabelFont(axisFont);
                    piePlot.setLabelFont(axisFont);
                    //piePlot.setSeriesLabelFont(axisTickFont);
                    //piePlot.setSectionLabelType(piePlot.NO_LABELS);
                    piePlot.setDirection(org.jfree.util.Rotation.CLOCKWISE);
                    piePlot.setForegroundAlpha(0.5f);
                    piePlot.setNoDataMessage("No data to display");
                } else if (plot instanceof PiePlot) {
                    PiePlot piePlot = (PiePlot) plot;
                    //piePlot.setSectionLabelFont(axisFont);
                    //piePlot.setSeriesLabelFont(axisTickFont);
                    piePlot.setLabelFont(axisFont);
                    //piePlot.setSectionLabelType(piePlot.NO_LABELS);

                }
                LegendTitle legend = (LegendTitle) chart.getLegend();
                if (legend != null) {
                    legend.setItemFont(legendFont);
                    /*
                         RectangleAnchor legendRectAnchor=RectangleAnchor.BOTTOM;
                        
                    switch (legendPosition){
                         case 0:
                            legendRectAnchor = RectangleAnchor.LEFT;
                            break;
                        case 1:
                           legendRectAnchor = RectangleAnchor.TOP;
                            break;
                        case 2:
                           legendRectAnchor = RectangleAnchor.RIGHT;
                            break;
                        case 3:
                           legendRectAnchor = RectangleAnchor.BOTTOM;
                            break;
                    }
                    legend.setLegendItemGraphicAnchor(legendRectAnchor);
                    */
                    RectangleEdge legendRectEdge = RectangleEdge.BOTTOM;
                    switch (legendPosition) {
                    case 0:
                        legendRectEdge = RectangleEdge.LEFT;
                        break;
                    case 1:
                        legendRectEdge = RectangleEdge.TOP;
                        break;
                    case 2:
                        legendRectEdge = RectangleEdge.RIGHT;
                        break;
                    case 3:
                        legendRectEdge = RectangleEdge.BOTTOM;
                        break;
                    }
                    legend.setPosition(legendRectEdge);
                }
                if (showSlicer) {
                    RectangleEdge slicerRectPos = RectangleEdge.BOTTOM;
                    HorizontalAlignment slicerHorizAlignment = HorizontalAlignment.LEFT;

                    switch (slicerPosition) {
                    case 0:
                        slicerRectPos = RectangleEdge.TOP;
                        break;
                    case 1:
                        slicerRectPos = RectangleEdge.BOTTOM;
                        break;
                    case 2:
                        slicerRectPos = RectangleEdge.RIGHT;
                        break;
                    case 3:
                        slicerRectPos = RectangleEdge.LEFT;
                        break;
                    }

                    switch (slicerAlignment) {
                    case 4:
                        slicerHorizAlignment = HorizontalAlignment.CENTER;
                        break;
                    case 3:
                        slicerHorizAlignment = HorizontalAlignment.LEFT;
                        break;
                    case 2:
                        slicerHorizAlignment = HorizontalAlignment.RIGHT;
                        break;
                    }
                    TextTitle slicer = new TextTitle(buildSlicer(), slicerFont, Color.BLACK, slicerRectPos,
                            slicerHorizAlignment, VerticalAlignment.CENTER, new RectangleInsets(0, 0, 0, 0));

                    slicer.setPosition(slicerRectPos);
                    chart.addSubtitle(slicer);
                }
                info = new ChartRenderingInfo(new StandardEntityCollection());
                //  Write the chart image to the temporary directory
                HttpSession session = context.getSession();
                filename = ServletUtilities.saveChartAsPNG(chart, chartWidth, chartHeight, info, session);

            } catch (Exception e) {
                filename = "public_error_500x300.png";
                dirty = true;
            }
        }
        // new DOM document
        DocumentBuilder parser = XmlUtils.getParser();
        // get an image map for the chart, wrap it in xchart tags
        String xchart = "<xchart>" + writeImageMap(filename, info, false) + "</xchart>";
        /*
              if (logger.isDebugEnabled()) {
                 logger.debug("Chart XML");
                 logger.debug(xchart);
              }
        */
        // create an InputStream from the DOM document
        InputStream stream = new ByteArrayInputStream(xchart.getBytes("UTF-8"));

        document = parser.parse(stream);
        Element root = document.getDocumentElement();
        // create url for img tag
        String graphURL = getGraphURL(context);

        Element img = document.createElement("img");
        img.setAttribute("src", graphURL);
        img.setAttribute("width", new Integer(chartWidth).toString());
        img.setAttribute("height", new Integer(chartHeight).toString());
        img.setAttribute("style", "border:0;");
        img.setAttribute("usemap", "#" + filename);
        root.appendChild(img);

        return document;
    }

    public String getGraphURL(RequestContext context) {
        String graphURL = "";
        if (baseDisplayURLSet) {
            graphURL = CHART_SERVLET;
        } else {
            graphURL = context.getRequest().getContextPath() + CHART_SERVLET;
        }
        graphURL = graphURL + (graphURL.indexOf('?') >= 0 ? "&" : "?") + "filename=" + getFilename();
        return graphURL;
    }

    /**
     * Writes an image map as a String
     * This function has been requested to be added to jfreechart
     * Also requires slight change to ChartEntity.getImageMapAreaTag()
     * to produce valid xml tag and use &amp; entity in urls.
     * Diffs sent to jfreechart project - so hopefully this will be in there soon
     *
     * @param name  the map name.
     * @param info  the chart rendering info.
     * @param useOverLibForToolTips  whether to use OverLIB for tooltips
     *                               (http://www.bosrup.com/web/overlib/).
     */
    public String writeImageMap(String name, ChartRenderingInfo info, boolean useOverLibForToolTips) {
        StringBuffer sb = new StringBuffer();
        sb.append("<map name=\"" + name + "\">");
        EntityCollection entities = info.getEntityCollection();
        Iterator iterator = entities.iterator();

        while (iterator.hasNext()) {
            ChartEntity entity = (ChartEntity) iterator.next();
            // the tag returned by getImageMapAreaTag is not valid xml
            //String area = entity.getImageMapAreaTag(useOverLibForToolTips);

            String area = "";

            if (useOverLibForToolTips)
                area = entity.getImageMapAreaTag(new org.jfree.chart.imagemap.OverLIBToolTipTagFragmentGenerator(),
                        new org.jfree.chart.imagemap.StandardURLTagFragmentGenerator());
            else
                area = entity.getImageMapAreaTag(new org.jfree.chart.imagemap.StandardToolTipTagFragmentGenerator(),
                        new org.jfree.chart.imagemap.StandardURLTagFragmentGenerator());
            // modify to valid xml tag
            area = area.replaceAll("&", "&amp;");
            //area = area.toLowerCase();
            //area = area.replaceAll(">$", "/>");
            if (area.length() > 0) {
                sb.append(area);
            }
        }
        sb.append("</map>");
        return sb.toString();
    }

    /**
     * build slicer text string
     */
    private String buildSlicer() {
        StringBuffer slicer = new StringBuffer();
        slicer.append("Slicer: ");
        Iterator pi = result.getSlicer().getPositions().iterator();
        while (pi.hasNext()) {
            Position p = (Position) pi.next();
            Member[] members = p.getMembers();
            for (int i = 0; i < members.length; i++) {
                slicer.append(members[i].getLevel().getLabel());
                slicer.append("=");
                slicer.append(members[i].getLabel());
                if (i < members.length - 1) {
                    slicer.append(", ");
                }
            }
        }
        return slicer.toString();
    }

    /**
     * Build a jfreechart CategoryDataset with a single series
     *
     */
    private DefaultCategoryDataset build1dimDataset() {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        // column axis
        List columnPositions = result.getAxes()[0].getPositions();
        colCount = columnPositions.size();
        // cells
        List cells = result.getCells();

        String series = "Series";

        // loop on column positions
        for (int i = 0; i < colCount; i++) {
            Member[] colMembers = ((Position) columnPositions.get(i)).getMembers();

            StringBuffer key = new StringBuffer();
            // loop on col position members
            for (int j = 0; j < colMembers.length; j++) {
                // build up composite name for this row
                key.append(colMembers[j].getLabel() + ".");
            }
            dataset.addValue(getNumberValue((Cell) cells.get(i)), series, key.toString());
        }
        return dataset;
    }

    /**
     * Get cell value as a Number. Parses the cell value string
     * using the locale set in this.locale.
     * @param cell
     * @return value as Number (can be null)
     */
    private Number getNumberValue(Cell cell) {
        //**** HACK AR 2004.01.10
        //String value = cell.getFormattedValue();
        Object value = cell.getValue();

        DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
        DecimalFormat formatter = new DecimalFormat();
        formatter.setDecimalFormatSymbols(dfs);
        Number number = null;
        try {
            number = (Number) value;
            //number = formatter.parse(value, new ParsePosition(0));
        } catch (Exception e) {
            number = null;
        }
        return number;
    }

    /**
    * Get a unique name string for a dataitem derived from the member chain
    *
    * @param myTree  (full member tree)
     * @param members - the list to be processed (either X/Y axis)
    * @return retValue as String
    */
    private String buildName(MemberTree myTree, Member[] members) {
        String retValue = new String();
        HashMap levelMap = new HashMap();
        HashMap hierarchyMap = new HashMap();
        for (int j = members.length - 1; j >= 0; j--) {
            Member member = members[j];
            while (member != null) {
                // only process if no other items from this level processed - should not be duplicates!
                if (!levelMap.containsValue(member.getLevel())) {
                    levelMap.put(member.getLevel().toString(), member.getLevel());
                    if (member.getRootDistance() == 0) {
                        // if root member, only add to name if no other members of the hierarchy are already added
                        if (!hierarchyMap.containsValue(member.getLevel().getHierarchy())
                                || myTree.getRootMembers(member.getLevel().getHierarchy()).length > 1) {
                            hierarchyMap.put(member.getLevel().getHierarchy().toString(),
                                    member.getLevel().getHierarchy());
                            retValue = member.getLabel() + "." + retValue;
                        }
                    } else {
                        hierarchyMap.put(member.getLevel().getHierarchy().toString(),
                                member.getLevel().getHierarchy());
                        retValue = member.getLabel() + "." + retValue;
                    }
                }
                member = myTree.getParent(member);
            }
        }
        return retValue;
    }

    /**
     * Build a jfreechart CategoryDataset with multiple series
     *
     */
    private DefaultCategoryDataset build2dimDataset() {

        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        // column axis

        List columnPositions = result.getAxes()[0].getPositions();//ladX.getPositions();
        colCount = columnPositions.size();

        // row axis

        List rowPositions = result.getAxes()[1].getPositions();//ladY.getPositions();
        int rowCount = rowPositions.size();
        List cells = result.getCells();

        // get the full member tree
        MemberTree myTree = ((MemberTree) olapModel.getExtension(MemberTree.ID));

        // for each column, starting with the bottom member, progress up the mmeber chain until the root is reached
        // keep track of the levels and hierarchies to avoid duplicates on level or hierarchys.
        //      *note: keeping track of the levels might be just extra work, I don't know if they CAN be repeated.
        //          if not, that logic can be easily removed (see buildName - above)
        // For each hierarchy, If a root member is reached (getRootDistance()=0), then only include it if there have been no other
        // lower level members already added:
        //       ie. All_dim1.dim1_lvl1.dim1_lvl2.All_dim2.dim2_lvl1 renders as dim1_lvl1.dim1_lvl2.dim2_lvl1
        //          whereas All_dim1.All_dim2 renders as the same.
        // The important part is that we include each parent on the way up, to ensure a unique name to
        // place in the map for the dataset (no longer overwriting each other)

        for (int i = 0; i < colCount; i++) {
            Position p = (Position) columnPositions.get(i);
            Member colMembers[] = p.getMembers();

            // build the label name for this column
            String label = buildName(myTree, colMembers);

            // For each row, use the same logic to build a unique key for each data item
            for (int k = 0; k < rowCount; k++) {
                Position rp = (Position) rowPositions.get(k);
                Member rowMembers[] = rp.getMembers();

                // build key name
                String key = buildName(myTree, rowMembers);

                Cell cell = (Cell) cells.get((k * colCount) + i);
                dataset.addValue(getNumberValue(cell), label.toString(), key.toString());
            }

        }
        return dataset;
    }

    /**
     * @return
     */
    public int getColCount() {
        return colCount;
    }

    /**
     * true means that render() will create a new chart
     */
    public boolean isDirty() {
        return dirty;
    }

    public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    public void modelChanged(ModelChangeEvent e) {
        this.dirty = true;
    }

    public void structureChanged(ModelChangeEvent e) {
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getChartHeight() {
        return chartHeight;
    }

    /**
     * @param chartHeight
     */
    public void setChartHeight(int chartHeight) {
        this.chartHeight = chartHeight;
        this.dirty = true;
    }

    /**
     * @return
     */
    public String getChartTitle() {
        return chartTitle;
    }

    /**
     * @param chartTitle
     */
    public void setChartTitle(String chartTitle) {
        this.chartTitle = chartTitle;
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getChartType() {
        return chartType;
    }

    /**
     * @param chartType
     */
    public void setChartType(int chartType) {
        this.chartType = chartType;
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getChartWidth() {
        return chartWidth;
    }

    /**
     * @param chartWidth
     */
    public void setChartWidth(int chartWidth) {
        this.chartWidth = chartWidth;
        this.dirty = true;
    }

    /**
     * @return
     */
    public String getHorizAxisLabel() {
        return horizAxisLabel;
    }

    /**
     * @param axisLabel
     */
    public void setHorizAxisLabel(String axisLabel) {
        horizAxisLabel = axisLabel;
        this.dirty = true;
    }

    /**
     * @return
     */
    public boolean getShowLegend() {
        return showLegend;
    }

    /**
     * @param showLegend
     */
    public void setShowLegend(boolean showLegend) {
        this.showLegend = showLegend;
        this.dirty = true;
    }

    /**
     * @return
     */
    public String getFontName() {
        return fontName;
    }

    /**
     * @param titleFont
     */
    public void setFontName(String fontname) {
        this.fontName = fontname;
        this.dirty = true;
    }

    /**
     * @return
     */
    public String getVertAxisLabel() {
        return vertAxisLabel;
    }

    /**
     * @param axisLabel
     */
    public void setVertAxisLabel(String axisLabel) {
        vertAxisLabel = axisLabel;
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getFontSize() {
        return fontSize;
    }

    /**
     * @param fontSize
     */
    public void setFontSize(int fontSize) {
        this.fontSize = fontSize;
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getFontStyle() {
        return fontStyle;
    }

    /**
     * @param fontStyle
     */
    public void setFontStyle(int fontStyle) {
        this.fontStyle = fontStyle;
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getBgColorB() {
        return bgColorB;
    }

    /**
     * @param bgColorB
     */
    public void setBgColorB(int bgColorB) {
        this.bgColorB = checkRGB(bgColorB);
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getBgColorG() {
        return bgColorG;
    }

    /**
     * @param bgColorG
     */
    public void setBgColorG(int bgColorG) {
        this.bgColorG = checkRGB(bgColorG);
        this.dirty = true;
    }

    /**
     * @return
     */
    public int getBgColorR() {
        return bgColorR;
    }

    /**
     * @param bgColorR
     */
    public void setBgColorR(int bgColorR) {
        this.bgColorR = checkRGB(bgColorR);
        this.dirty = true;
    }

    /**
     * Enforce limits of 0 - 255 for RGB values.
     */
    private int checkRGB(int v) {
        if (v > 255) {
            v = 255;
        } else if (v < 0) {
            v = 0;
        }
        return v;
    }

    /**
    * A URLGenerator class to generate pie urls that work with jpivot
    * @author ati
    *
    */
    public class jpivotPieURLGenerator extends StandardPieURLGenerator {
        /** Prefix to the URL */
        private String prefix = "";

        private List cells = result.getCells();

        private int rowCount;

        private TableOrder order; // COLUMN or ROW  - used to calculate cell Position

        jpivotPieURLGenerator() {
        }

        jpivotPieURLGenerator(String prefix) {
            this.prefix = prefix;
        }

        /*
         * Use this constructor to set dataExtraction type (PER_COLUMN/PER_ROW), and allow for
         * rowcount of current dataset  (could be changed to just take rowCount)
         */
        jpivotPieURLGenerator(TableOrder order, DefaultCategoryDataset dataset) {
            this.order = order;
            this.rowCount = dataset.getRowCount();
        }

        /*
         * As above with web controller URL
         */
        jpivotPieURLGenerator(TableOrder order, DefaultCategoryDataset dataset, String controllerURL) {
            this(order, dataset);
            this.prefix = controllerURL;
        }

        /**
         * Implementation of generateURL that integrates with jpivot/wcf framework.
         * A request handler is added for each cell/item.
         * No test is done to see if a cell is drillable, since the url has to added (I think, like an all or nothing ?)
         * Generates a URL for a particular item within a series.
         *
         * @param data  the dataset.
         * @param key  the data item key.
         * @param pieIndex the index of the pie containing key (zero-based).
         *
         * @return the generated URL
         */
        public String generateURL(PieDataset data, Comparable key, int pieIndex) {
            String url = prefix;
            int index = data.getIndex(key);

            int cellpos;
            if (order == TableOrder.BY_COLUMN) {
                cellpos = (pieIndex * rowCount) + index;
            } else {
                cellpos = pieIndex + (rowCount * index);
            }

            if (canDrillThrough((Cell) cells.get(cellpos)) && (!((Cell) cells.get(cellpos)).isNull())) {
                String id = DomUtils.randomId();
                dispatcher.addRequestListener(id, null, new DrillThroughHandler((Cell) cells.get(cellpos)));

                boolean firstParameter = url.indexOf("?") == -1;
                url += firstParameter ? "?" : "&";
                url += id;
                return url;
            } else {
                return null;
            }
        }
    }

    /**
     * A URLGenerator class to generate chart urls that work with jpivot
     * @author robin
     *
     */
    public class jpivotCategoryURLGenerator extends StandardCategoryURLGenerator {

        /** Prefix to the URL */
        private String prefix = "";

        /** Series parameter name to go in each URL */
        private String seriesParameterName = "col";

        /** Category parameter name to go in each URL */
        private String categoryParameterName = "row";

        private List cells = result.getCells();

        jpivotCategoryURLGenerator() {
        }

        jpivotCategoryURLGenerator(String prefix) {
            this.prefix = prefix;
        }

        /**
         * Implementation of generateURL that integrates with jpivot/wcf framework.
         * A request handler is added for each cell/item.
         * No test is done to see if a cell is drillable, since the url has to added (I think, like an all or nothing ?)
         * Generates a URL for a particular item within a series.
         *
         * @param data  the dataset.
         * @param series  the series index (zero-based).
         * @param category  the category index (zero-based).
         *
         * @return the generated URL
         */
        public String generateURL(CategoryDataset data, int series, int category) {
            String url = prefix;
            // convert col, row into ordinal
            // series is col, category is row
            // (reverse terminology to jfreechart, that way series on measures which is more logical)
            int cellpos = (category * colCount) + series;
            if (canDrillThrough((Cell) cells.get(cellpos)) && (!((Cell) cells.get(cellpos)).isNull())) {
                String id = DomUtils.randomId();
                dispatcher.addRequestListener(id, null, new DrillThroughHandler((Cell) cells.get(cellpos)));

                boolean firstParameter = url.indexOf("?") == -1;
                url += firstParameter ? "?" : "&";
                url += id;
                return url;
            } else {
                return null;
            }
        }
    }

    /**
     * request handler for chart drill through
     * does nothing if cell can't be drilled through (e.g. calculated measure)
     * @author robin
     *
     */
    //   class DrillThroughHandler implements RequestListener {
    //      Cell cell;
    //      DrillThroughHandler(Cell cell) {
    //         this.cell = cell;
    //      }
    //      public void request(RequestContext context) throws Exception {
    //
    //         if ( canDrillThrough(cell) ) {
    //
    //            HttpSession session = context.getSession();
    //            final String drillTableRef = olapModel.getID() + ".drillthroughtable";
    //            com.tonbeller.wcf.table.TableComponent tc =
    //                     (com.tonbeller.wcf.table.TableComponent)
    //                     session.getAttribute(drillTableRef);
    //            // get a new drill through table model
    //            TableModel tm = drillThrough(cell);
    //
    //            // need to create a new table model for each drill through request
    //            // because table model creates an array of columns
    //            tc = new com.tonbeller.wcf.table.TableComponent(drillTableRef, tm);
    //            tc.initialize(context);
    //            ((DefaultSelectionModel) tc.getSelectionModel()).setMode(SelectionModel.NO_SELECTION);
    //            session.setAttribute(drillTableRef, tc);
    //            tc.setVisible(true);
    //         }
    //      }
    //   }
    class DrillThroughHandler implements RequestListener {
        Cell cell;

        DrillThroughHandler(Cell cell) {
            this.cell = cell;
        }

        public void request(RequestContext context) throws Exception {

            if (canDrillThrough(cell)) {

                HttpSession session = context.getSession();
                final String drillTableRef = olapModel.getID() + ".drillthroughtable";
                ITableComponent tc = (ITableComponent) session.getAttribute(drillTableRef);
                // get a new drill through table model
                TableModel tm = drillThrough(cell);
                tc.setModel(tm);
                tc.setVisible(true);

                TableColumn[] tableColumns = null;
                if (tc instanceof EditableTableComponent) {
                    tableColumns = ((EditableTableComponent) tc).getTableComp().getTableColumns();
                } else if (tc instanceof com.tonbeller.wcf.table.TableComponent) {
                    tableColumns = ((com.tonbeller.wcf.table.TableComponent) tc).getTableColumns();
                }
                if (tableColumns != null) {
                    for (int i = 0; i < tableColumns.length; i++) {
                        TableColumn tableColumn = tableColumns[i];
                        tableColumn.setHidden(false);
                    }
                }
            }
        }
    }

    protected boolean canDrillThrough(Cell cell) {
        return ((DrillThrough) olapModel.getExtension(DrillThrough.ID))
                .canDrillThrough((Cell) cell.getRootDecoree());
    }

    /**
     * returns a DrillThroughTableModel for the drill through
     * @param cell
     * @return
     */
    protected TableModel drillThrough(Cell cell) {
        return ((DrillThrough) olapModel.getExtension(DrillThrough.ID)).drillThrough((Cell) cell.getRootDecoree());
    }

    public boolean isDrillThroughEnabled() {
        return drillThroughEnabled;
    }

    public void setDrillThroughEnabled(boolean drillThroughEnabled) {
        this.drillThroughEnabled = drillThroughEnabled;
    }

    /**
     * @return
     */
    public String getAxisFontName() {
        return axisFontName;
    }

    /**
     * @param axisFontName
     */
    public void setAxisFontName(String axisFontName) {
        this.axisFontName = axisFontName;
    }

    /**
     * @return
     */
    public int getAxisFontSize() {
        return axisFontSize;
    }

    /**
     * @param axisFontSize
     */
    public void setAxisFontSize(int axisFontSize) {
        this.axisFontSize = axisFontSize;
    }

    /**
     * @return
     */
    public int getAxisFontStyle() {
        return axisFontStyle;
    }

    /**
     * @param axisFontStyle
     */
    public void setAxisFontStyle(int axisFontStyle) {
        this.axisFontStyle = axisFontStyle;
    }

    /**
     * @return
     */
    public String getLegendFontName() {
        return legendFontName;
    }

    /**
     * @param legendFontName
     */
    public void setLegendFontName(String legendFontName) {
        this.legendFontName = legendFontName;
    }

    /**
     * @return
     */
    public int getLegendFontSize() {
        return legendFontSize;
    }

    /**
     * @param legendFontSize
     */
    public void setLegendFontSize(int legendFontSize) {
        this.legendFontSize = legendFontSize;
    }

    /**
     * @return
     */
    public int getLegendFontStyle() {
        return legendFontStyle;
    }

    /**
     * @param legendFontStyle
     */
    public void setLegendFontStyle(int legendFontStyle) {
        this.legendFontStyle = legendFontStyle;
    }

    /**
     * @return
     */
    public int getSlicerAlignment() {
        return slicerAlignment;
    }

    /**
     * @param slicerAlignment
     */
    public void setSlicerAlignment(int slicerAlignment) {
        this.slicerAlignment = slicerAlignment;
    }

    /**
     * @return
     */
    public String getSlicerFontName() {
        return slicerFontName;
    }

    /**
     * @param slicerFontName
     */
    public void setSlicerFontName(String slicerFontName) {
        this.slicerFontName = slicerFontName;
    }

    /**
     * @return
     */
    public int getSlicerFontSize() {
        return slicerFontSize;
    }

    /**
     * @param slicerFontSize
     */
    public void setSlicerFontSize(int slicerFontSize) {
        this.slicerFontSize = slicerFontSize;
    }

    /**
     * @return
     */
    public int getSlicerFontStyle() {
        return slicerFontStyle;
    }

    /**
     * @param slicerFontStyle
     */
    public void setSlicerFontStyle(int slicerFontStyle) {
        this.slicerFontStyle = slicerFontStyle;
    }

    /**
     * @return
     */
    public int getSlicerPosition() {
        return slicerPosition;
    }

    /**
     * @param slicerPosition
     */
    public void setSlicerPosition(int slicerPosition) {
        this.slicerPosition = slicerPosition;
    }

    /**
     * @return
     */
    public int getLegendPosition() {
        return legendPosition;
    }

    /**
     * @param legendPosition
     */
    public void setLegendPosition(int legendPosition) {
        this.legendPosition = legendPosition;
    }

    /**
     * @return
     */
    public String getAxisTickFontName() {
        return axisTickFontName;
    }

    /**
     * @param axisTickFontName
     */
    public void setAxisTickFontName(String axisTickFontName) {
        this.axisTickFontName = axisTickFontName;
    }

    /**
     * @return
     */
    public int getAxisTickFontSize() {
        return axisTickFontSize;
    }

    /**
     * @param axisTickFontSize
     */
    public void setAxisTickFontSize(int axisTickFontSize) {
        this.axisTickFontSize = axisTickFontSize;
    }

    /**
     * @return
     */
    public int getAxisTickFontStyle() {
        return axisTickFontStyle;
    }

    /**
     * @param axisTickFontStyle
     */
    public void setAxisTickFontStyle(int axisTickFontStyle) {
        this.axisTickFontStyle = axisTickFontStyle;
    }

    /**
     * @return Returns the tickLabelRotate.
     */
    public int getTickLabelRotate() {
        return tickLabelRotate;
    }

    /**
     * @param tickLabelRotate The tickLabelRotate to set.
     */
    public void setTickLabelRotate(int tickLabelRotate) {
        this.tickLabelRotate = tickLabelRotate;
    }

    /**
     * @return
     */
    public boolean isShowSlicer() {
        return showSlicer;
    }

    /**
     * @param showSlicer
     */
    public void setShowSlicer(boolean showSlicer) {
        this.showSlicer = showSlicer;
    }

    /**
    * @return
    */
    public String getFilename() {
        return filename;
    }
}