diff -r f65f740e69f9 -r 8e12a575a9b5 sysperfana/memspyext/com.nokia.s60tools.swmtanalyser/src/com/nokia/s60tools/swmtanalyser/ui/graphs/GraphsUtils.java --- /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 true when zooming out and false 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 Point objects into integer array. + * + * @param pointsList + * list of point objects + * @return points converted in 1-dimensional integer array. + */ + public static int[] convertPointListToIntArray(List 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 solidsList) { + int[] solidPts = new int[solidsList.size()]; + for (int j = 0; j < solidsList.size(); j++) { + solidPts[j] = solidsList.get(j); + } + return solidPts; + } + +}