sysperfana/memspyext/com.nokia.s60tools.swmtanalyser/src/com/nokia/s60tools/swmtanalyser/ui/graphs/GraphsUtils.java
changeset 7 8e12a575a9b5
equal deleted inserted replaced
6:f65f740e69f9 7:8e12a575a9b5
       
     1 /*
       
     2  * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3  * All rights reserved.
       
     4  * This component and the accompanying materials are made available
       
     5  * under the terms of "Eclipse Public License v1.0"
       
     6  * which accompanies this distribution, and is available
       
     7  * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8  *
       
     9  * Initial Contributors:
       
    10  * Nokia Corporation - initial contribution.
       
    11  *
       
    12  * Contributors:
       
    13  *
       
    14  * Description: 
       
    15  *
       
    16  */
       
    17 package com.nokia.s60tools.swmtanalyser.ui.graphs;
       
    18 
       
    19 import java.util.Arrays;
       
    20 import java.util.List;
       
    21 import java.util.Random;
       
    22 
       
    23 import org.eclipse.draw2d.Graphics;
       
    24 import org.eclipse.swt.SWT;
       
    25 import org.eclipse.swt.graphics.Color;
       
    26 import org.eclipse.swt.graphics.Font;
       
    27 import org.eclipse.swt.graphics.GC;
       
    28 import org.eclipse.swt.graphics.Image;
       
    29 import org.eclipse.swt.graphics.ImageData;
       
    30 import org.eclipse.swt.graphics.ImageLoader;
       
    31 import org.eclipse.swt.graphics.Point;
       
    32 import org.eclipse.swt.widgets.Composite;
       
    33 import org.eclipse.swt.widgets.Display;
       
    34 import org.eclipse.swt.widgets.FileDialog;
       
    35 
       
    36 import com.nokia.s60tools.swmtanalyser.ui.graphs.GenericGraph.EventTypes;
       
    37 import com.nokia.s60tools.util.debug.DbgUtility;
       
    38 
       
    39 /**
       
    40  * Graph utilities common to all graph types.
       
    41  */
       
    42 public class GraphsUtils {
       
    43 
       
    44 	//
       
    45 	// Constants
       
    46 	// 
       
    47 
       
    48 	/**
       
    49 	 * Constant for no marker possibility.
       
    50 	 */
       
    51 	private static final int NO_MARKER = -1;
       
    52 
       
    53 	/**
       
    54 	 * Possible marker sizes from smallest to biggest value.
       
    55 	 */
       
    56 	private static final int[] MARKER_SIZES = { 4, 6, 8 };
       
    57 
       
    58 	/**
       
    59 	 * Default marker size.
       
    60 	 */
       
    61 	private static final int DEFAULT_MARKER = MARKER_SIZES[2];
       
    62 
       
    63 	/**
       
    64 	 * Array of event names used to map the event names into corresponding event
       
    65 	 * enumerators.
       
    66 	 * 
       
    67 	 * @see com.nokia.s60tools.swmtanalyser.ui.graphs.GenericGraph.EventTypes
       
    68 	 */
       
    69 	private static final String[] EVENT_NAMES_ARR = { "Global data size",
       
    70 			"Non heap chunk size", "Disk used", "Disk total", "No of Files",
       
    71 			"Max size", "Heap size", "Heap allocated space", "Heap free space",
       
    72 			"Heap allocated cell count", "Heap free cell count", "Free slack",
       
    73 			"No of PS Handles", "RAM used", "RAM total", "System Data" };
       
    74 
       
    75 	/**
       
    76 	 * Percentage of byte count to be graphed that is added extra reserve in
       
    77 	 * order to show markers appropriately.
       
    78 	 */
       
    79 	private static final int MARKER_MARGIN_PERCENTAGE = 5;
       
    80 
       
    81 	//
       
    82 	// Symbolic name constants for different byte units use in setting Y-axis
       
    83 	// value level according given maximum byte value
       
    84 	// 
       
    85 	private static final int KILOBYTE = 1024;
       
    86 	private static final int MEGABYTE = 1024 * 1024;
       
    87 
       
    88 	//
       
    89 	// Members
       
    90 	// 
       
    91 	private static String imageFilename;
       
    92 	private static Composite parentComposite;
       
    93 
       
    94 	/**
       
    95 	 * Maps event names into corresponding enumerator type
       
    96 	 * 
       
    97 	 * @param event
       
    98 	 *            event name
       
    99 	 * @return enumerator constant corresponding to the given event
       
   100 	 */
       
   101 	public static EventTypes getMappedEvent(String eventName) {
       
   102 		int index = Arrays.asList(EVENT_NAMES_ARR).indexOf(eventName);
       
   103 		GenericGraph.EventTypes eventType = null;
       
   104 
       
   105 		switch (index) {
       
   106 		case 0:
       
   107 			eventType = GenericGraph.EventTypes.GLOBAL_DATA_SIZE;
       
   108 			break;
       
   109 		case 1:
       
   110 			eventType = GenericGraph.EventTypes.NON_HEAP_CHUNK_SIZE;
       
   111 			break;
       
   112 		case 2:
       
   113 			eventType = GenericGraph.EventTypes.DISK_USED_SIZE;
       
   114 			break;
       
   115 		case 3:
       
   116 			eventType = GenericGraph.EventTypes.DISK_TOTAL_SIZE;
       
   117 			break;
       
   118 		case 4:
       
   119 			eventType = GenericGraph.EventTypes.NO_OF_FILES;
       
   120 			break;
       
   121 		case 5:
       
   122 			eventType = GenericGraph.EventTypes.MAX_HEAP_SIZE;
       
   123 			break;
       
   124 		case 6:
       
   125 			eventType = GenericGraph.EventTypes.HEAP_SIZE;
       
   126 			break;
       
   127 		case 7:
       
   128 			eventType = GenericGraph.EventTypes.HEAP_ALLOC_SPACE;
       
   129 			break;
       
   130 		case 8:
       
   131 			eventType = GenericGraph.EventTypes.HEAP_FREE_SPACE;
       
   132 			break;
       
   133 		case 9:
       
   134 			eventType = GenericGraph.EventTypes.HEAP_ALLOC_CELL_COUNT;
       
   135 			break;
       
   136 		case 10:
       
   137 			eventType = GenericGraph.EventTypes.HEAP_FREE_CELL_COUNT;
       
   138 			break;
       
   139 		case 11:
       
   140 			eventType = GenericGraph.EventTypes.HEAP_FREE_SLACK;
       
   141 			break;
       
   142 		case 12:
       
   143 			eventType = GenericGraph.EventTypes.NO_OF_PSHANDLES;
       
   144 			break;
       
   145 		case 13:
       
   146 			eventType = GenericGraph.EventTypes.RAM_USED;
       
   147 			break;
       
   148 		case 14:
       
   149 			eventType = GenericGraph.EventTypes.RAM_TOTAL;
       
   150 			break;
       
   151 		case 15:
       
   152 			eventType = GenericGraph.EventTypes.SYSTEM_DATA;
       
   153 			break;
       
   154 		}
       
   155 
       
   156 		return eventType;
       
   157 	}
       
   158 
       
   159 	/**
       
   160 	 * Get next scale when zooming in or out
       
   161 	 * @param scale
       
   162 	 * @param bigger give <code>true</code> when zooming out and <code>false</code> when zooming in.
       
   163 	 * @return next scale
       
   164 	 */
       
   165 	public static double nextScale(double scale, boolean bigger) {
       
   166 		double logScale = Math.log10(scale);
       
   167 		double floorLogScale = Math.floor(Math.log10(scale));
       
   168 		double mostSignificantDigit = Math.rint(Math.pow(10,
       
   169 				(logScale - floorLogScale)));
       
   170 		double powerOfTen = Math.pow(10, floorLogScale);
       
   171 
       
   172 		if (bigger) {
       
   173 			if (mostSignificantDigit < 2) {
       
   174 				mostSignificantDigit = 2;
       
   175 			} else if (mostSignificantDigit < 5) {
       
   176 				mostSignificantDigit = 5;
       
   177 			} else {
       
   178 				mostSignificantDigit = 10;
       
   179 			}
       
   180 		} else {
       
   181 			if (mostSignificantDigit > 5) {
       
   182 				mostSignificantDigit = 5;
       
   183 			} else if (mostSignificantDigit > 2) {
       
   184 				mostSignificantDigit = 2;
       
   185 			} else if (mostSignificantDigit > 1) {
       
   186 				mostSignificantDigit = 1;
       
   187 			} else {
       
   188 				mostSignificantDigit = 0.5;
       
   189 			}
       
   190 		}
       
   191 
       
   192 		double result = mostSignificantDigit * powerOfTen;
       
   193 
       
   194 		if (result < 0.1)
       
   195 			result = 0.1;
       
   196 
       
   197 		return result;
       
   198 	}
       
   199 
       
   200 	/**
       
   201 	 * Save the given composite as an image to local file system.
       
   202 	 * 
       
   203 	 * @param parent
       
   204 	 */
       
   205 	public static void saveGraph(Composite parent) {
       
   206 		parentComposite = parent;
       
   207 		FileDialog dlg = new FileDialog(Display.getCurrent().getActiveShell(),
       
   208 				SWT.SAVE);
       
   209 		dlg.setFilterExtensions(new String[] { "*.bmp", "*.png", "*.jpeg" });
       
   210 		imageFilename = dlg.open();
       
   211 		if (imageFilename == null)
       
   212 			return;
       
   213 
       
   214 		Runnable p = new Runnable() {
       
   215 			public void run() {
       
   216 				GC gc = new GC(parentComposite);
       
   217 				Image image = new Image(Display.getCurrent(), parentComposite
       
   218 						.getClientArea().width,
       
   219 						parentComposite.getClientArea().height);
       
   220 				parentComposite.setFocus();
       
   221 				gc.copyArea(image, 0, 0);
       
   222 				gc.dispose();
       
   223 				ImageData data = image.getImageData();
       
   224 				ImageLoader loader = new ImageLoader();
       
   225 				loader.data = new ImageData[] { data };
       
   226 				if (imageFilename != null)
       
   227 					loader.save(imageFilename, SWT.IMAGE_BMP);
       
   228 				image.dispose();
       
   229 			}
       
   230 		};
       
   231 		Display.getDefault().timerExec(500, p);
       
   232 	}
       
   233 
       
   234 	/**
       
   235 	 * Generate random color.
       
   236 	 * 
       
   237 	 * @return a random color
       
   238 	 */
       
   239 	public static Color getRandomColor() {
       
   240 		Random rand = new Random();
       
   241 		int r = rand.nextInt(255);
       
   242 		int g = rand.nextInt(255);
       
   243 		int b = rand.nextInt(255);
       
   244 		return new Color(Display.getCurrent(), r, g, b);
       
   245 	}
       
   246 
       
   247 	/**
       
   248 	 * Creates an image and writes the given text vertically on the image. This
       
   249 	 * is used to represent the Y-axis names in the Analysis tab and Graphed
       
   250 	 * events -graphs. Those graphs show double Y-axis and therefore layout
       
   251 	 * differs from single Y-axis situation.
       
   252 	 * 
       
   253 	 * @param axisLabelName
       
   254 	 *            name of the label
       
   255 	 * @return vertical axis label image
       
   256 	 */
       
   257 	public static Image getDoubleYAxisVerticalLabel(String axisLabelName) {
       
   258 		return getVerticalLabel(axisLabelName, 90, 18, 10);
       
   259 	}
       
   260 
       
   261 	/**
       
   262 	 * Creates an image and writes the given text vertically on the image with
       
   263 	 * given coordinates and font size.
       
   264 	 * 
       
   265 	 * @param axisLabelName
       
   266 	 *            name of the label
       
   267 	 * @param x
       
   268 	 *            x-coordinate
       
   269 	 * @param y
       
   270 	 *            y-coordinate
       
   271 	 * @param fontSize
       
   272 	 *            font size
       
   273 	 * @return vertical axis label image
       
   274 	 */
       
   275 	public static Image getVerticalLabel(String axisLabelName, int x, int y,
       
   276 			int fontSize) {
       
   277 		final Image image = new Image(Display.getDefault(), x, y);
       
   278 		GC gc = new GC(image);
       
   279 		Font font = new Font(Display.getDefault(), Display.getDefault()
       
   280 				.getSystemFont().getFontData()[0].getName(), fontSize, SWT.BOLD);
       
   281 		gc.setFont(font);
       
   282 		gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
       
   283 		gc.fillRectangle(0, 0, 90, 15);
       
   284 		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
       
   285 		gc.drawText(axisLabelName + " ->", 0, 0, true);
       
   286 		font.dispose();
       
   287 		gc.dispose();
       
   288 		return image;
       
   289 	}
       
   290 
       
   291 	/**
       
   292 	 * Draws markers to data points with current foreground color and marker
       
   293 	 * size.
       
   294 	 * 
       
   295 	 * @param graphics
       
   296 	 *            Graphics context
       
   297 	 * @param points
       
   298 	 *            Data point array in format [ X0, Y0, X1, Y1, ... ]
       
   299 	 */
       
   300 	private static void drawMarkers(Graphics graphics, int[] points,
       
   301 			int markerSize) {
       
   302 		Color backgroundColor = graphics.getBackgroundColor();
       
   303 		graphics.setBackgroundColor(graphics.getForegroundColor());
       
   304 		for (int j = 0; j < points.length; j += 2) {
       
   305 			int width = markerSize;
       
   306 			int height = width;
       
   307 			int x = points[j] - (width / 2);
       
   308 			int y = points[j + 1] - (width / 2);
       
   309 			graphics.fillRectangle(x, y, width, height);
       
   310 		}
       
   311 		graphics.setBackgroundColor(backgroundColor);
       
   312 	}
       
   313 
       
   314 	/**
       
   315 	 * Resolving minimum space between subsequent X-coordinate data points. This
       
   316 	 * method expects that there are at least 2 x,y pairs in the array
       
   317 	 * 
       
   318 	 * @param points
       
   319 	 *            Data point array in format [ X0, Y0, X1, Y1, ... ]
       
   320 	 * @return minimum space between subsequent X-coordinate data points
       
   321 	 */
       
   322 	private static int resolveMinumumXDelta(int[] points) {
       
   323 		int minDelta = points[2] - points[0];
       
   324 		for (int i = 4; i < points.length; i += 2) {
       
   325 			int delta = points[i] - points[i - 2];
       
   326 			if (delta < minDelta) {
       
   327 				minDelta = delta;
       
   328 			}
       
   329 		}
       
   330 		return minDelta;
       
   331 	}
       
   332 
       
   333 	/**
       
   334 	 * Draws markers to data points with current foreground color. Markers drawn
       
   335 	 * only if there is enough room in X-axis to draw them between all
       
   336 	 * individual data points.
       
   337 	 * 
       
   338 	 * @param graphics
       
   339 	 *            Graphics context
       
   340 	 * @param points
       
   341 	 *            Data point array in format [ X0, Y0, X1, Y1, ... ]
       
   342 	 */
       
   343 	public static void drawMarkers(Graphics graphics, int[] points) {
       
   344 		int markerSize = NO_MARKER; // By default not drawing markers if there
       
   345 									// is no room for them
       
   346 		// Checking deltas only if there is more than single point to draw
       
   347 		if (points.length > 2) {
       
   348 			// Resolving minimum space between subsequent X-coordinate data
       
   349 			// points
       
   350 			int minDelta = resolveMinumumXDelta(points);
       
   351 			// Resolving if there is at all need to draw markers
       
   352 			for (int i = MARKER_SIZES.length - 1; i >= 0; i--) {
       
   353 				int size = MARKER_SIZES[i];
       
   354 				if (size < minDelta) {
       
   355 					markerSize = size; // Using this size in drawing
       
   356 					break;
       
   357 				}
       
   358 			}
       
   359 			//			DbgUtility.println(DbgUtility.PRIORITY_LOOP, "minDelta: " + minDelta); //$NON-NLS-1$
       
   360 		} else {
       
   361 			// Using default marker in case there
       
   362 			markerSize = DEFAULT_MARKER;
       
   363 		}
       
   364 		//		DbgUtility.println(DbgUtility.PRIORITY_LOOP, "markerSize: " + markerSize); //$NON-NLS-1$
       
   365 		// Drawing markers
       
   366 		if (markerSize != NO_MARKER) {
       
   367 			drawMarkers(graphics, points, markerSize);
       
   368 		}
       
   369 	}
       
   370 
       
   371 	/**
       
   372 	 * Gets nearest Y-legend bytes label from the given bytes
       
   373 	 * 
       
   374 	 * @param bytes
       
   375 	 *            bytes number
       
   376 	 * @return nearest Y-legend bytes label from the given bytes
       
   377 	 */
       
   378 	static public int prettyMaxBytes(int bytes) {
       
   379 
       
   380 		// Adding some margin that makes possible to show also markers
       
   381 		int byteMarginForMarkers = (int) Math
       
   382 				.ceil((MARKER_MARGIN_PERCENTAGE / 100.0) * bytes);
       
   383 		bytes = bytes + byteMarginForMarkers;
       
   384 
       
   385 		//		DbgUtility.println(DbgUtility.PRIORITY_LOOP, "prettyMaxBytes/bytes: " + bytes); //$NON-NLS-1$
       
   386 		//		DbgUtility.println(DbgUtility.PRIORITY_LOOP, "byteMarginForMarkers: " + byteMarginForMarkers); //$NON-NLS-1$
       
   387 
       
   388 		// Before 10 KB limit using byte units are used Y-axis legend and
       
   389 		// therefore thousand is used as checkpoint limit instead if KILOBYTE
       
   390 		final int thousand = 1000;
       
   391 
       
   392 		if (bytes < thousand)
       
   393 			bytes = thousand;
       
   394 		else if (bytes < 10 * thousand)
       
   395 			bytes = 10 * thousand;
       
   396 		else if (bytes < 20 * KILOBYTE)
       
   397 			bytes = 20 * KILOBYTE;
       
   398 		else if (bytes < 30 * KILOBYTE)
       
   399 			bytes = 30 * KILOBYTE;
       
   400 		else if (bytes < 50 * KILOBYTE)
       
   401 			bytes = 50 * KILOBYTE;
       
   402 		else if (bytes < 100 * KILOBYTE)
       
   403 			bytes = 100 * KILOBYTE;
       
   404 		else if (bytes < 150 * KILOBYTE)
       
   405 			bytes = 150 * KILOBYTE;
       
   406 		else if (bytes < 200 * KILOBYTE)
       
   407 			bytes = 200 * KILOBYTE;
       
   408 		else if (bytes < 300 * KILOBYTE)
       
   409 			bytes = 300 * KILOBYTE;
       
   410 		else if (bytes < 400 * KILOBYTE)
       
   411 			bytes = 400 * KILOBYTE;
       
   412 		else if (bytes < 500 * KILOBYTE)
       
   413 			bytes = 500 * KILOBYTE;
       
   414 		else if (bytes < 600 * KILOBYTE)
       
   415 			bytes = 600 * KILOBYTE;
       
   416 		else if (bytes < 700 * KILOBYTE)
       
   417 			bytes = 700 * KILOBYTE;
       
   418 		else if (bytes < 800 * KILOBYTE)
       
   419 			bytes = 800 * KILOBYTE;
       
   420 		else if (bytes < 900 * KILOBYTE)
       
   421 			bytes = 900 * KILOBYTE;
       
   422 		else if (bytes < 1000 * KILOBYTE)
       
   423 			bytes = 1000 * KILOBYTE;
       
   424 		else if (bytes < 1 * MEGABYTE)
       
   425 			bytes = 1 * MEGABYTE;
       
   426 		else if (bytes < 2 * MEGABYTE)
       
   427 			bytes = 2 * MEGABYTE;
       
   428 		else if (bytes < 3 * MEGABYTE)
       
   429 			bytes = 3 * MEGABYTE;
       
   430 		else if (bytes < 5 * MEGABYTE)
       
   431 			bytes = 5 * MEGABYTE;
       
   432 		else if (bytes < 10 * MEGABYTE)
       
   433 			bytes = 10 * MEGABYTE;
       
   434 		else if (bytes < 20 * MEGABYTE)
       
   435 			bytes = 20 * MEGABYTE;
       
   436 		else if (bytes < 30 * MEGABYTE)
       
   437 			bytes = 30 * MEGABYTE;
       
   438 		else if (bytes < 50 * MEGABYTE)
       
   439 			bytes = 50 * MEGABYTE;
       
   440 		else if (bytes < 100 * MEGABYTE)
       
   441 			bytes = 100 * MEGABYTE;
       
   442 		else if (bytes < 200 * MEGABYTE)
       
   443 			bytes = 200 * MEGABYTE;
       
   444 		else if (bytes < 300 * MEGABYTE)
       
   445 			bytes = 300 * MEGABYTE;
       
   446 		else if (bytes < 500 * MEGABYTE)
       
   447 			bytes = 500 * MEGABYTE;
       
   448 		else
       
   449 			bytes = ((bytes + 1024 * MEGABYTE - 1) / (1024 * MEGABYTE))
       
   450 					* (1024 * MEGABYTE);
       
   451 
       
   452 		return bytes;
       
   453 	}
       
   454 
       
   455 	/**
       
   456 	 * Converts list of <code>Point</code> objects into integer array.
       
   457 	 * 
       
   458 	 * @param pointsList
       
   459 	 *            list of point objects
       
   460 	 * @return points converted in 1-dimensional integer array.
       
   461 	 */
       
   462 	public static int[] convertPointListToIntArray(List<Point> pointsList) {
       
   463 		int[] integerArray = new int[pointsList.size() * 2];
       
   464 		for (int i = 0, j = 0; i < pointsList.size(); i++, j += 2) {
       
   465 			Point pnt = pointsList.get(i);
       
   466 			integerArray[j] = pnt.x;
       
   467 			integerArray[(j + 1)] = pnt.y;
       
   468 		}
       
   469 		return integerArray;
       
   470 	}
       
   471 
       
   472 	/**
       
   473 	 * Gets nearest Y-legend count value label from the given input count value.
       
   474 	 * Maximum count value handled is 999*100 i.e. 99900 counts.
       
   475 	 * 
       
   476 	 * @param inputCountValue
       
   477 	 *            bytes input count value
       
   478 	 * @return nearest Y-legend count value label from the given input count
       
   479 	 *         value.
       
   480 	 * @return
       
   481 	 */
       
   482 	static public int roundToNearestNumber(int inputCountValue) {
       
   483 		int tempCount = inputCountValue;
       
   484 
       
   485 		// Adding some safe margin for making sure that all data points with
       
   486 		// markers are drawn appropriately
       
   487 		int countMarginForMarkers = (int) Math
       
   488 				.ceil((MARKER_MARGIN_PERCENTAGE / 100.0) * tempCount);
       
   489 		tempCount = tempCount + countMarginForMarkers;
       
   490 
       
   491 		DbgUtility.println(DbgUtility.PRIORITY_OPERATION,
       
   492 				"roundToNearestNumber/count: " + tempCount); //$NON-NLS-1$
       
   493 		DbgUtility.println(DbgUtility.PRIORITY_OPERATION,
       
   494 				"countMarginForMarkers: " + countMarginForMarkers); //$NON-NLS-1$
       
   495 
       
   496 		if (tempCount < 10)
       
   497 			tempCount = 10;
       
   498 		else if (tempCount < 50)
       
   499 			tempCount = 50;
       
   500 		else if (tempCount < 100)
       
   501 			tempCount = 100;
       
   502 		else {
       
   503 			for (int i = 2; i < 1000; i++) {
       
   504 				if (tempCount < (i * 100)) {
       
   505 					tempCount = i * 100;
       
   506 					break;
       
   507 				}
       
   508 			}
       
   509 		}
       
   510 
       
   511 		return tempCount;
       
   512 	}
       
   513 
       
   514 	/**
       
   515 	 * Builds int array from Integer List object
       
   516 	 * 
       
   517 	 * @param solidsList
       
   518 	 *            Integer list
       
   519 	 * @return int array
       
   520 	 */
       
   521 	public static int[] CreateIntArrayFromIntegerList(List<Integer> solidsList) {
       
   522 		int[] solidPts = new int[solidsList.size()];
       
   523 		for (int j = 0; j < solidsList.size(); j++) {
       
   524 			solidPts[j] = solidsList.get(j);
       
   525 		}
       
   526 		return solidPts;
       
   527 	}
       
   528 
       
   529 }