Home | History | Annotate | Download | only in ui
      1 /*******************************************************************************
      2  * Copyright (c) 2000, 2009 IBM Corporation and others.
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *     IBM Corporation - initial API and implementation
     10  *******************************************************************************/
     11 package org.eclipse.test.performance.ui;
     12 
     13 import java.io.File;
     14 import java.io.PrintStream;
     15 import java.text.NumberFormat;
     16 import java.util.ArrayList;
     17 import java.util.HashMap;
     18 import java.util.Iterator;
     19 import java.util.List;
     20 import java.util.Map;
     21 
     22 import org.eclipse.swt.SWT;
     23 import org.eclipse.swt.graphics.Color;
     24 import org.eclipse.swt.graphics.Font;
     25 import org.eclipse.swt.graphics.FontData;
     26 import org.eclipse.swt.graphics.GC;
     27 import org.eclipse.swt.graphics.Image;
     28 import org.eclipse.swt.graphics.ImageData;
     29 import org.eclipse.swt.graphics.Point;
     30 import org.eclipse.swt.graphics.Rectangle;
     31 import org.eclipse.swt.graphics.Resource;
     32 import org.eclipse.swt.widgets.Display;
     33 import org.eclipse.test.internal.performance.results.db.BuildResults;
     34 import org.eclipse.test.internal.performance.results.db.ConfigResults;
     35 import org.eclipse.test.internal.performance.results.db.DB_Results;
     36 import org.eclipse.test.internal.performance.results.db.ScenarioResults;
     37 import org.eclipse.test.internal.performance.results.utils.Util;
     38 
     39 /**
     40  * Abstract class to build graph with bars
     41  */
     42 public class FingerPrintGraph {
     43 
     44 	// Sizes
     45 	static final int MARGIN= 5; // margin on all four sides
     46 	static final int BAR_HEIGHT= 6; // height of bar
     47 	static final int GAP= 10; // gap between bars
     48 	static final int TGAP= 5; // gap between lines and labels
     49 	static final int LINE_HEIGHT = 2*BAR_HEIGHT + GAP;
     50 
     51 	// fraction of width reserved for bar graph
     52 	static final double RATIO= 0.6;
     53 
     54 	// Formatting constants
     55 	static final NumberFormat NUMBER_FORMAT;
     56 	static {
     57 		NUMBER_FORMAT = NumberFormat.getInstance();
     58 		NUMBER_FORMAT.setMaximumFractionDigits(1);
     59 	}
     60 
     61 	// Graphic constants
     62 	static final Display DEFAULT_DISPLAY = Display.getDefault();
     63 	static final Color BLACK= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLACK);
     64 	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
     65 	static final Color GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GREEN);
     66 	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
     67 	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
     68 	static final Color DARK_GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GRAY);
     69 	static final Color YELLOW = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_YELLOW);
     70 	static final Color WHITE = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_WHITE);
     71 
     72 	// Bar graph kinds
     73 	static final int NO_TIME = 0; // i.e. percentage
     74 	static final int TIME_LINEAR = 1;
     75 	static final int TIME_LOG = 2;
     76 	static final int[] SUPPORTED_GRAPHS = {
     77 //		NO_TIME,
     78 		TIME_LINEAR,
     79 		TIME_LOG,
     80 	};
     81 
     82 	// Graphic fields
     83 	GC gc;
     84 	Image image;
     85 	int imageWidth;
     86 	int imageHeight;
     87 	int graphWidth;
     88 	int graphHeight;
     89 	Map resources = new HashMap();
     90 
     91 	// Data fields
     92 	int count = 0;
     93 	ConfigResults[] results = new ConfigResults[10];
     94 	BarGraphArea[] areas;
     95 
     96 	// Values
     97 	double maxValue = 0.0;
     98 	double minValue = Double.MAX_VALUE;
     99 
    100 	// File info
    101 	File outputDir;
    102 	String imageName;
    103 	private final String defaultDimName = DB_Results.getDefaultDimension().getName();
    104 
    105 	/*
    106 	 * Member class defining a bar graph area.
    107 	 * This area applies to a configuration results and is made of several zones.
    108 	 */
    109 	class BarGraphArea {
    110 		List zones;
    111 		private ConfigResults configResults;
    112 
    113 		/*
    114 		 * Member class defining a zone inside a bar graph area.
    115 		 * Typically made of a rectangle and an associated text used as tooltip.
    116 		 */
    117 		class AreaZone {
    118 			Rectangle zone;
    119 			String title;
    120 
    121 			AreaZone(Rectangle zone, String tooltip) {
    122 	            super();
    123 	            this.zone = zone;
    124 	            this.title = tooltip;
    125             }
    126 
    127 			void print(String url, PrintStream stream) {
    128 				stream.print("		echo '<area shape=\"RECT\"");
    129 				if (this.title != null) {
    130 					stream.print(" title=\""+this.title+"\"");
    131 				}
    132 				stream.print("coords=\"");
    133 				stream.print(this.zone.x);
    134 				stream.print(',');
    135 				stream.print(this.zone.y);
    136 				stream.print(',');
    137 				stream.print(this.zone.x+this.zone.width);
    138 				stream.print(',');
    139 				stream.print(this.zone.y+this.zone.height);
    140 				stream.print('"');
    141 				if (url != null) {
    142 					stream.print(" href=\"");
    143 					stream.print(url);
    144 					stream.print('"');
    145 				}
    146 				stream.print(">';\n");
    147 			}
    148 		}
    149 
    150 		 BarGraphArea(ConfigResults results) {
    151 			this.configResults = results;
    152 			this.zones = new ArrayList();
    153         }
    154 
    155 		void print(PrintStream stream) {
    156 			String url = this.configResults.getName() + "/" + ((ScenarioResults) this.configResults.getParent()).getFileName() + ".html";
    157 			int size = this.zones.size();
    158 			for (int i=0; i<size; i++) {
    159 				AreaZone zone = (AreaZone) this.zones.get(i);
    160 				zone.print(url, stream);
    161 			}
    162 		}
    163 
    164 		void addArea(Rectangle rec, String tooltip) {
    165 			AreaZone zone = new AreaZone(rec, tooltip);
    166 			this.zones.add(zone);
    167 		}
    168 
    169 	}
    170 
    171 
    172 FingerPrintGraph(File dir, String fileName, int width, List results) {
    173     super();
    174     this.imageWidth = width;
    175     this.count = results.size();
    176     this.results = new ConfigResults[this.count];
    177     results.toArray(this.results);
    178     this.outputDir = dir;
    179     this.imageName = fileName;
    180 }
    181 
    182 /**
    183  */
    184 void drawBars(int kind) {
    185 
    186 	// Get/Set graphical resources
    187 	Font italicFont = (Font) this.resources.get("italicFont");
    188 	if (italicFont == null) {
    189 		String fontDataName = this.gc.getFont().getFontData()[0].toString();
    190 		FontData fdItalic = new FontData(fontDataName);
    191 		fdItalic.setStyle(SWT.ITALIC);
    192 		italicFont = new Font(DEFAULT_DISPLAY, fdItalic);
    193 		this.resources.put("italicFont", italicFont);
    194 	}
    195 	Color blueref = (Color) this.resources.get("blueref");
    196 	if (blueref == null) {
    197 		blueref = new Color(DEFAULT_DISPLAY, 200, 200, 255);
    198 		this.resources.put("blueref", blueref);
    199 	}
    200 	Color lightyellow= (Color) this.resources.get("lightyellow");
    201 	if (lightyellow == null) {
    202 		lightyellow = new Color(DEFAULT_DISPLAY, 255, 255, 160);
    203 		this.resources.put("lightyellow", lightyellow);
    204 	}
    205 	Color darkyellow= (Color) this.resources.get("darkyellow");
    206 	if (darkyellow == null) {
    207 		darkyellow = new Color(DEFAULT_DISPLAY, 160, 160, 0);
    208 		this.resources.put("darkyellow", darkyellow);
    209 	}
    210 	Color okColor= (Color) this.resources.get("lightgreen");
    211 	if (okColor == null) {
    212 		okColor = new Color(DEFAULT_DISPLAY, 95, 191, 95);
    213 		this.resources.put("lightgreen", okColor);
    214 	}
    215 	Color failureColor = (Color) this.resources.get("lightred");
    216 	if (failureColor == null) {
    217 		failureColor = new Color(DEFAULT_DISPLAY, 220, 50, 50);
    218 		this.resources.put("lightred", failureColor);
    219 	}
    220 
    221 	// Build each scenario bar graph
    222 	this.areas = new BarGraphArea[this.count];
    223 	double max = kind == TIME_LOG ? Math.log(this.maxValue) : this.maxValue;
    224 	for (int i=0, y=MARGIN; i < this.count; i++, y+=LINE_HEIGHT) {
    225 
    226 		// get builds info
    227 		ConfigResults configResults = this.results[i];
    228 		this.areas[i] = new BarGraphArea(configResults);
    229 		BarGraphArea graphArea = this.areas[i];
    230 		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
    231 		double currentValue = currentBuildResults.getValue();
    232 		double currentError = currentBuildResults.getError();
    233 		double error = configResults.getError();
    234 		boolean singleTest = Double.isNaN(error);
    235 		boolean isSignificant = singleTest || error < Utils.STANDARD_ERROR_THRESHOLD;
    236 		boolean isCommented = currentBuildResults.getComment() != null;
    237 		BuildResults baselineBuildResults = configResults.getBaselineBuildResults();
    238 		double baselineValue = baselineBuildResults.getValue();
    239 		double baselineError = baselineBuildResults.getError();
    240 
    241 		// draw baseline build bar
    242 		Color whiteref = (Color) this.resources.get("whiteref");
    243 		if (whiteref == null) {
    244 			whiteref = new Color(DEFAULT_DISPLAY, 240, 240, 248);
    245 			this.resources.put("whiteref", whiteref);
    246 		}
    247 		this.gc.setBackground(whiteref);
    248 		double baselineGraphValue = kind == TIME_LOG ? Math.log(baselineValue) : baselineValue;
    249 		int baselineBarLength= (int) (baselineGraphValue / max * this.graphWidth);
    250 		int baselineErrorLength= (int) (baselineError / max * this.graphWidth / 2);
    251 		int labelxpos = MARGIN + baselineBarLength;
    252 		if (kind == TIME_LOG || baselineErrorLength <= 1) {
    253 			this.gc.fillRectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
    254 			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
    255 			this.gc.drawRectangle(rec);
    256 			graphArea.addArea(rec, "Time for baseline build "+baselineBuildResults.getName()+": "+Util.timeString((long)baselineValue));
    257 		} else {
    258 			int wr = baselineBarLength - baselineErrorLength;
    259 			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2), wr, BAR_HEIGHT);
    260 			this.gc.fillRectangle(recValue);
    261 			this.gc.setBackground(YELLOW);
    262 			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2), baselineErrorLength*2, BAR_HEIGHT);
    263 			this.gc.fillRectangle(recError);
    264 			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength+baselineErrorLength, BAR_HEIGHT);
    265 			this.gc.drawRectangle(rec);
    266 			StringBuffer tooltip = new StringBuffer("Time for baseline build ");
    267 			tooltip.append(baselineBuildResults.getName());
    268 			tooltip.append(": ");
    269 			tooltip.append(Util.timeString((long)baselineValue));
    270 			tooltip.append(" [&#177;");
    271 			tooltip.append(Util.timeString((long)baselineError));
    272 			tooltip.append(']');
    273 			graphArea.addArea(rec, tooltip.toString());
    274 			labelxpos += baselineErrorLength;
    275 		}
    276 
    277 		// set current build bar color
    278 		if (baselineValue < currentValue) {
    279 			if (isCommented) {
    280 				this.gc.setBackground(GRAY);
    281 			} else  {
    282 				this.gc.setBackground(failureColor);
    283 			}
    284 		} else {
    285 			this.gc.setBackground(okColor);
    286 		}
    287 
    288 		// draw current build bar
    289 		double currentGraphValue = kind == TIME_LOG ? Math.log(currentValue) : currentValue;
    290 		int currentBarLength= (int) (currentGraphValue / max * this.graphWidth);
    291 		int currentErrorLength= (int) (currentError / max * this.graphWidth / 2);
    292 		if (kind == TIME_LOG || currentErrorLength <= 1) {
    293 			this.gc.fillRectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
    294 			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
    295 			this.gc.drawRectangle(rec);
    296 			String tooltip = "Time for current build "+currentBuildResults.getName()+": "+Util.timeString((long)currentValue);
    297 			if (isCommented) {
    298 				tooltip += ".		" + currentBuildResults.getComment();
    299 			}
    300 			graphArea.addArea(rec, tooltip);
    301 			if (labelxpos < (MARGIN+currentBarLength)) {
    302 				labelxpos = MARGIN + currentBarLength;
    303 			}
    304 		} else {
    305 			int wr = currentBarLength - currentErrorLength;
    306 			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, wr, BAR_HEIGHT);
    307 			this.gc.fillRectangle(recValue);
    308 			this.gc.setBackground(YELLOW);
    309 			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2) + BAR_HEIGHT, currentErrorLength*2, BAR_HEIGHT);
    310 			this.gc.fillRectangle(recError);
    311 			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength+currentErrorLength, BAR_HEIGHT);
    312 			this.gc.drawRectangle(rec);
    313 			StringBuffer tooltip = new StringBuffer("Time for current build ");
    314 			tooltip.append(currentBuildResults.getName());
    315 			tooltip.append(": ");
    316 			tooltip.append(Util.timeString((long)currentValue));
    317 			tooltip.append(" [&#177;");
    318 			tooltip.append(Util.timeString((long)currentError));
    319 			tooltip.append(']');
    320 			if (isCommented) {
    321 				tooltip.append(".		");
    322 				tooltip.append(currentBuildResults.getComment());
    323 			}
    324 			graphArea.addArea(rec, tooltip.toString());
    325 			if (labelxpos < (MARGIN+currentBarLength+currentErrorLength)) {
    326 				labelxpos = MARGIN + currentBarLength+currentErrorLength;
    327 			}
    328 		}
    329 
    330 		// set delta value style and color
    331 		boolean hasFailure = currentBuildResults.getFailure() != null;
    332 		if (hasFailure) {
    333 			if (isCommented) {
    334 				this.gc.setForeground(DARK_GRAY);
    335 			} else  {
    336 				this.gc.setForeground(RED);
    337 			}
    338 		} else {
    339 			this.gc.setForeground(BLACK);
    340 		}
    341 
    342 		// draw delta value
    343 		double delta = -configResults.getDelta();
    344 		String label = delta > 0 ? "+" : "";
    345 		label += NUMBER_FORMAT.format(delta*100) + "%";
    346 		Point labelExtent= this.gc.stringExtent(label);
    347 		int labelvpos= y + (LINE_HEIGHT - labelExtent.y) / 2;
    348 		this.gc.drawString(label, labelxpos+TGAP, labelvpos, true);
    349 		this.gc.setForeground(BLACK);
    350 		this.gc.setFont(null);
    351 		int titleStart = (int) (RATIO * this.imageWidth);
    352 		if (singleTest || !isSignificant) {
    353 			String deltaTooltip = null;
    354 			if (singleTest) {
    355 				deltaTooltip = "This test performed only one iteration; hence its reliability cannot be assessed";
    356 			} else if (!isSignificant) {
    357 				deltaTooltip = "This test has a bad reliability: error is "+NUMBER_FORMAT.format(error*100)+"% (> 3%)!";
    358 			}
    359 			Image warning = (Image) this.resources.get("warning");
    360 			int xi = labelxpos+TGAP+labelExtent.x;
    361 			this.gc.drawImage(warning, xi, labelvpos);
    362 			ImageData imageData = warning.getImageData();
    363 			// Set zones
    364 			// - first one is between end of bar and warning image beginning
    365 			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, xi-labelxpos, labelExtent.y+4);
    366 			graphArea.addArea(deltaZone, null);
    367 			// - second one is the warning image
    368 			Rectangle warningZone = new Rectangle(xi, labelvpos, imageData.width, imageData.height);
    369 			graphArea.addArea(warningZone, deltaTooltip);
    370 			// - last one is between end of the warning image and the scenario title beginning
    371 			int warningImageEnd = xi+imageData.width;
    372 			Rectangle emptyZone = new Rectangle(warningImageEnd, labelvpos, titleStart-warningImageEnd, imageData.height);
    373 			graphArea.addArea(emptyZone, deltaTooltip);
    374 		} else {
    375 			// No tooltip => delta zone is between end of bar and the scenario title beginning
    376 			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, titleStart-labelxpos, labelExtent.y+4);
    377 			graphArea.addArea(deltaZone, null);
    378 		}
    379 
    380 		// set title style
    381 		Color oldfg= this.gc.getForeground();
    382 		this.gc.setForeground(BLUE);
    383 
    384 		// draw scenario title
    385 		int x= titleStart;
    386 		ScenarioResults scenarioResults = (ScenarioResults) configResults.getParent();
    387 		String title = scenarioResults.getLabel() + " (" + this.defaultDimName + ")";
    388 		Point e= this.gc.stringExtent(title);
    389 		this.gc.drawLine(x, labelvpos + e.y - 1, x + e.x, labelvpos + e.y - 1);
    390 		this.gc.drawString(title, x, labelvpos, true);
    391 		this.gc.setForeground(oldfg);
    392 		this.gc.setFont(null);
    393 		Rectangle titleZone = new Rectangle(x, labelvpos, e.x, e.y);
    394 		graphArea.addArea(titleZone, null/*no tooltip*/);
    395 		if (!configResults.isBaselined()) {
    396 			Image warning = (Image) this.resources.get("warning");
    397 			this.gc.drawImage(warning, x+e.x, labelvpos);
    398 			ImageData imageData = warning.getImageData();
    399 			Rectangle warningZone = new Rectangle(x+e.x, labelvpos, imageData.width, imageData.height);
    400 			String titleTooltip =  "This test has no baseline result, hence use build "+configResults.getBaselineBuildName()+" for reference!";
    401 			graphArea.addArea(warningZone, titleTooltip);
    402 		}
    403 	}
    404 }
    405 
    406 void drawLinearScale() {
    407 
    408 	// Draw scale background
    409 	drawScaleBackground();
    410 
    411 	// Draw scale grid lines
    412 	int gridValue = 100;
    413 	int n = (int) (this.maxValue / gridValue);
    414 	while (n > 10) {
    415 		switch (gridValue) {
    416 			case 100:
    417 				gridValue = 200;
    418 				break;
    419 			case 200:
    420 				gridValue = 500;
    421 				break;
    422 			case 500:
    423 				gridValue = 1000;
    424 				break;
    425 			default:
    426 				gridValue += 1000;
    427 				break;
    428 		}
    429 		n = (int) (this.maxValue / gridValue);
    430 	}
    431 	int gridWidth = (int) (this.graphWidth * gridValue / this.maxValue);
    432 	int x = MARGIN;
    433 	long value = 0; // TODO use minValue instead
    434 	while (x < this.graphWidth) {
    435 
    436 		// draw line
    437 		this.gc.setForeground(GRAY);
    438 		if (x > 0) {
    439 			this.gc.setLineStyle(SWT.LINE_DOT);
    440 			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
    441 		}
    442 
    443 		// draw value
    444 		this.gc.setForeground(BLACK);
    445 		String val= Util.timeString(value);
    446 		Point point= this.gc.stringExtent(val);
    447 		this.gc.drawString(val, x - point.x / 2, this.graphHeight + TGAP, true);
    448 
    449 		// compute next grid position
    450 		x += gridWidth;
    451 		value += gridValue; // value is expressed in seconds
    452 	}
    453 	this.gc.setLineStyle(SWT.LINE_SOLID);
    454 	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
    455 }
    456 
    457 void drawLogarithmScale() {
    458 
    459 	// Draw scale background
    460 	drawScaleBackground();
    461 
    462 	// Draw scale grid lines
    463 	double max = Math.log(this.maxValue);
    464 	int gridValue = 100;
    465 	int x = MARGIN;
    466 	long value = 0; // TODO use minValue instead
    467 	while (x < this.graphWidth) {
    468 
    469 		// draw line
    470 		this.gc.setForeground(GRAY);
    471 		if (x > MARGIN) {
    472 			this.gc.setLineStyle(SWT.LINE_DOT);
    473 			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
    474 		}
    475 
    476 		// draw value
    477 		this.gc.setForeground(BLACK);
    478 		String str = Util.timeString(value);
    479 		Point point= this.gc.stringExtent(str);
    480 		this.gc.drawString(str, x - point.x / 2, this.graphHeight + TGAP, true);
    481 
    482 		// compute next grid position
    483 		value += gridValue;
    484 		int v = (int) (value / 100);
    485 		int c = 1;
    486 		while (v > 10) {
    487 			v = v / 10;
    488 			c *= 10;
    489 		}
    490 		switch (v) {
    491 			case 3:
    492 				gridValue = 200*c;
    493 				break;
    494 			case 5:
    495 				gridValue = 500*c;
    496 				break;
    497 			case 10:
    498 				gridValue = 1000*c;
    499 				break;
    500 		}
    501 		x = MARGIN + (int) (this.graphWidth * Math.log(value) / max);
    502 	}
    503 	this.gc.setLineStyle(SWT.LINE_SOLID);
    504 	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
    505 }
    506 
    507 /**
    508  * Draw the scale depending on the bar time graph kind.
    509  */
    510 void drawScale(int kind) {
    511 	switch (kind) {
    512 		case TIME_LINEAR:
    513 			drawLinearScale();
    514 			break;
    515 		case TIME_LOG:
    516 			drawLogarithmScale();
    517 			break;
    518 	}
    519 }
    520 
    521 private void drawScaleBackground() {
    522 
    523 	// Draw striped background
    524 	Color lightblue = (Color) this.resources.get("lightblue");
    525 	if (lightblue == null) {
    526 		lightblue = new Color(DEFAULT_DISPLAY, 237, 243, 254);
    527 		this.resources.put("lightblue", lightblue);
    528 	}
    529 	this.gc.setBackground(lightblue);
    530 	for (int i= 0; i<this.count; i++) {
    531 		if (i % 2 == 0) {
    532 	        this.gc.fillRectangle(0, MARGIN + i * LINE_HEIGHT, this.imageWidth, LINE_HEIGHT);
    533         }
    534 	}
    535 
    536 	// Draw bottom vertical line
    537 	int yy= MARGIN + this.count * LINE_HEIGHT;
    538 	this.gc.drawLine(MARGIN, MARGIN, MARGIN, yy + TGAP);
    539 }
    540 
    541 String getImageName(int kind) {
    542 	switch (kind) {
    543 		case TIME_LINEAR:
    544 			return this.imageName+"_linear";
    545 		case TIME_LOG:
    546 			return this.imageName+"_log";
    547 	}
    548 	return this.imageName;
    549 }
    550 
    551 void paint(int kind) {
    552 
    553 	// Set image
    554 	this.graphHeight = MARGIN + this.count * LINE_HEIGHT;
    555 	this.imageHeight = this.graphHeight + GAP + 16 + MARGIN;
    556 	this.image = new Image(DEFAULT_DISPLAY, this.imageWidth, this.imageHeight);
    557 	this.gc = new GC(this.image);
    558 
    559 	// draw white background
    560 	this.gc.setBackground(WHITE);
    561 	this.gc.fillRectangle(0, 0, this.imageWidth, this.imageHeight);
    562 
    563 	// Set widths and heights
    564 	int width= (int) (RATIO * this.imageWidth); // width for results bar
    565 	this.graphWidth= width - this.gc.stringExtent("-999.9%").x - TGAP - MARGIN; // reserve space //$NON-NLS-1$
    566 
    567 	// Get warning image width
    568 	Image warning = (Image) this.resources.get("warning");
    569 	if (warning == null) {
    570 		warning = new Image(this.gc.getDevice(), new File(this.outputDir, Utils.WARNING_OBJ).toString());
    571 		this.resources.put("warning", warning);
    572 	}
    573 	this.graphWidth -= warning.getImageData().width;
    574 
    575 	// Set maximum of values
    576 	this.maxValue = 0.0;
    577 	this.minValue = Double.MAX_VALUE;
    578 	for (int i= 0; i<this.count; i++) {
    579 		BuildResults baselineBuildResults = this.results[i].getBaselineBuildResults();
    580 		double value = baselineBuildResults.getValue();
    581 		double error = baselineBuildResults.getError();
    582 		if (!Double.isNaN(error)) value += Math.abs(error);
    583 		if (value < 1000000 && value > this.maxValue) {
    584 			this.maxValue = value;
    585 		}
    586 		if (value < this.minValue) {
    587 			this.minValue = value;
    588 		}
    589 		BuildResults currentBuildResults = this.results[i].getCurrentBuildResults();
    590 		value = currentBuildResults.getValue();
    591 		error = currentBuildResults.getError();
    592 		if (!Double.isNaN(error)) value += Math.abs(error);
    593 		if (value < 1000000 && value > this.maxValue) {
    594 			this.maxValue = value;
    595 		}
    596 		if (value < this.minValue) {
    597 			this.minValue = value;
    598 		}
    599 	}
    600 	this.minValue = 0; // do not use minValue for now...
    601 
    602 	// Draw the scale
    603 	drawScale(kind);
    604 
    605 	// Draw the bars
    606 	drawBars(kind);
    607 
    608 	// Dispose
    609 	this.gc.dispose();
    610 }
    611 
    612 /**
    613  * Create, paint and save all supported bar graphs and add the corresponding
    614  * image and map references in the given stream.
    615  *
    616  * @param stream
    617  */
    618 final public void paint(PrintStream stream) {
    619 
    620 	// Paint supported graphs
    621 	int length = SUPPORTED_GRAPHS.length;
    622 	for (int i=0; i<length; i++) {
    623 		int kind = SUPPORTED_GRAPHS[i];
    624 		paint(kind);
    625 		save(kind, stream);
    626 	}
    627 
    628 	// Dispose created graphic resources
    629 	Iterator iterator = this.resources.values().iterator();
    630 	while (iterator.hasNext()) {
    631 		Resource resource = (Resource) iterator.next();
    632 		resource.dispose();
    633 	}
    634 	this.resources.clear();
    635 }
    636 
    637 void print(int kind, PrintStream stream) {
    638 	String imgName = getImageName(kind);
    639 	stream.print("	if ($type==\"fp_type="+kind+"\") {\n");
    640 	stream.print("		echo '<img src=\"");
    641 	stream.print(imgName);
    642 	stream.print(".gif\" usemap=\"#");
    643 	stream.print(imgName);
    644 	stream.print("\" name=\"");
    645 	stream.print(imgName.substring(imgName.lastIndexOf('.')));
    646 	stream.print("\">';\n");
    647 	stream.print("		echo '<map name=\"");
    648 	stream.print(imgName);
    649 	stream.print("\">';\n");
    650 	if (this.areas != null) {
    651 		for (int i=0; i<this.count; i++) {
    652 			this.areas[i].print(stream);
    653 		}
    654 	}
    655 	stream.print("		echo '</map>';\n");
    656 	stream.print("	}\n");
    657 }
    658 
    659 void save(int kind, PrintStream stream) {
    660 	File file = new File(this.outputDir, getImageName(kind)+".gif");
    661 	Utils.saveImage(file, this.image);
    662 	if (file.exists()) {
    663 		print(kind, stream);
    664 	} else {
    665 		stream.print("<br><br>There is no fingerprint for ");
    666 		stream.print(this.imageName);
    667 		stream.print(" (kind=");
    668 		stream.print(kind);
    669 		stream.print(")<br><br>\n");
    670 	}
    671 }
    672 }
    673