org.opennms.netmgt.charts.ChartUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.charts.ChartUtils.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2006-2012 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2012 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.charts;

import java.awt.Color;
import java.awt.Paint;
import java.awt.image.BufferedImage;
import java.beans.PropertyVetoException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.List;

import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.ExtendedCategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.jdbc.JDBCCategoryDataset;
import org.opennms.core.db.DataSourceFactory;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.ChartConfigFactory;
import org.opennms.netmgt.config.charts.BarChart;
import org.opennms.netmgt.config.charts.Blue;
import org.opennms.netmgt.config.charts.Green;
import org.opennms.netmgt.config.charts.ImageSize;
import org.opennms.netmgt.config.charts.Red;
import org.opennms.netmgt.config.charts.Rgb;
import org.opennms.netmgt.config.charts.SeriesDef;
import org.opennms.netmgt.config.charts.SubTitle;
import org.opennms.netmgt.config.charts.Title;

/**
 * <p>ChartUtils class.</p>
 *
 * @author <a href="david@opennms.org">David Hustace</a>
 * @version $Id: $
 */
public abstract class ChartUtils {

    /**
     * Use this it initialize required factories so that the WebUI doesn't
     * have to.  Can't wait for Spring.
     */
    static {
        try {
            DataSourceFactory.init();
            ChartConfigFactory.init();
        } catch (MarshalException e) {
            log().error("static initializer: Error marshalling chart configuration. " + e);
        } catch (ValidationException e) {
            log().error("static initializer: Error validating chart configuration. " + e);
        } catch (FileNotFoundException e) {
            log().error("static initializer: Error finding chart configuration. " + e);
        } catch (IOException e) {
            log().error("static initializer: IO error while marshalling chart configuration file. " + e);
        } catch (ClassNotFoundException e) {
            log().error("static initializer: Error initializing database connection factory. " + e);
        } catch (PropertyVetoException e) {
            log().error("static initializer: Error initializing database connection factory. " + e);
        } catch (SQLException e) {
            log().error("static initializer: Error initializing database connection factory. " + e);
        }
        // XXX why don't we throw an exception here or something?
    }

    /**
     * Logging helper method.
     * 
     * @return A log4j <code>Category</code>.
     */
    private static ThreadCategory log() {
        return ThreadCategory.getInstance(ChartUtils.class);
    }

    /**
     * This method will returns a JFreeChart bar chart constructed based on XML configuration.
     *
     * @param chartName Name specified in chart-configuration.xml
     * @return <code>JFreeChart</code> constructed from the chartName
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     * @throws java.sql.SQLException if any.
     */
    public static JFreeChart getBarChart(String chartName)
            throws MarshalException, ValidationException, IOException, SQLException {

        //ChartConfigFactory.reload();

        BarChart chartConfig = null;
        chartConfig = getBarChartConfigByName(chartName);

        if (chartConfig == null) {
            throw new IllegalArgumentException("getBarChart: Invalid chart name.");
        }

        DefaultCategoryDataset baseDataSet = buildCategoryDataSet(chartConfig);
        JFreeChart barChart = createBarChart(chartConfig, baseDataSet);
        addSubTitles(chartConfig, barChart);

        String subLabelClass = chartConfig.getSubLabelClass();
        if (subLabelClass != null) {
            addSubLabels(barChart, subLabelClass);
        }

        customizeSeries(barChart, chartConfig);

        return barChart;

    }

    /**
     * @param barChart TODO
     * @param subLabelClass
     */
    private static void addSubLabels(JFreeChart barChart, String subLabelClass) {
        ExtendedCategoryAxis subLabels;
        CategoryPlot plot = barChart.getCategoryPlot();
        try {
            subLabels = (ExtendedCategoryAxis) Class.forName(subLabelClass).newInstance();
            List<?> cats = plot.getCategories();
            for (int i = 0; i < cats.size(); i++) {
                subLabels.addSubLabel((Comparable<?>) cats.get(i), cats.get(i).toString());
            }
            plot.setDomainAxis(subLabels);
        } catch (InstantiationException e) {
            log().error("getBarChart: Couldn't instantiate configured CategorySubLabels class: " + subLabelClass,
                    e);
        } catch (IllegalAccessException e) {
            log().error("getBarChart: Couldn't instantiate configured CategorySubLabels class: " + subLabelClass,
                    e);
        } catch (ClassNotFoundException e) {
            log().error("getBarChart: Couldn't instantiate configured CategorySubLabels class: " + subLabelClass,
                    e);
        }
    }

    /**
     * @param barChart TODO
     * @param chartConfig
     */
    private static void customizeSeries(JFreeChart barChart, BarChart chartConfig) {

        /*
         * Set the series colors and labels
         */
        CategoryItemLabelGenerator itemLabelGenerator = new StandardCategoryItemLabelGenerator("{2}",
                new DecimalFormat("0"));
        SeriesDef[] seriesDefs = chartConfig.getSeriesDef();
        CustomSeriesColors seriesColors = null;

        if (chartConfig.getSeriesColorClass() != null) {
            try {
                seriesColors = (CustomSeriesColors) Class.forName(chartConfig.getSeriesColorClass()).newInstance();
            } catch (InstantiationException e) {
                log().error(
                        "getBarChart: Couldn't instantiate configured CustomSeriesColors class: " + seriesColors,
                        e);
            } catch (IllegalAccessException e) {
                log().error(
                        "getBarChart: Couldn't instantiate configured CustomSeriesColors class: " + seriesColors,
                        e);
            } catch (ClassNotFoundException e) {
                log().error(
                        "getBarChart: Couldn't instantiate configured CustomSeriesColors class: " + seriesColors,
                        e);
            }
        }

        for (int i = 0; i < seriesDefs.length; i++) {
            SeriesDef seriesDef = seriesDefs[i];
            Paint paint = Color.BLACK;
            if (seriesColors != null) {
                Comparable<?> cat = (Comparable<?>) ((BarRenderer) barChart.getCategoryPlot().getRenderer())
                        .getPlot().getCategories().get(i);
                paint = seriesColors.getPaint(cat);
            } else {
                Rgb rgb = seriesDef.getRgb();
                paint = new Color(rgb.getRed().getRgbColor(), rgb.getGreen().getRgbColor(),
                        rgb.getBlue().getRgbColor());
            }
            ((BarRenderer) barChart.getCategoryPlot().getRenderer()).setSeriesPaint(i, paint);
            ((BarRenderer) barChart.getCategoryPlot().getRenderer()).setSeriesItemLabelsVisible(i,
                    seriesDef.getUseLabels());
            ((BarRenderer) barChart.getCategoryPlot().getRenderer()).setSeriesItemLabelGenerator(i,
                    itemLabelGenerator);
        }
    }

    /**
     * @param chartConfig
     * @param barChart
     */
    private static void addSubTitles(BarChart chartConfig, JFreeChart barChart) {
        Iterator<SubTitle> it;
        /*
         * Add subtitles.
         */
        for (it = chartConfig.getSubTitleCollection().iterator(); it.hasNext();) {
            SubTitle subTitle = (SubTitle) it.next();
            Title title = subTitle.getTitle();
            String value = title.getValue();
            barChart.addSubtitle(new TextTitle(value));
        }
    }

    /**
     * @param chartConfig
     * @param baseDataSet
     * @return
     */
    private static JFreeChart createBarChart(BarChart chartConfig, DefaultCategoryDataset baseDataSet) {
        PlotOrientation po = (chartConfig.getPlotOrientation() == "horizontal" ? PlotOrientation.HORIZONTAL
                : PlotOrientation.VERTICAL);
        JFreeChart barChart = null;
        if ("3d".equalsIgnoreCase(chartConfig.getVariation())) {
            barChart = ChartFactory.createBarChart3D(chartConfig.getTitle().getValue(),
                    chartConfig.getDomainAxisLabel(), chartConfig.getRangeAxisLabel(), baseDataSet, po,
                    chartConfig.getShowLegend(), chartConfig.getShowToolTips(), chartConfig.getShowUrls());
        } else {
            barChart = ChartFactory.createBarChart(chartConfig.getTitle().getValue(),
                    chartConfig.getDomainAxisLabel(), chartConfig.getRangeAxisLabel(), baseDataSet, po,
                    chartConfig.getShowLegend(), chartConfig.getShowToolTips(), chartConfig.getShowUrls());
        }

        // Create a bit more headroom for value labels than is allowed for by the default 0.05 upper margin
        ValueAxis rangeAxis = barChart.getCategoryPlot().getRangeAxis();
        if (rangeAxis.getUpperMargin() < 0.1) {
            rangeAxis.setUpperMargin(0.1);
        }

        return barChart;
    }

    /**
     * @param chartConfig
     * @param baseDataSet
     * @throws SQLException
     */
    private static DefaultCategoryDataset buildCategoryDataSet(BarChart chartConfig) throws SQLException {
        DefaultCategoryDataset baseDataSet = new DefaultCategoryDataset();
        /*
         * Configuration can contain more than one series.  This loop adds
         * single series data sets returned from sql query to a base data set
         * to be displayed in a the chart. 
         */
        Connection conn = null;
        try {
            conn = DataSourceFactory.getInstance().getConnection();
            Iterator<SeriesDef> it = chartConfig.getSeriesDefCollection().iterator();
            while (it.hasNext()) {
                SeriesDef def = it.next();
                JDBCCategoryDataset dataSet = new JDBCCategoryDataset(conn, def.getJdbcDataSet().getSql());

                for (int i = 0; i < dataSet.getRowCount(); i++) {
                    for (int j = 0; j < dataSet.getColumnCount(); j++) {
                        baseDataSet.addValue(dataSet.getValue(i, j), def.getSeriesName(), dataSet.getColumnKey(j));
                    }
                }
            }
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return baseDataSet;
    }

    /**
     * Helper method that returns the JFreeChart to an output stream written in JPEG format.
     *
     * @param chartName a {@link java.lang.String} object.
     * @param out a {@link java.io.OutputStream} object.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     * @throws java.sql.SQLException if any.
     */
    public static void getBarChart(String chartName, OutputStream out)
            throws MarshalException, ValidationException, IOException, SQLException {
        BarChart chartConfig = getBarChartConfigByName(chartName);
        JFreeChart chart = getBarChart(chartName);
        ImageSize imageSize = chartConfig.getImageSize();
        int hzPixels;
        int vtPixels;

        if (imageSize == null) {
            hzPixels = 400;
            vtPixels = 400;
        } else {
            hzPixels = imageSize.getHzSize().getPixels();
            vtPixels = imageSize.getVtSize().getPixels();
        }

        ChartUtilities.writeChartAsJPEG(out, chart, hzPixels, vtPixels);

    }

    /**
     * Helper method that returns the JFreeChart to an output stream written in JPEG format.
     *
     * @param chartName a {@link java.lang.String} object.
     * @param out a {@link java.io.OutputStream} object.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     * @throws java.sql.SQLException if any.
     */
    public static void getBarChartPNG(String chartName, OutputStream out)
            throws MarshalException, ValidationException, IOException, SQLException {
        ChartFactory.setChartTheme(StandardChartTheme.createLegacyTheme());
        BarChart chartConfig = getBarChartConfigByName(chartName);
        JFreeChart chart = getBarChart(chartName);
        if (chartConfig.getChartBackgroundColor() != null) {
            setChartBackgroundColor(chartConfig, chart);
        }

        if (chartConfig.getPlotBackgroundColor() != null) {
            setPlotBackgroundColor(chartConfig, chart);
        }
        ImageSize imageSize = chartConfig.getImageSize();
        int hzPixels;
        int vtPixels;

        if (imageSize == null) {
            hzPixels = 400;
            vtPixels = 400;
        } else {
            hzPixels = imageSize.getHzSize().getPixels();
            vtPixels = imageSize.getVtSize().getPixels();
        }

        ChartUtilities.writeChartAsPNG(out, chart, hzPixels, vtPixels, false, 6);

    }

    private static void setPlotBackgroundColor(BarChart chartConfig, JFreeChart chart) {
        Red red = chartConfig.getPlotBackgroundColor().getRgb().getRed();
        Blue blue = chartConfig.getPlotBackgroundColor().getRgb().getBlue();
        Green green = chartConfig.getPlotBackgroundColor().getRgb().getGreen();

        chart.getPlot().setBackgroundPaint(new Color(red.getRgbColor(), green.getRgbColor(), blue.getRgbColor()));
    }

    private static void setChartBackgroundColor(BarChart chartConfig, JFreeChart chart) {
        Red red = chartConfig.getChartBackgroundColor().getRgb().getRed();
        Blue blue = chartConfig.getChartBackgroundColor().getRgb().getBlue();
        Green green = chartConfig.getChartBackgroundColor().getRgb().getGreen();
        chart.setBackgroundPaint(new Color(red.getRgbColor(), green.getRgbColor(), blue.getRgbColor()));
    }

    /**
     * Helper method that returns the JFreeChart as a PNG byte array.
     *
     * @param chartName a {@link java.lang.String} object.
     * @return a byte array
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     * @throws java.sql.SQLException if any.
     */
    public static byte[] getBarChartAsPNGByteArray(String chartName)
            throws MarshalException, ValidationException, IOException, SQLException {
        BarChart chartConfig = getBarChartConfigByName(chartName);
        JFreeChart chart = getBarChart(chartName);
        ImageSize imageSize = chartConfig.getImageSize();
        int hzPixels;
        int vtPixels;

        if (imageSize == null) {
            hzPixels = 400;
            vtPixels = 400;
        } else {
            hzPixels = imageSize.getHzSize().getPixels();
            vtPixels = imageSize.getVtSize().getPixels();
        }
        return ChartUtilities.encodeAsPNG(chart.createBufferedImage(hzPixels, vtPixels));
    }

    /**
     * Helper method used to return a JFreeChart as a buffered Image.
     *
     * @param chartName a {@link java.lang.String} object.
     * @return a <code>BufferedImage</code>
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     * @throws java.sql.SQLException if any.
     */
    public static BufferedImage getChartAsBufferedImage(String chartName)
            throws MarshalException, ValidationException, IOException, SQLException {
        BarChart chartConfig = getBarChartConfigByName(chartName);
        JFreeChart chart = getBarChart(chartName);
        ImageSize imageSize = chartConfig.getImageSize();
        int hzPixels;
        int vtPixels;

        if (imageSize == null) {
            hzPixels = 400;
            vtPixels = 400;
        } else {
            hzPixels = imageSize.getHzSize().getPixels();
            vtPixels = imageSize.getVtSize().getPixels();
        }

        return chart.createBufferedImage(hzPixels, vtPixels);

    }

    /**
     * Helper method used to retrieve the XML defined BarChart (castor class)
     *
     * @param chartName a {@link java.lang.String} object.
     * @return a derived Castor class: BarChart
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public static BarChart getBarChartConfigByName(String chartName)
            throws MarshalException, ValidationException, IOException {
        Iterator<BarChart> it = getChartCollectionIterator();
        BarChart chart = null;
        while (it.hasNext()) {
            chart = (BarChart) it.next();
            if (chart.getName().equals(chartName))
                return chart;
        }
        return null;
    }

    /**
     * Helper method used to fetch an Iterator for all defined Charts
     *
     * @return <code>BarChart</code> Iterator
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public static Iterator<BarChart> getChartCollectionIterator()
            throws IOException, MarshalException, ValidationException {
        return ChartConfigFactory.getInstance().getConfiguration().getBarChartCollection().iterator();
    }

}