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 *******************************************************************************/
11package org.eclipse.test.performance.ui;
12
13import java.text.NumberFormat;
14import java.util.ArrayList;
15import java.util.List;
16
17import org.eclipse.swt.SWT;
18import org.eclipse.swt.graphics.Color;
19import org.eclipse.swt.graphics.GC;
20import org.eclipse.swt.graphics.Point;
21import org.eclipse.swt.widgets.Display;
22
23public class BarGraph {
24
25	private static final int MARGIN= 5; // margin on all four sides
26	private static final int BARHEIGHT= 8; // height of bar
27	private static final int GAP= 10; // gap between bars
28	private static final int TGAP= 5; // gap between lines and labels
29
30	private static final boolean NO_SCALE= true; //
31
32	// if NO_SCALE is true the following values are used:
33	private static final double RATIO= 0.6; // fraction of width reserved for bar graph; needs tweaking
34	private static final int FROM_END= 20; // a break (//) is shown this far from end of bar
35	private static final int SLANT= 8; // slant of break
36	private static final int GAP2= 5; // width of break
37
38	private StringBuffer fAreaBuffer;
39
40	private static class BarItem {
41
42		String title;
43		double value;
44		String url;
45		String slowdownExpected;
46		boolean significant;
47
48		BarItem(String t, double[] stats, String u, String slow, boolean sig) {
49			this.title= t;
50			this.value= stats[0]==0 ? 0 : -stats[0] * 100;
51			this.url= u;
52			this.slowdownExpected= slow;
53			this.significant= sig;
54		}
55	}
56
57	private String fTitle;
58	private List fItems;
59
60	BarGraph(String title) {
61		this.fTitle= title;
62		this.fItems= new ArrayList();
63	}
64
65	public void addItem(String name, double[] stats, String url, String slow, boolean significant) {
66		this.fItems.add(new BarItem(name, stats, url, slow, significant));
67	}
68
69	public int getHeight() {
70		int n= this.fItems.size();
71		int textHeight= 16;
72		int titleHeight= 0;
73		if (this.fTitle != null)
74			titleHeight= textHeight + GAP;
75		return MARGIN + titleHeight + n * (GAP + BARHEIGHT) + GAP + textHeight + MARGIN;
76	}
77
78	public void paint(Display display, int width, int height, GC gc) {
79
80		NumberFormat nf= NumberFormat.getInstance();
81
82		BarItem[] bars= (BarItem[]) this.fItems.toArray(new BarItem[this.fItems.size()]);
83
84		// draw white background
85		Color bg= display.getSystemColor(SWT.COLOR_WHITE);
86		gc.setBackground(bg);
87		gc.fillRectangle(0, 0, width, height);
88
89		// determine the widths of the bar and the label areas
90		int w;
91		if (NO_SCALE) {
92			// we use a fixed width
93			w= (int) (RATIO * width);
94		} else {
95			// we calculate the max width
96			int maxNameLength= 0;
97			for (int i= 0; i < bars.length; i++) {
98				Point es= gc.stringExtent(bars[i].title);
99				maxNameLength= Math.max(maxNameLength, es.x);
100			}
101			w= width - maxNameLength - TGAP - 2 * MARGIN;
102		}
103
104		Color fg= display.getSystemColor(SWT.COLOR_BLACK);
105
106		int vstart= 0; // start rows here
107		if (this.fTitle != null) {
108			vstart= gc.stringExtent(this.fTitle).y + GAP;
109			gc.drawString(this.fTitle, MARGIN, MARGIN, true); // draw title left aligned
110		}
111
112		int center= MARGIN + w / 2;
113		int w2= w / 2 - gc.stringExtent("-999.9").x - TGAP; // reserve space //$NON-NLS-1$
114
115		// determine maximum of values
116		double max= 0.0;
117		for (int i= 0; i < bars.length; i++)
118			max= Math.max(max, Math.abs(bars[i].value));
119
120		double d;
121		if (NO_SCALE) {
122			d= 25;
123			max= 125;
124		} else {
125			if (max > 400.0) {
126				d= 200;
127			} else if (max > 200.0) {
128				d= 100;
129			} else if (max > 100.0) {
130				d= 50;
131			} else if (max > 50) {
132				d= 25;
133			} else if (max > 25) {
134				d= 10;
135			} else if (max > 10) {
136				d= 5;
137			} else if (max > 5) {
138				d= 2.5;
139			} else {
140				d= 1.0;
141			}
142		}
143
144		// draw striped background
145		int y= MARGIN + vstart;
146		Color lightblue= new Color(display, 237, 243, 254);
147		gc.setBackground(lightblue);
148		for (int i= 0; i < bars.length; i++)
149			if (i % 2 == 0)
150				gc.fillRectangle(0, y + i * (BARHEIGHT + GAP), width, BARHEIGHT + GAP);
151
152		// draw grid
153		int yy= y + bars.length * (BARHEIGHT + GAP);
154		gc.drawLine(center, y, center, yy + TGAP);
155		Color grey= display.getSystemColor(SWT.COLOR_GRAY);
156		for (int i= 1; d * i < max; i++) {
157
158			double xx= d * i;
159			int x= (int) ((xx / max) * w2);
160
161			gc.setForeground(grey);
162			gc.drawLine(center - x, y, center - x, yy + TGAP);
163			gc.drawLine(center + x, y, center + x, yy + TGAP);
164
165			gc.setForeground(fg);
166
167			String s3= nf.format(-xx) + "%"; //$NON-NLS-1$
168			Point es3= gc.stringExtent(s3);
169			gc.drawString(s3, center - x - es3.x / 2, yy + TGAP, true);
170
171			String s4= nf.format(xx) + "%"; //$NON-NLS-1$
172			Point es4= gc.stringExtent(s4);
173			gc.drawString(s4, center + x - es4.x / 2, yy + TGAP, true);
174		}
175		gc.drawLine(0, yy, w, yy);
176
177		nf.setMaximumFractionDigits(1);
178
179		// link color
180		Color blue= display.getSystemColor(SWT.COLOR_BLUE);
181		// draw bars
182//		Color green= display.getSystemColor(SWT.COLOR_GREEN);
183//		Color red= display.getSystemColor(SWT.COLOR_RED);
184		Color green = new Color(display, 95, 191, 95);
185		Color red = new Color(display, 225, 50, 50);
186		Color gray= display.getSystemColor(SWT.COLOR_GRAY);
187		Color yellow= display.getSystemColor(SWT.COLOR_YELLOW);
188		Color white= display.getSystemColor(SWT.COLOR_WHITE);
189		for (int i= 0; i < bars.length; i++) {
190
191			BarItem bar= bars[i];
192			double delta = bar.value;
193			double orgDelta= delta;
194
195			boolean clamped= false;
196			if (NO_SCALE) {
197				if (delta > max) {
198					delta= max;
199					clamped= true;
200				} else if (delta < -max) {
201					delta= -max;
202					clamped= true;
203				}
204			}
205
206			int barLength= (int) (delta / max * w2);
207
208			if (delta < 0) {
209				if (bar.slowdownExpected != null) {
210					gc.setBackground(gray);
211				} else if (!bar.significant) {
212					gc.setBackground(yellow);
213				} else  {
214					gc.setBackground(red);
215				}
216			} else if (!bar.significant) {
217				gc.setBackground(yellow);
218			} else {
219				gc.setBackground(green);
220			}
221
222			if (barLength > 0) {
223				gc.fillRectangle(center, y + (GAP / 2), barLength, BARHEIGHT);
224				gc.drawRectangle(center, y + (GAP / 2), barLength, BARHEIGHT);
225			} else if (barLength < 0) {
226				gc.fillRectangle(center+barLength, y + (GAP / 2), -barLength, BARHEIGHT);
227				gc.drawRectangle(center+barLength, y + (GAP / 2), -barLength, BARHEIGHT);
228			}
229
230			if (clamped) {
231
232				int h2= (BARHEIGHT + GAP);
233				int x= center + barLength;
234				if (barLength > 0)
235					x-= FROM_END;
236				else
237					x+= FROM_END - GAP2 - SLANT;
238				int[] pts= new int[] { x, y + h2 - 1, x + SLANT, y + 1, x + SLANT + GAP2, y + 1, x + GAP2, y + h2 - 1};
239				if (i % 2 == 0)
240					gc.setBackground(lightblue);
241				else
242					gc.setBackground(white);
243				gc.fillPolygon(pts);
244				gc.drawLine(pts[0], pts[1], pts[2], pts[3]);
245				gc.drawLine(pts[4], pts[5], pts[6], pts[7]);
246			}
247
248			String label= nf.format(orgDelta);
249			Point labelExtent= gc.stringExtent(label);
250			int labelxpos= center + barLength;
251			int labelvpos= y + (BARHEIGHT + GAP - labelExtent.y) / 2;
252			if (orgDelta > 0.0) {
253				gc.drawString(label, labelxpos + TGAP, labelvpos, true);
254			} else {
255				gc.drawString(label, labelxpos - TGAP - labelExtent.x, labelvpos, true);
256			}
257
258			int x= MARGIN + w + TGAP;
259			String title= bar.title;
260			boolean hasURL= bar.url != null;
261			Color oldfg= gc.getForeground();
262			if (hasURL) {
263				gc.setForeground(blue);
264				Point e= gc.stringExtent(title);
265				gc.drawLine(x, labelvpos + e.y - 1, x + e.x, labelvpos + e.y - 1);
266			}
267			gc.drawString(title, x, labelvpos, true);
268			if (hasURL)
269				gc.setForeground(oldfg);
270
271			int y0= y;
272			y+= BARHEIGHT + GAP;
273
274			if (hasURL) {
275				if (this.fAreaBuffer == null)
276					this.fAreaBuffer= new StringBuffer();
277				this.fAreaBuffer.append("		echo '<area shape=\"RECT\" coords=\"0," + y0 + ',' + width + ',' + y + "\" href=\"" + bar.url + "\">';\n");
278			}
279		}
280
281		lightblue.dispose();
282		red.dispose();
283		green.dispose();
284	}
285
286	public String getAreas() {
287		if (this.fAreaBuffer != null) {
288			String s= this.fAreaBuffer.toString();
289			this.fAreaBuffer= null;
290			return s;
291		}
292		return null;
293	}
294}
295