--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/memspyext/com.nokia.s60tools.swmtanalyser/src/com/nokia/s60tools/swmtanalyser/ui/graphs/GraphsUtils.java Wed Apr 21 20:01:08 2010 +0300
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:
+ *
+ */
+package com.nokia.s60tools.swmtanalyser.ui.graphs;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+
+import com.nokia.s60tools.swmtanalyser.ui.graphs.GenericGraph.EventTypes;
+import com.nokia.s60tools.util.debug.DbgUtility;
+
+/**
+ * Graph utilities common to all graph types.
+ */
+public class GraphsUtils {
+
+ //
+ // Constants
+ //
+
+ /**
+ * Constant for no marker possibility.
+ */
+ private static final int NO_MARKER = -1;
+
+ /**
+ * Possible marker sizes from smallest to biggest value.
+ */
+ private static final int[] MARKER_SIZES = { 4, 6, 8 };
+
+ /**
+ * Default marker size.
+ */
+ private static final int DEFAULT_MARKER = MARKER_SIZES[2];
+
+ /**
+ * Array of event names used to map the event names into corresponding event
+ * enumerators.
+ *
+ * @see com.nokia.s60tools.swmtanalyser.ui.graphs.GenericGraph.EventTypes
+ */
+ private static final String[] EVENT_NAMES_ARR = { "Global data size",
+ "Non heap chunk size", "Disk used", "Disk total", "No of Files",
+ "Max size", "Heap size", "Heap allocated space", "Heap free space",
+ "Heap allocated cell count", "Heap free cell count", "Free slack",
+ "No of PS Handles", "RAM used", "RAM total", "System Data" };
+
+ /**
+ * Percentage of byte count to be graphed that is added extra reserve in
+ * order to show markers appropriately.
+ */
+ private static final int MARKER_MARGIN_PERCENTAGE = 5;
+
+ //
+ // Symbolic name constants for different byte units use in setting Y-axis
+ // value level according given maximum byte value
+ //
+ private static final int KILOBYTE = 1024;
+ private static final int MEGABYTE = 1024 * 1024;
+
+ //
+ // Members
+ //
+ private static String imageFilename;
+ private static Composite parentComposite;
+
+ /**
+ * Maps event names into corresponding enumerator type
+ *
+ * @param event
+ * event name
+ * @return enumerator constant corresponding to the given event
+ */
+ public static EventTypes getMappedEvent(String eventName) {
+ int index = Arrays.asList(EVENT_NAMES_ARR).indexOf(eventName);
+ GenericGraph.EventTypes eventType = null;
+
+ switch (index) {
+ case 0:
+ eventType = GenericGraph.EventTypes.GLOBAL_DATA_SIZE;
+ break;
+ case 1:
+ eventType = GenericGraph.EventTypes.NON_HEAP_CHUNK_SIZE;
+ break;
+ case 2:
+ eventType = GenericGraph.EventTypes.DISK_USED_SIZE;
+ break;
+ case 3:
+ eventType = GenericGraph.EventTypes.DISK_TOTAL_SIZE;
+ break;
+ case 4:
+ eventType = GenericGraph.EventTypes.NO_OF_FILES;
+ break;
+ case 5:
+ eventType = GenericGraph.EventTypes.MAX_HEAP_SIZE;
+ break;
+ case 6:
+ eventType = GenericGraph.EventTypes.HEAP_SIZE;
+ break;
+ case 7:
+ eventType = GenericGraph.EventTypes.HEAP_ALLOC_SPACE;
+ break;
+ case 8:
+ eventType = GenericGraph.EventTypes.HEAP_FREE_SPACE;
+ break;
+ case 9:
+ eventType = GenericGraph.EventTypes.HEAP_ALLOC_CELL_COUNT;
+ break;
+ case 10:
+ eventType = GenericGraph.EventTypes.HEAP_FREE_CELL_COUNT;
+ break;
+ case 11:
+ eventType = GenericGraph.EventTypes.HEAP_FREE_SLACK;
+ break;
+ case 12:
+ eventType = GenericGraph.EventTypes.NO_OF_PSHANDLES;
+ break;
+ case 13:
+ eventType = GenericGraph.EventTypes.RAM_USED;
+ break;
+ case 14:
+ eventType = GenericGraph.EventTypes.RAM_TOTAL;
+ break;
+ case 15:
+ eventType = GenericGraph.EventTypes.SYSTEM_DATA;
+ break;
+ }
+
+ return eventType;
+ }
+
+ /**
+ * Get next scale when zooming in or out
+ * @param scale
+ * @param bigger give <code>true</code> when zooming out and <code>false</code> when zooming in.
+ * @return next scale
+ */
+ public static double nextScale(double scale, boolean bigger) {
+ double logScale = Math.log10(scale);
+ double floorLogScale = Math.floor(Math.log10(scale));
+ double mostSignificantDigit = Math.rint(Math.pow(10,
+ (logScale - floorLogScale)));
+ double powerOfTen = Math.pow(10, floorLogScale);
+
+ if (bigger) {
+ if (mostSignificantDigit < 2) {
+ mostSignificantDigit = 2;
+ } else if (mostSignificantDigit < 5) {
+ mostSignificantDigit = 5;
+ } else {
+ mostSignificantDigit = 10;
+ }
+ } else {
+ if (mostSignificantDigit > 5) {
+ mostSignificantDigit = 5;
+ } else if (mostSignificantDigit > 2) {
+ mostSignificantDigit = 2;
+ } else if (mostSignificantDigit > 1) {
+ mostSignificantDigit = 1;
+ } else {
+ mostSignificantDigit = 0.5;
+ }
+ }
+
+ double result = mostSignificantDigit * powerOfTen;
+
+ if (result < 0.1)
+ result = 0.1;
+
+ return result;
+ }
+
+ /**
+ * Save the given composite as an image to local file system.
+ *
+ * @param parent
+ */
+ public static void saveGraph(Composite parent) {
+ parentComposite = parent;
+ FileDialog dlg = new FileDialog(Display.getCurrent().getActiveShell(),
+ SWT.SAVE);
+ dlg.setFilterExtensions(new String[] { "*.bmp", "*.png", "*.jpeg" });
+ imageFilename = dlg.open();
+ if (imageFilename == null)
+ return;
+
+ Runnable p = new Runnable() {
+ public void run() {
+ GC gc = new GC(parentComposite);
+ Image image = new Image(Display.getCurrent(), parentComposite
+ .getClientArea().width,
+ parentComposite.getClientArea().height);
+ parentComposite.setFocus();
+ gc.copyArea(image, 0, 0);
+ gc.dispose();
+ ImageData data = image.getImageData();
+ ImageLoader loader = new ImageLoader();
+ loader.data = new ImageData[] { data };
+ if (imageFilename != null)
+ loader.save(imageFilename, SWT.IMAGE_BMP);
+ image.dispose();
+ }
+ };
+ Display.getDefault().timerExec(500, p);
+ }
+
+ /**
+ * Generate random color.
+ *
+ * @return a random color
+ */
+ public static Color getRandomColor() {
+ Random rand = new Random();
+ int r = rand.nextInt(255);
+ int g = rand.nextInt(255);
+ int b = rand.nextInt(255);
+ return new Color(Display.getCurrent(), r, g, b);
+ }
+
+ /**
+ * Creates an image and writes the given text vertically on the image. This
+ * is used to represent the Y-axis names in the Analysis tab and Graphed
+ * events -graphs. Those graphs show double Y-axis and therefore layout
+ * differs from single Y-axis situation.
+ *
+ * @param axisLabelName
+ * name of the label
+ * @return vertical axis label image
+ */
+ public static Image getDoubleYAxisVerticalLabel(String axisLabelName) {
+ return getVerticalLabel(axisLabelName, 90, 18, 10);
+ }
+
+ /**
+ * Creates an image and writes the given text vertically on the image with
+ * given coordinates and font size.
+ *
+ * @param axisLabelName
+ * name of the label
+ * @param x
+ * x-coordinate
+ * @param y
+ * y-coordinate
+ * @param fontSize
+ * font size
+ * @return vertical axis label image
+ */
+ public static Image getVerticalLabel(String axisLabelName, int x, int y,
+ int fontSize) {
+ final Image image = new Image(Display.getDefault(), x, y);
+ GC gc = new GC(image);
+ Font font = new Font(Display.getDefault(), Display.getDefault()
+ .getSystemFont().getFontData()[0].getName(), fontSize, SWT.BOLD);
+ gc.setFont(font);
+ gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ gc.fillRectangle(0, 0, 90, 15);
+ gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+ gc.drawText(axisLabelName + " ->", 0, 0, true);
+ font.dispose();
+ gc.dispose();
+ return image;
+ }
+
+ /**
+ * Draws markers to data points with current foreground color and marker
+ * size.
+ *
+ * @param graphics
+ * Graphics context
+ * @param points
+ * Data point array in format [ X0, Y0, X1, Y1, ... ]
+ */
+ private static void drawMarkers(Graphics graphics, int[] points,
+ int markerSize) {
+ Color backgroundColor = graphics.getBackgroundColor();
+ graphics.setBackgroundColor(graphics.getForegroundColor());
+ for (int j = 0; j < points.length; j += 2) {
+ int width = markerSize;
+ int height = width;
+ int x = points[j] - (width / 2);
+ int y = points[j + 1] - (width / 2);
+ graphics.fillRectangle(x, y, width, height);
+ }
+ graphics.setBackgroundColor(backgroundColor);
+ }
+
+ /**
+ * Resolving minimum space between subsequent X-coordinate data points. This
+ * method expects that there are at least 2 x,y pairs in the array
+ *
+ * @param points
+ * Data point array in format [ X0, Y0, X1, Y1, ... ]
+ * @return minimum space between subsequent X-coordinate data points
+ */
+ private static int resolveMinumumXDelta(int[] points) {
+ int minDelta = points[2] - points[0];
+ for (int i = 4; i < points.length; i += 2) {
+ int delta = points[i] - points[i - 2];
+ if (delta < minDelta) {
+ minDelta = delta;
+ }
+ }
+ return minDelta;
+ }
+
+ /**
+ * Draws markers to data points with current foreground color. Markers drawn
+ * only if there is enough room in X-axis to draw them between all
+ * individual data points.
+ *
+ * @param graphics
+ * Graphics context
+ * @param points
+ * Data point array in format [ X0, Y0, X1, Y1, ... ]
+ */
+ public static void drawMarkers(Graphics graphics, int[] points) {
+ int markerSize = NO_MARKER; // By default not drawing markers if there
+ // is no room for them
+ // Checking deltas only if there is more than single point to draw
+ if (points.length > 2) {
+ // Resolving minimum space between subsequent X-coordinate data
+ // points
+ int minDelta = resolveMinumumXDelta(points);
+ // Resolving if there is at all need to draw markers
+ for (int i = MARKER_SIZES.length - 1; i >= 0; i--) {
+ int size = MARKER_SIZES[i];
+ if (size < minDelta) {
+ markerSize = size; // Using this size in drawing
+ break;
+ }
+ }
+ // DbgUtility.println(DbgUtility.PRIORITY_LOOP, "minDelta: " + minDelta); //$NON-NLS-1$
+ } else {
+ // Using default marker in case there
+ markerSize = DEFAULT_MARKER;
+ }
+ // DbgUtility.println(DbgUtility.PRIORITY_LOOP, "markerSize: " + markerSize); //$NON-NLS-1$
+ // Drawing markers
+ if (markerSize != NO_MARKER) {
+ drawMarkers(graphics, points, markerSize);
+ }
+ }
+
+ /**
+ * Gets nearest Y-legend bytes label from the given bytes
+ *
+ * @param bytes
+ * bytes number
+ * @return nearest Y-legend bytes label from the given bytes
+ */
+ static public int prettyMaxBytes(int bytes) {
+
+ // Adding some margin that makes possible to show also markers
+ int byteMarginForMarkers = (int) Math
+ .ceil((MARKER_MARGIN_PERCENTAGE / 100.0) * bytes);
+ bytes = bytes + byteMarginForMarkers;
+
+ // DbgUtility.println(DbgUtility.PRIORITY_LOOP, "prettyMaxBytes/bytes: " + bytes); //$NON-NLS-1$
+ // DbgUtility.println(DbgUtility.PRIORITY_LOOP, "byteMarginForMarkers: " + byteMarginForMarkers); //$NON-NLS-1$
+
+ // Before 10 KB limit using byte units are used Y-axis legend and
+ // therefore thousand is used as checkpoint limit instead if KILOBYTE
+ final int thousand = 1000;
+
+ if (bytes < thousand)
+ bytes = thousand;
+ else if (bytes < 10 * thousand)
+ bytes = 10 * thousand;
+ else if (bytes < 20 * KILOBYTE)
+ bytes = 20 * KILOBYTE;
+ else if (bytes < 30 * KILOBYTE)
+ bytes = 30 * KILOBYTE;
+ else if (bytes < 50 * KILOBYTE)
+ bytes = 50 * KILOBYTE;
+ else if (bytes < 100 * KILOBYTE)
+ bytes = 100 * KILOBYTE;
+ else if (bytes < 150 * KILOBYTE)
+ bytes = 150 * KILOBYTE;
+ else if (bytes < 200 * KILOBYTE)
+ bytes = 200 * KILOBYTE;
+ else if (bytes < 300 * KILOBYTE)
+ bytes = 300 * KILOBYTE;
+ else if (bytes < 400 * KILOBYTE)
+ bytes = 400 * KILOBYTE;
+ else if (bytes < 500 * KILOBYTE)
+ bytes = 500 * KILOBYTE;
+ else if (bytes < 600 * KILOBYTE)
+ bytes = 600 * KILOBYTE;
+ else if (bytes < 700 * KILOBYTE)
+ bytes = 700 * KILOBYTE;
+ else if (bytes < 800 * KILOBYTE)
+ bytes = 800 * KILOBYTE;
+ else if (bytes < 900 * KILOBYTE)
+ bytes = 900 * KILOBYTE;
+ else if (bytes < 1000 * KILOBYTE)
+ bytes = 1000 * KILOBYTE;
+ else if (bytes < 1 * MEGABYTE)
+ bytes = 1 * MEGABYTE;
+ else if (bytes < 2 * MEGABYTE)
+ bytes = 2 * MEGABYTE;
+ else if (bytes < 3 * MEGABYTE)
+ bytes = 3 * MEGABYTE;
+ else if (bytes < 5 * MEGABYTE)
+ bytes = 5 * MEGABYTE;
+ else if (bytes < 10 * MEGABYTE)
+ bytes = 10 * MEGABYTE;
+ else if (bytes < 20 * MEGABYTE)
+ bytes = 20 * MEGABYTE;
+ else if (bytes < 30 * MEGABYTE)
+ bytes = 30 * MEGABYTE;
+ else if (bytes < 50 * MEGABYTE)
+ bytes = 50 * MEGABYTE;
+ else if (bytes < 100 * MEGABYTE)
+ bytes = 100 * MEGABYTE;
+ else if (bytes < 200 * MEGABYTE)
+ bytes = 200 * MEGABYTE;
+ else if (bytes < 300 * MEGABYTE)
+ bytes = 300 * MEGABYTE;
+ else if (bytes < 500 * MEGABYTE)
+ bytes = 500 * MEGABYTE;
+ else
+ bytes = ((bytes + 1024 * MEGABYTE - 1) / (1024 * MEGABYTE))
+ * (1024 * MEGABYTE);
+
+ return bytes;
+ }
+
+ /**
+ * Converts list of <code>Point</code> objects into integer array.
+ *
+ * @param pointsList
+ * list of point objects
+ * @return points converted in 1-dimensional integer array.
+ */
+ public static int[] convertPointListToIntArray(List<Point> pointsList) {
+ int[] integerArray = new int[pointsList.size() * 2];
+ for (int i = 0, j = 0; i < pointsList.size(); i++, j += 2) {
+ Point pnt = pointsList.get(i);
+ integerArray[j] = pnt.x;
+ integerArray[(j + 1)] = pnt.y;
+ }
+ return integerArray;
+ }
+
+ /**
+ * Gets nearest Y-legend count value label from the given input count value.
+ * Maximum count value handled is 999*100 i.e. 99900 counts.
+ *
+ * @param inputCountValue
+ * bytes input count value
+ * @return nearest Y-legend count value label from the given input count
+ * value.
+ * @return
+ */
+ static public int roundToNearestNumber(int inputCountValue) {
+ int tempCount = inputCountValue;
+
+ // Adding some safe margin for making sure that all data points with
+ // markers are drawn appropriately
+ int countMarginForMarkers = (int) Math
+ .ceil((MARKER_MARGIN_PERCENTAGE / 100.0) * tempCount);
+ tempCount = tempCount + countMarginForMarkers;
+
+ DbgUtility.println(DbgUtility.PRIORITY_OPERATION,
+ "roundToNearestNumber/count: " + tempCount); //$NON-NLS-1$
+ DbgUtility.println(DbgUtility.PRIORITY_OPERATION,
+ "countMarginForMarkers: " + countMarginForMarkers); //$NON-NLS-1$
+
+ if (tempCount < 10)
+ tempCount = 10;
+ else if (tempCount < 50)
+ tempCount = 50;
+ else if (tempCount < 100)
+ tempCount = 100;
+ else {
+ for (int i = 2; i < 1000; i++) {
+ if (tempCount < (i * 100)) {
+ tempCount = i * 100;
+ break;
+ }
+ }
+ }
+
+ return tempCount;
+ }
+
+ /**
+ * Builds int array from Integer List object
+ *
+ * @param solidsList
+ * Integer list
+ * @return int array
+ */
+ public static int[] CreateIntArrayFromIntegerList(List<Integer> solidsList) {
+ int[] solidPts = new int[solidsList.size()];
+ for (int j = 0; j < solidsList.size(); j++) {
+ solidPts[j] = solidsList.get(j);
+ }
+ return solidPts;
+ }
+
+}