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.util.*;
14
15import org.eclipse.swt.graphics.Color;
16import org.eclipse.swt.graphics.GC;
17import org.eclipse.swt.graphics.Image;
18import org.eclipse.swt.graphics.Point;
19import org.eclipse.swt.graphics.Rectangle;
20import org.eclipse.test.internal.performance.data.Dim;
21
22public class TimeLineGraph extends LineGraph{
23
24    Hashtable fItemGroups;
25
26    public TimeLineGraph (String title, Dim dim) {
27        super(title, dim);
28        this.fItemGroups=new Hashtable();
29    }
30
31    public void paint(Image im) {
32
33        Rectangle bounds= im.getBounds();
34
35        GC g= new GC(im);
36
37        Point ee= g.stringExtent(this.fTitle);
38        int titleHeight= ee.y;
39
40        double maxItem= getMaxItem();
41        double minItem= getMinItem();
42
43        int max= (int) (Math.ceil(maxItem * (maxItem < 0 ? 0.8 : 1.2)));
44        int min= (int) (Math.floor(minItem * (minItem < 0 ? 1.2 : 0.8)));
45
46        String smin= this.fDimension.getDisplayValue(min);
47        Point emin= g.stringExtent(smin);
48
49        String smax= this.fDimension.getDisplayValue(max);
50        Point emax= g.stringExtent(smax);
51
52        int labelWidth= Math.max(emin.x, emax.x) + 2;
53
54        int top= PADDING;
55        int bottom= bounds.height - titleHeight - PADDING;
56        int left= PADDING + labelWidth;
57
58        //getMostRecent
59        TimeLineGraphItem lastItem= getMostRecent(this.fItemGroups);
60        int right=bounds.width - PADDING/2;
61        if (lastItem!=null)
62        	right= bounds.width - lastItem.getSize(g).x - PADDING/2;
63
64        // draw the max and min values
65        g.drawString(smin, PADDING/2+labelWidth-emin.x, bottom-titleHeight, true);
66        g.drawString(smax, PADDING/2+labelWidth-emax.x, top, true);
67        g.drawString("TIME (not drawn to scale)", (right-left)/3+PADDING+titleHeight,bottom-PADDING+(titleHeight*2), true);
68
69        // draw the vertical and horizontal lines
70        g.drawLine(left, top, left, bottom);
71        g.drawLine(left, bottom, right, bottom);
72
73        Color oldbg= g.getBackground();
74        Color oldfg= g.getForeground();
75
76        setCoordinates(right-left,left,bottom-top,bottom,max-min);
77
78        Enumeration _enum=this.fItemGroups.elements();
79        Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
80
81        while (_enum.hasMoreElements()) {
82 			List items = (List) _enum.nextElement();
83			Object[] fItemsArray=items.toArray();
84			Arrays.sort(fItemsArray,comparator);
85			int lastx = 0;
86			int lasty = 0;
87
88			int n = fItemsArray.length;
89
90			for (int i = 0; i < n; i++) {
91				TimeLineGraphItem thisItem = (TimeLineGraphItem) fItemsArray[i];
92
93				int yposition = thisItem.y;
94				int xposition = thisItem.x;
95				g.setLineWidth(1);
96
97				g.setBackground(thisItem.color);
98				g.setForeground(thisItem.color);
99
100				if (thisItem.drawAsBaseline){
101					g.setLineWidth(0);
102					g.drawLine(xposition, yposition,right,yposition);
103					g.drawLine(left,yposition,xposition, yposition);
104    		    }
105
106				if (i > 0) // don't draw for first segment
107					g.drawLine(lastx, lasty, xposition, yposition);
108
109				g.setBackground(thisItem.color);
110				g.setForeground(thisItem.color);
111			//	g.fillOval(xposition - 2, yposition - 2, 6, 6);
112				g.fillRectangle(xposition - 2, yposition - 2, 5, 5);
113
114				if (thisItem.isSpecial)
115					g.drawRectangle(xposition -4, yposition - 4, 8, 8);
116
117				if (this.fAreaBuffer == null)
118					this.fAreaBuffer = new StringBuffer();
119
120				this.fAreaBuffer.append("\r<area shape=\"circle\" coords=\""
121						+ (xposition - 2) + ',' + (yposition - 2) + ',' + 5
122						+ " alt=\"" + thisItem.title + ": "
123						+ thisItem.description + "\"" + " title=\""
124						+ thisItem.title + ": " + thisItem.description + "\">");
125
126				int shift;
127				if (i > 0 && yposition < lasty)
128					shift = 3; // below dot
129				else
130					shift = -(2 * titleHeight + 3); // above dot
131				if (thisItem.displayDescription) {
132					g.drawString(thisItem.title, xposition + 2, yposition
133							+ shift, true);
134					g.drawString(thisItem.description, xposition + 2, yposition
135							+ shift + titleHeight, true);
136				}
137				g.setBackground(oldbg);
138				g.setForeground(oldfg);
139
140				lastx = xposition;
141				lasty = yposition;
142			}
143		}
144
145        g.dispose();
146    }
147
148    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp) {
149    	addItem(groupName, name, description, value, col, display,	timestamp,false);
150    }
151
152    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial) {
153 		addItem(groupName, name,description, value, col, display,
154 				timestamp,isSpecial,false);
155	}
156
157    public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial,boolean drawBaseline) {
158      	List items = (List) this.fItemGroups.get(groupName);
159  		if (this.fItemGroups.get(groupName) == null) {
160  			items=new ArrayList();
161  			this.fItemGroups.put(groupName, items);
162  		}
163  		items.add(new TimeLineGraphItem(name, description, value, col, display,
164  				timestamp,isSpecial,drawBaseline));
165    }
166
167    public double getMaxItem() {
168    	Enumeration _enum=this.fItemGroups.elements();
169        double maxItem= 0;
170    	while (_enum.hasMoreElements()) {
171			List items = (List) _enum.nextElement();
172			for (int i = 0; i < items.size(); i++) {
173				TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
174				if (graphItem.value > maxItem)
175					maxItem = graphItem.value;
176			}
177		}
178        if (maxItem == 0)
179            return 1;
180        return maxItem;
181    }
182
183    public double getMinItem() {
184       	Enumeration _enum = this.fItemGroups.elements();
185		double minItem = getMaxItem();
186
187		while (_enum.hasMoreElements()) {
188			List items = (List) _enum.nextElement();
189			for (int i = 0; i < items.size(); i++) {
190				TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
191				if (graphItem.value < minItem)
192					minItem = graphItem.value;
193			}
194		}
195        if (minItem == 0)
196            return -1;
197        return minItem;
198    }
199
200    private TimeLineGraphItem getMostRecent(Hashtable lineGraphGroups) {
201		Enumeration _enum = lineGraphGroups.elements();
202		long mostRecentTimestamp = 0;
203		TimeLineGraphItem mostRecentItem = null;
204
205		while (_enum.hasMoreElements()) {
206			List items = (List) _enum.nextElement();
207			for (int i = 0; i < items.size(); i++) {
208				if (items.size() == 1)
209					return (TimeLineGraphItem) items.get(i);
210				else {
211					TimeLineGraphItem graphItem = (TimeLineGraphItem) items.get(i);
212					if (graphItem.timestamp > mostRecentTimestamp) {
213						mostRecentTimestamp = graphItem.timestamp;
214						mostRecentItem = (TimeLineGraphItem) items.get(i);
215					}
216				}
217			}
218		}
219		return mostRecentItem;
220	}
221
222    private void setCoordinates(int width, int xOffset, int height, int yOffset, int yValueRange){
223
224        List mainGroup=(ArrayList)this.fItemGroups.get("main");
225        List referenceGroup=(ArrayList)this.fItemGroups.get("reference");
226
227        Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
228
229 		Object[] fItemsArray=mainGroup.toArray();
230		Arrays.sort(fItemsArray,comparator);
231
232		int n = mainGroup.size();
233		int xIncrement=width/n;
234		double max=getMaxItem()*1.2;
235//		double min=getMinItem()*0.8;
236
237		for (int i = 0; i < n; i++) {
238			TimeLineGraphItem thisItem = (TimeLineGraphItem) fItemsArray[i];
239			thisItem.setX(xOffset + (i * xIncrement));
240			thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
241
242			}
243
244		if (referenceGroup==null)
245			return;
246
247		n = referenceGroup.size();
248		for (int i = 0; i < n; i++) {
249			 TimeLineGraphItem thisItem = (TimeLineGraphItem) referenceGroup.get(i);
250			 if (thisItem.timestamp==-1)
251				 thisItem.setX(xOffset + (i * (width/n)));
252			 else
253				 setRelativeXPosition(thisItem,mainGroup);
254
255			 thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
256
257		}
258    }
259
260
261	private void setRelativeXPosition (TimeLineGraphItem thisItem, List items){
262			Comparator comparator=new TimeLineGraphItem.GraphItemComparator();
263			Object[] fItemsArray=items.toArray();
264			Arrays.sort(fItemsArray,comparator);
265
266			TimeLineGraphItem closestPrecedingItem=null;
267			long minimumTimeDiffPreceding=thisItem.timestamp;
268
269			TimeLineGraphItem closestFollowingItem=null;
270			long minimumTimeDiffFollowing=thisItem.timestamp;
271
272			for (int i=0;i<fItemsArray.length;i++){
273				TimeLineGraphItem anItem=(TimeLineGraphItem)fItemsArray[i];
274				long timeDiff=thisItem.timestamp-anItem.timestamp;
275
276				 if (timeDiff>0&&timeDiff<minimumTimeDiffPreceding){
277					 closestPrecedingItem=anItem;
278				 	minimumTimeDiffPreceding=thisItem.timestamp-anItem.timestamp;
279				 }
280				 if (timeDiff<=0&&Math.abs(timeDiff)<=minimumTimeDiffFollowing){
281					 closestFollowingItem=anItem;
282					 minimumTimeDiffFollowing=thisItem.timestamp-anItem.timestamp;
283				 }
284			}
285			if (closestFollowingItem==null && closestPrecedingItem!=null)
286				thisItem.setX(closestPrecedingItem.x);
287
288			else if (closestFollowingItem!=null && closestPrecedingItem==null)
289				thisItem.setX(closestFollowingItem.x);
290			else{
291				long timeRange=closestFollowingItem.timestamp-closestPrecedingItem.timestamp;
292
293				int xRange=closestFollowingItem.x-closestPrecedingItem.x;
294				double increments=(xRange*1.0)/timeRange;
295
296				thisItem.setX((int)(Math.round((thisItem.timestamp-closestPrecedingItem.timestamp)*increments)+closestPrecedingItem.x));
297			}
298	}
299}
300