org.objectweb.proactive.extensions.timitspmd.util.charts.MatrixChart.java Source code

Java tutorial

Introduction

Here is the source code for org.objectweb.proactive.extensions.timitspmd.util.charts.MatrixChart.java

Source

/*
 * ################################################################
 *
 * ProActive Parallel Suite(TM): The Java(TM) library for
 *    Parallel, Distributed, Multi-Core Computing for
 *    Enterprise Grids & Clouds
 *
 * Copyright (C) 1997-2012 INRIA/University of
 *                 Nice-Sophia Antipolis/ActiveEon
 * Contact: proactive@ow2.org or contact@activeeon.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; version 3 of
 * the License.
 *
 * This library 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
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * If needed, contact us to obtain a release under GPL Version 2 or 3
 * or a different license than the AGPL.
 *
 *  Initial developer(s):               The ProActive Team
 *                        http://proactive.inria.fr/team_members.htm
 *  Contributor(s):
 *
 * ################################################################
 * $$PROACTIVE_INITIAL_DEV$$
 */
package org.objectweb.proactive.extensions.timitspmd.util.charts;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import javax.imageio.ImageIO;

import org.jdom.Element;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.AxisState;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTick;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.MatrixSeriesCollection;
import org.jfree.data.xy.NormalizedMatrixSeries;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleEdge;
import org.objectweb.proactive.extensions.timitspmd.TimIt;
import org.objectweb.proactive.extensions.timitspmd.config.ConfigChart;
import org.objectweb.proactive.extensions.timitspmd.util.BenchmarkStatistics;
import org.objectweb.proactive.extensions.timitspmd.util.XMLHelper;

/**
 * This class contains all methods to build a matrix chart from a
 * two-dimensional integer array. Used for communication pattern analysis.
 *
 * @author The ProActive Team
 */
public class MatrixChart implements Chart {

    /**
     *
     */
    public static final URL logoFile = MatrixChart.class
            .getResource("/org/objectweb/proactive/extensions/timitspmd/pics/TimItProActive.png");
    private Chart.LegendFormat legendFormatMode;
    private Chart.Scale scaleMode;

    /** The array that will be displayed on the chart */
    private int[][] array;

    /** The legendValues */
    private int[] legendValues;

    /** The max value of the array */
    private int maxValue;

    /** The main chart */
    private JFreeChart mainChart;

    /** The legend containing chart */
    private JFreeChart legendChart;

    public void generateChart(Element eTimit, BenchmarkStatistics bstats, ConfigChart cChart) {
        String name = cChart.get("eventName");
        int[][] a = (int[][]) bstats.getEventsStatistics().getEventDataValue(name);
        this.array = ((a == null) ? MatrixChart.build2DArray(16) : a);
        this.maxValue = MatrixChart.getMaxValue(this.array);
        this.scaleMode = ConfigChart.scaleValue(cChart.get("scaleMode"));
        this.legendFormatMode = ConfigChart.legendValue(cChart.get("legendFormatMode"));

        if (this.scaleMode == Chart.Scale.DEFAULT) {
            this.scaleMode = Chart.Scale.LOGARITHMIC;
        }
        if (this.legendFormatMode == Chart.LegendFormat.DEFAULT) {
            this.legendFormatMode = Chart.LegendFormat.POW2;
        }

        this.buildFinalChart(cChart);
    }

    /**
     * Creates a two-dim array and returns it.
     *
     * @param size
     *            The size of the array.
     * @return The created array
     */
    private static int[][] build2DArray(int size) {
        int[][] res = new int[size][size];

        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res.length; j++) {
                res[i][j] = i * 100000;
            }
        }
        return res;
    }

    private static int getMaxValue(int[][] a) {
        int i;
        int j;
        int max = 0;

        for (i = 0; i < a.length; i++) {
            for (j = 0; j < a.length; j++) {
                if (a[i][j] > max) {
                    max = a[i][j];
                }
            }
        }
        return max;
    }

    private void buildMainChart(String title, String subTitle, String xAxisLabel, String yAxisLabel,
            String fileName) {
        final MatrixSeriesCollection dataset = new MatrixSeriesCollection(this.createMatrixDataSet());

        final JFreeChart chart = ChartFactory.createBubbleChart(title, xAxisLabel, yAxisLabel, dataset,
                PlotOrientation.VERTICAL, true, true, false);

        chart.addSubtitle(new TextTitle(subTitle));
        chart.setBackgroundPaint(new GradientPaint(0, 0, Color.white, 0, 1000, Color.WHITE));
        chart.removeLegend();

        // Perform customizations starts here ...
        final XYPlot plot1 = chart.getXYPlot();

        plot1.setDomainGridlinesVisible(false);
        plot1.setRangeGridlinesVisible(false);
        plot1.setForegroundAlpha(0.5f);
        plot1.setDomainAxis(new CustomAxis(plot1.getDomainAxis().getLabel()));
        plot1.setRangeAxis(new CustomAxis(plot1.getRangeAxis().getLabel()));

        // Custumize the domain axis ( y )
        final NumberAxis domainAxis = (NumberAxis) plot1.getDomainAxis();
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domainAxis.setRange(-1, this.array.length);

        // Custumize the range axis ( y )
        final NumberAxis rangeAxis = (NumberAxis) plot1.getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis.setRange(-1, this.array.length);

        // Create custom renderer
        StandardXYItemRenderer ren = new CustomRenderer(false);
        ren.setSeriesItemLabelPaint(0, Color.BLACK);
        plot1.setRenderer(ren);
        this.mainChart = chart;
    }

    private void buildLegendChart(int nbValues) {
        this.legendValues = new int[nbValues + 1];
        this.legendValues[0] = 0;
        int offset = 255 / nbValues;
        int step = offset;

        if (this.scaleMode == Chart.Scale.LOGARITHMIC) {
            double logStep = (Math.log(this.maxValue) / Math.log(2)) / nbValues;
            for (int i = 1; i < (nbValues + 1); i++) {
                this.legendValues[i] = (int) Math.pow(2, logStep * i);
            }
        } else { // Linear scale mode
            for (int i = 1; i < (nbValues + 1); i++) {
                this.legendValues[i] = (step * this.maxValue) / 255;
                step += offset;
            }
        }

        final MatrixSeriesCollection dataset = new MatrixSeriesCollection(this.createLegendDataSet());

        final JFreeChart chart = ChartFactory.createBubbleChart("", "", "", dataset, PlotOrientation.VERTICAL, true,
                true, false);

        chart.setBackgroundPaint(new GradientPaint(0, 0, Color.white, 0, 1000, Color.WHITE));
        chart.removeLegend();

        // Perform customizations starts here ...
        final XYPlot plot1 = chart.getXYPlot();

        plot1.setDomainGridlinesVisible(false);
        plot1.setRangeGridlinesVisible(false);
        plot1.setForegroundAlpha(0.5f);
        plot1.setDomainAxis(new CustomAxis(plot1.getDomainAxis().getLabel()));
        plot1.setRangeAxis(new CustomAxis(plot1.getRangeAxis().getLabel()));

        // Custumize the domain axis ( x )
        final NumberAxis domainAxis = (NumberAxis) plot1.getDomainAxis();
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domainAxis.setRange(-1, 1);
        domainAxis.setVisible(false);

        // Custumize the range axis ( y )
        final NumberAxis rangeAxis = (NumberAxis) plot1.getRangeAxis();
        rangeAxis.setTickUnit(new CustomTickUnit(rangeAxis.getTickUnit().getSize()));
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis.setRange(-1, this.legendValues.length);
        rangeAxis.setTickLabelsVisible(true);
        rangeAxis.setTickMarkInsideLength(4);

        // Create custom renderer
        StandardXYItemRenderer ren = new CustomRenderer(true);
        ren.setSeriesItemLabelPaint(0, Color.BLUE);
        plot1.setRenderer(ren);
        plot1.setRangeAxisLocation(AxisLocation.TOP_OR_RIGHT);

        this.legendChart = chart;
    }

    private void buildFinalChart(ConfigChart cChart) {
        this.buildFinalChart(cChart.get("title"), cChart.get("subTitle"), cChart.get("filename"),
                cChart.get("xAxisLabel"), cChart.get("yAxisLabel"), Integer.valueOf(cChart.get("width")),
                Integer.valueOf(cChart.get("height")));
    }

    private void buildFinalChart(String title, String subTitle, String filename, String xAxisLabel,
            String yAxisLabel, int width, int height) {
        this.buildMainChart(title, subTitle, xAxisLabel, yAxisLabel, filename);

        this.buildLegendChart(5);

        BufferedImage mainChartImage = this.mainChart.createBufferedImage(width, height);
        BufferedImage legendChartImage = this.legendChart.createBufferedImage(width / 6, height / 3);
        BufferedImage info = null;
        try {
            info = ImageIO.read(MatrixChart.logoFile);
        } catch (IOException ex) {
        }

        BufferedImage total = new BufferedImage(width + (width / 6), height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = total.createGraphics();

        g.drawImage(mainChartImage, 0, 0, null);
        g.drawImage(legendChartImage, width, height / 4, null);
        g.setPaint(Color.WHITE);
        g.fillRect(width, 0, width, height / 4);
        g.fillRect(width, (height / 4) + (height / 3), width, height);
        if (info != null) {
            // g.drawImage(info, (width+(width/6))-info.getWidth(),10, null); //
            // up-right
            g.drawImage(info, (width + (width / 6)) - info.getWidth(), height - info.getHeight(), null); // down-right
        }
        g.dispose();

        try {
            javax.imageio.ImageIO.write(total, "png", XMLHelper.createFileWithDirs(filename));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Creates a NormalizedMatrixSeries from the array and returns it.
     */
    private NormalizedMatrixSeries createMatrixDataSet() {
        NormalizedMatrixSeries matrix = new NormalizedMatrixSeries("s", this.array.length, this.array.length);
        return matrix;
    }

    /**
     * Creates a Legend DataSet and returns it.
     */
    private NormalizedMatrixSeries createLegendDataSet() {
        NormalizedMatrixSeries matrix = new NormalizedMatrixSeries("Legend", this.legendValues.length, 1);

        return matrix;
    }

    /**
     * A custom renderer that returns a different color for each item in a
     * single series.
     */
    private class CustomRenderer extends StandardXYItemRenderer {

        /**
         *
         */

        /** The current viewed row */
        private int currentRow = 0;

        /** A boolean for legend mode */
        private boolean legendMode = false;

        /**
         * Creates a new renderer.
         *
         * @param colors
         *            the colors.
         */
        public CustomRenderer(boolean legendMode) {
            super();
            this.legendMode = legendMode;
        }

        /**
         * Returns the paint for an item. Overrides the default behaviour
         * inherited from AbstractSeriesRenderer.
         *
         * @param row
         *            the series.
         * @param column
         *            the category.
         *
         * @return The item color.
         */
        @Override
        public Paint getItemPaint(final int series, final int item) {
            int value = 0;
            if (this.legendMode) {
                if (MatrixChart.this.scaleMode == Chart.Scale.LOGARITHMIC) {
                    value = (255 / (MatrixChart.this.legendValues.length - 1)) * item;
                } else {
                    value = (MatrixChart.this.legendValues[item] * 255)
                            / ((MatrixChart.this.maxValue == 0) ? 1 : MatrixChart.this.maxValue);
                }
            } else {
                if ((item != 0) && ((item % MatrixChart.this.array.length) == 0)) {
                    this.currentRow++;
                }
                value = (int) ((MatrixChart.this.array[this.currentRow][item % MatrixChart.this.array.length]
                        / (MatrixChart.this.maxValue + 0.01)) * 255);

                if (MatrixChart.this.scaleMode == Chart.Scale.LOGARITHMIC) {
                    value = (int) (((Math.log(((value == 255) ? 255 : (value + 1))) / Math.log(2))
                            / (Math.log(255) / Math.log(2))) * 255);
                }
            }

            return new Color(0, 0, (int) (255 - (Math.pow(value / 255.0, 6) * 80)), value);
        }

        /**
         * Draws the visual representation of a single data item.
         *
         * @param g2
         *            the graphics device.
         * @param state
         *            the renderer state.
         * @param dataArea
         *            the area within which the data is being drawn.
         * @param info
         *            collects information about the drawing.
         * @param plot
         *            the plot (can be used to obtain standard color information
         *            etc).
         * @param domainAxis
         *            the domain (horizontal) axis.
         * @param rangeAxis
         *            the range (vertical) axis.
         * @param dataset
         *            the dataset.
         * @param series
         *            the series index (zero-based).
         * @param item
         *            the item index (zero-based).
         * @param crosshairState
         *            crosshair information for the plot (<code>null</code>
         *            permitted).
         * @param pass
         *            the pass index.
         */
        @Override
        public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info,
                XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item,
                CrosshairState crosshairState, int pass) {
            PlotOrientation orientation = plot.getOrientation();

            // get the data point...
            double x = dataset.getXValue(series, item);
            double y = dataset.getYValue(series, item);
            double z = 0.97d;

            RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
            RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
            double transX = domainAxis.valueToJava2D(x, dataArea, domainAxisLocation);
            double transY = rangeAxis.valueToJava2D(y, dataArea, rangeAxisLocation);

            double transDomain = 0.0;
            double transRange = 0.0;

            double zero1 = domainAxis.valueToJava2D(0.0, dataArea, domainAxisLocation);
            double zero2 = rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation);
            transDomain = domainAxis.valueToJava2D(z, dataArea, domainAxisLocation) - zero1;
            transRange = zero2 - rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation);

            transDomain = Math.abs(transDomain);
            transRange = Math.abs(transRange);

            RoundRectangle2D.Double rect = new RoundRectangle2D.Double(transX - (transDomain / 2.0),
                    transY - (transRange / 2.0), transDomain, transRange, 10, 10);

            g2.setPaint(this.getItemPaint(series, item));
            g2.fill(rect);
            g2.setStroke(new BasicStroke(1.0f));
            g2.setPaint(Color.LIGHT_GRAY);
            g2.draw(rect);

            this.updateCrosshairValues(crosshairState, x, y, transX, transY, orientation);
        }
    }

    private static class CustomAxis extends NumberAxis {

        /**
         *
         */
        public CustomAxis(String labelName) {
            super(labelName);
        }

        /**
         * Calculates the positions of the tick labels for the axis, storing the
         * results in the tick label list (ready for drawing).
         *
         * @param g2
         *            the graphics device.
         * @param state
         *            the axis state.
         * @param dataArea
         *            the area in which the plot should be drawn.
         * @param edge
         *            the location of the axis.
         *
         * @return A list of ticks.
         *
         */
        @Override
        public List<NumberTick> refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea,
                RectangleEdge edge) {
            List<NumberTick> result = new java.util.ArrayList<NumberTick>();
            if (RectangleEdge.isTopOrBottom(edge)) {
                result = this.refreshTicksHorizontal(g2, dataArea, edge);
            } else if (RectangleEdge.isLeftOrRight(edge)) {
                result = this.refreshTicksVertical(g2, dataArea, edge);
            }

            int size = result.size();
            NumberTick first = result.get(0);
            NumberTick last = result.get(size - 1);

            NumberTick newFirst = new NumberTick(0, "", first.getTextAnchor(), first.getRotationAnchor(),
                    first.getAngle());

            NumberTick newLast = new NumberTick(0, "", last.getTextAnchor(), last.getRotationAnchor(),
                    last.getAngle());

            result.set(0, newFirst);
            result.set(size - 1, newLast);
            return result;
        }
    }

    private class CustomTickUnit extends NumberTickUnit {

        /**
         *
         */
        public CustomTickUnit(double size) {
            super(size);
        }

        /**
         * Converts a value to a string.
         *
         * @param value
         *            the value.
         *
         * @return The formatted string.
         */
        @Override
        public String valueToString(double value) {
            if (MatrixChart.this.legendFormatMode == Chart.LegendFormat.POW10) {
                return (((value >= 0) && (value < MatrixChart.this.legendValues.length))
                        ? ("" + MatrixChart.formatDataSize(MatrixChart.this.legendValues[(int) value], 1000))
                        : "");
            } else if (MatrixChart.this.legendFormatMode == Chart.LegendFormat.POW2) {
                return (((value >= 0) && (value < MatrixChart.this.legendValues.length))
                        ? ("" + MatrixChart.formatDataSize(MatrixChart.this.legendValues[(int) value], 1024))
                        : "");
            } else {
                return (((value >= 0) && (value < MatrixChart.this.legendValues.length))
                        ? ("" + MatrixChart.this.legendValues[(int) value])
                        : "");
            }
        }
    }

    private static String formatDataSize(int value, int base) {
        int selector = 0;
        double v = value;
        while (v > base) {
            selector++;
            v /= base;
        }
        switch (selector) {
        case 1:
            return "" + TimIt.df.format(v) + "K";
        case 2:
            return "" + TimIt.df.format(v) + "M";
        case 3:
            return "" + TimIt.df.format(v) + "G";
        case 4:
            return "" + TimIt.df.format(v) + "T";
        case 5:
            return "" + TimIt.df.format(v) + "P";
        default:
            return "" + value;
        }
    }
}