All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mgmtp.perfload.perfalyzer.reportpreparation.PlotCreator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-2015 mgm technology partners GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mgmtp.perfload.perfalyzer.reportpreparation;

import com.mgmtp.perfload.perfalyzer.annotations.IntFormat;
import com.mgmtp.perfload.perfalyzer.util.Marker;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.LineBorder;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.DefaultDrawingSupplier;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleInsets;

import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.List;
import java.util.ResourceBundle;

import static com.google.common.io.Files.createParentDirs;
import static com.mgmtp.perfload.perfalyzer.util.PerfAlyzerUtils.selectDisplayData;

/**
 * @author rnaegele
 */
@Singleton
public class PlotCreator {

	private static final StandardChartTheme CHART_THEME = new StandardChartTheme("JFree");

	static {
		CHART_THEME.setDrawingSupplier(new PerfAlyzerDrawingSupplier());
	}

	public static enum AxisType {
		LINEAR,
		LOGARITHMIC
	}

	public static enum RendererType {
		LINES {
			@Override
			XYItemRenderer createRenderer() {
				return new XYLineAndShapeRenderer(true, false);
			}
		},
		SHAPES {
			@Override
			XYItemRenderer createRenderer() {
				return new XYLineAndShapeRenderer(false, true);
			}
		},
		LINES_AND_SHAPES {
			@Override
			XYItemRenderer createRenderer() {
				return new XYLineAndShapeRenderer(true, true);
			}
		};

		abstract XYItemRenderer createRenderer();
	}

	public static enum ChartDimensions {
		DEFAULT(640, 400),
		LARGE(1115, 800),
		WIDE(1115, 400);

		private final int width;
		private final int height;

		private ChartDimensions(final int width, final int height) {
			this.width = width;
			this.height = height;
		}

		/**
		 * @return the width
		 */
		public int getWidth() {
			return width;
		}

		/**
		 * @return the height
		 */
		public int getHeight() {
			return height;
		}
	}

	private final NumberFormat numberFormat;
	private final ResourceBundle resourceBundle;
	private final List displayDataList;
	private final List markers;

	@Inject
	public PlotCreator(@IntFormat final NumberFormat numberFormat, final ResourceBundle resourceBundle,
			final List displayDataList, final List markers) {
		this.numberFormat = numberFormat;
		this.resourceBundle = resourceBundle;
		this.displayDataList = displayDataList;
		this.markers = markers;
	}

	public JFreeChart createPlot(final AxisType xAxisType, final AxisType yAxisType, final RendererType rendererType,
			final DisplayData displayData, final DataRange dataRange, boolean showMarkers, final NumberDataSet... dataSets) {

		NumberAxis xAxis = createAxis(xAxisType, resourceBundle.getString(displayData.getUnitX()));

		XYPlot plot;
		if (dataSets.length == 1) {
			NumberAxis yAxis = createAxis(yAxisType, resourceBundle.getString(displayData.getUnitY()));
			plot = new XYPlot(dataSets[0], xAxis, yAxis, rendererType.createRenderer());
		} else {
			CombinedDomainXYPlot combinedPlot = new CombinedDomainXYPlot(xAxis);
			for (int i = 0; i < dataSets.length; ++i) {
				NumberDataSet dataSet = dataSets[i];
				// no range for y-axis!
				NumberAxis yAxis = createAxis(yAxisType, resourceBundle.getString(displayData.getUnitYList().get(i)));
				XYPlot subPlot = new XYPlot(dataSet, null, yAxis, rendererType.createRenderer());
				combinedPlot.add(subPlot);
				formatPlot(subPlot);
			}
			plot = combinedPlot;
		}

		JFreeChart chart = new JFreeChart(plot);
		CHART_THEME.apply(chart);

		formatPlot(plot);

		if (showMarkers) {
			for (Marker marker : markers) {
				IntervalMarker im = new IntervalMarker(marker.getLeftMillis() / 1000L, marker.getRightMillis() / 1000L);
				im.setLabel(marker.getName());
				im.setLabelFont(new Font("Sans Serif", Font.ITALIC | Font.BOLD, 14));
				im.setLabelAnchor(RectangleAnchor.TOP);
				im.setLabelOffset(new RectangleInsets(8d, 0d, 0d, 0d));
				im.setLabelPaint(Color.BLACK);
				im.setAlpha(.2f);
				im.setPaint(Color.WHITE);
				im.setOutlinePaint(Color.BLACK);
				im.setOutlineStroke(new BasicStroke(1.0f));
				plot.addDomainMarker(im, Layer.BACKGROUND);
			}
		}

		LegendTitle legend = chart.getLegend();
		legend.setBackgroundPaint(new Color(229, 229, 229));
		legend.setFrame(new LineBorder(new Color(213, 213, 213), new BasicStroke(1.0f), legend.getFrame().getInsets()));

		// only for non-logarithmic axes
		// range must be set after plot is created, otherwise nothing is drawn
		if (dataRange != null && !xAxisType.equals(AxisType.LOGARITHMIC)) {
			xAxis.setRange(dataRange.getLowerSeconds(), dataRange.getUpperSeconds());
		}

		return chart;
	}

	private NumberAxis createAxis(final AxisType axisType, final String axisLabel) {
		NumberAxis numberAxis =
				axisType.equals(AxisType.LOGARITHMIC) ? new LogarithmicAxis(axisLabel) : new NumberAxis(axisLabel);
		numberAxis.setNumberFormatOverride((NumberFormat) numberFormat.clone());
		return numberAxis;
	}

	private void formatPlot(final Plot plot) {
		plot.setBackgroundPaint(new GradientPaint(0f, 0f, new Color(213, 213, 213), 0f, 0f, new Color(156, 156, 156)));
		plot.setOutlinePaint(new Color(221, 221, 221));
	}

	public void writePlotFile(final File file, final AxisType xAxisType, final AxisType yAxisType,
			final RendererType rendererType, final DisplayData displayData, final ChartDimensions dimensions,
			final DataRange dataRange, boolean showMarkers, final NumberDataSet... dataSets) throws IOException {
		createParentDirs(file);
		for (NumberDataSet dataSet : dataSets) {
			dataSet.sortSeries();
		}
		JFreeChart chart = createPlot(xAxisType, yAxisType, rendererType, displayData, dataRange, showMarkers, dataSets);
		BufferedImage chartImage = chart.createBufferedImage(dimensions.getWidth(), dimensions.getHeight());
		ImageIO.write(chartImage, "png", file);
	}

	public void writePlotFile(final File file, final AxisType xAxisType, final AxisType yAxisType,
			final RendererType rendererType, final ChartDimensions dimensions, final DataRange dataRange, boolean showMarkers,
			final NumberDataSet... dataSets) throws IOException {
		DisplayData displayData = selectDisplayData(file, displayDataList);
		writePlotFile(file, xAxisType, yAxisType, rendererType, displayData, dimensions, dataRange, showMarkers, dataSets);
	}

	static class PerfAlyzerDrawingSupplier extends DefaultDrawingSupplier {

		public PerfAlyzerDrawingSupplier() {
			super(DEFAULT_PAINT_SEQUENCE, DEFAULT_FILL_PAINT_SEQUENCE, DEFAULT_OUTLINE_PAINT_SEQUENCE,
					new Stroke[]{new BasicStroke(1.5f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL)},
					DEFAULT_OUTLINE_STROKE_SEQUENCE, createStandardSeriesShapes());
		}

		/**
		 * Creates an array of standard shapes to display for the items in series on charts.
		 *
		 * @return The array of shapes.
		 */
		public static Shape[] createStandardSeriesShapes() {

			Shape[] result = new Shape[10];

			double size = 4.0;
			double delta = size / 2.0;

			// square
			result[0] = new Rectangle2D.Double(-delta, -delta, size, size);
			// circle
			result[1] = new Ellipse2D.Double(-delta, -delta, size, size);

			// up-pointing triangle
			int[] xpoints = intArray(0.0, delta, -delta);
			int[] ypoints = intArray(-delta, delta, delta);
			result[2] = new Polygon(xpoints, ypoints, 3);

			// diamond
			xpoints = intArray(0.0, delta, 0.0, -delta);
			ypoints = intArray(-delta, 0.0, delta, 0.0);
			result[3] = new Polygon(xpoints, ypoints, 4);

			// horizontal rectangle
			result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2);

			// down-pointing triangle
			xpoints = intArray(-delta, +delta, 0.0);
			ypoints = intArray(-delta, -delta, delta);
			result[5] = new Polygon(xpoints, ypoints, 3);

			// horizontal ellipse
			result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2);

			// right-pointing triangle
			xpoints = intArray(-delta, delta, -delta);
			ypoints = intArray(-delta, 0.0, delta);
			result[7] = new Polygon(xpoints, ypoints, 3);

			// vertical rectangle
			result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size);

			// left-pointing triangle
			xpoints = intArray(-delta, delta, delta);
			ypoints = intArray(0.0, -delta, +delta);
			result[9] = new Polygon(xpoints, ypoints, 3);

			return result;

		}

		/**
		 * Helper method to avoid lots of explicit casts in getShape(). Returns an array containing
		 * the provided doubles cast to ints.
		 *
		 * @param a
		 * 		x
		 * @param b
		 * 		y
		 * @param c
		 * 		z
		 * @return int[3] with converted params.
		 */
		private static int[] intArray(final double a, final double b, final double c) {
			return new int[]{(int) a, (int) b, (int) c};
		}

		/**
		 * Helper method to avoid lots of explicit casts in getShape(). Returns an array containing
		 * the provided doubles cast to ints.
		 *
		 * @param a
		 * 		x
		 * @param b
		 * 		y
		 * @param c
		 * 		z
		 * @param d
		 * 		t
		 * @return int[4] with converted params.
		 */
		private static int[] intArray(final double a, final double b, final double c, final double d) {
			return new int[]{(int) a, (int) b, (int) c, (int) d};
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy