blob: 982826a1473ba4b299d58b680e3b6b68927a4aeb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.test.performance.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.test.internal.performance.data.Dim;
public class TimeLineGraph extends LineGraph{
Hashtable<String, List<TimeLineGraphItem>> fItemGroups;
public TimeLineGraph (String title, Dim dim) {
super(title, dim);
this.fItemGroups=new Hashtable<>();
}
@Override
public void paint(Image im) {
Rectangle bounds= im.getBounds();
GC g= new GC(im);
Point ee= g.stringExtent(this.fTitle);
int titleHeight= ee.y;
double maxItem= getMaxItem();
double minItem= getMinItem();
int max= (int) (Math.ceil(maxItem * (maxItem < 0 ? 0.8 : 1.2)));
int min= (int) (Math.floor(minItem * (minItem < 0 ? 1.2 : 0.8)));
String smin= this.fDimension.getDisplayValue(min);
Point emin= g.stringExtent(smin);
String smax= this.fDimension.getDisplayValue(max);
Point emax= g.stringExtent(smax);
int labelWidth= Math.max(emin.x, emax.x) + 2;
int top= PADDING;
int bottom= bounds.height - titleHeight - PADDING;
int left= PADDING + labelWidth;
//getMostRecent
TimeLineGraphItem lastItem= getMostRecent(this.fItemGroups);
int right=bounds.width - PADDING/2;
if (lastItem!=null)
right= bounds.width - lastItem.getSize(g).x - PADDING/2;
// draw the max and min values
g.drawString(smin, PADDING/2+labelWidth-emin.x, bottom-titleHeight, true);
g.drawString(smax, PADDING/2+labelWidth-emax.x, top, true);
g.drawString("TIME (not drawn to scale)", (right-left)/3+PADDING+titleHeight,bottom-PADDING+(titleHeight*2), true);
// draw the vertical and horizontal lines
g.drawLine(left, top, left, bottom);
g.drawLine(left, bottom, right, bottom);
Color oldbg= g.getBackground();
Color oldfg= g.getForeground();
setCoordinates(right-left,left,bottom-top,max-min);
Enumeration<List<TimeLineGraphItem>> _enum=this.fItemGroups.elements();
Comparator<TimeLineGraphItem> comparator=new TimeLineGraphItem.GraphItemComparator();
while (_enum.hasMoreElements()) {
List<TimeLineGraphItem> items = _enum.nextElement();
TimeLineGraphItem[] fItemsArray=items.toArray(new TimeLineGraphItem[items.size()]);
Arrays.sort(fItemsArray,comparator);
int lastx = 0;
int lasty = 0;
int n = fItemsArray.length;
for (int i = 0; i < n; i++) {
TimeLineGraphItem thisItem = fItemsArray[i];
int yposition = thisItem.y;
int xposition = thisItem.x;
g.setLineWidth(1);
g.setBackground(thisItem.color);
g.setForeground(thisItem.color);
if (thisItem.drawAsBaseline){
g.setLineWidth(0);
g.drawLine(xposition, yposition,right,yposition);
g.drawLine(left,yposition,xposition, yposition);
}
if (i > 0) // don't draw for first segment
g.drawLine(lastx, lasty, xposition, yposition);
g.setBackground(thisItem.color);
g.setForeground(thisItem.color);
// g.fillOval(xposition - 2, yposition - 2, 6, 6);
g.fillRectangle(xposition - 2, yposition - 2, 5, 5);
if (thisItem.isSpecial)
g.drawRectangle(xposition -4, yposition - 4, 8, 8);
if (this.fAreaBuffer == null)
this.fAreaBuffer = new StringBuilder();
this.fAreaBuffer.append("\r<area shape=\"circle\" coords=\""
+ (xposition - 2) + ',' + (yposition - 2) + ',' + 5
+ " alt=\"" + thisItem.title + ": "
+ thisItem.description + "\"" + " title=\""
+ thisItem.title + ": " + thisItem.description + "\">");
int shift;
if (i > 0 && yposition < lasty)
shift = 3; // below dot
else
shift = -(2 * titleHeight + 3); // above dot
if (thisItem.displayDescription) {
g.drawString(thisItem.title, xposition + 2, yposition
+ shift, true);
g.drawString(thisItem.description, xposition + 2, yposition
+ shift + titleHeight, true);
}
g.setBackground(oldbg);
g.setForeground(oldfg);
lastx = xposition;
lasty = yposition;
}
}
g.dispose();
}
public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp) {
addItem(groupName, name, description, value, col, display, timestamp,false);
}
public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial) {
addItem(groupName, name,description, value, col, display,
timestamp,isSpecial,false);
}
public void addItem(String groupName,String name, String description, double value, Color col, boolean display, long timestamp,boolean isSpecial,boolean drawBaseline) {
List<TimeLineGraphItem> items = this.fItemGroups.get(groupName);
if (this.fItemGroups.get(groupName) == null) {
items=new ArrayList<>();
this.fItemGroups.put(groupName, items);
}
items.add(new TimeLineGraphItem(name, description, value, col, display,
timestamp,isSpecial,drawBaseline));
}
@Override
public double getMaxItem() {
Enumeration<List<TimeLineGraphItem>> _enum=this.fItemGroups.elements();
double maxItem= 0;
while (_enum.hasMoreElements()) {
List<TimeLineGraphItem> items = _enum.nextElement();
for (int i = 0; i < items.size(); i++) {
TimeLineGraphItem graphItem = items.get(i);
if (graphItem.value > maxItem)
maxItem = graphItem.value;
}
}
if (maxItem == 0)
return 1;
return maxItem;
}
@Override
public double getMinItem() {
Enumeration<List<TimeLineGraphItem>> _enum = this.fItemGroups.elements();
double minItem = getMaxItem();
while (_enum.hasMoreElements()) {
List<TimeLineGraphItem> items = _enum.nextElement();
for (int i = 0; i < items.size(); i++) {
TimeLineGraphItem graphItem = items.get(i);
if (graphItem.value < minItem)
minItem = graphItem.value;
}
}
if (minItem == 0)
return -1;
return minItem;
}
private TimeLineGraphItem getMostRecent(Hashtable<String, List<TimeLineGraphItem>> lineGraphGroups) {
Enumeration<List<TimeLineGraphItem>> _enum = lineGraphGroups.elements();
long mostRecentTimestamp = 0;
TimeLineGraphItem mostRecentItem = null;
while (_enum.hasMoreElements()) {
List<TimeLineGraphItem> items = _enum.nextElement();
for (int i = 0; i < items.size(); i++) {
if (items.size() == 1)
return items.get(i);
else {
TimeLineGraphItem graphItem = items.get(i);
if (graphItem.timestamp > mostRecentTimestamp) {
mostRecentTimestamp = graphItem.timestamp;
mostRecentItem = items.get(i);
}
}
}
}
return mostRecentItem;
}
private void setCoordinates(int width, int xOffset, int height, int yValueRange){
List<TimeLineGraphItem> mainGroup=this.fItemGroups.get("main");
List<TimeLineGraphItem> referenceGroup=this.fItemGroups.get("reference");
Comparator<TimeLineGraphItem> comparator=new TimeLineGraphItem.GraphItemComparator();
TimeLineGraphItem[] fItemsArray=mainGroup.toArray(new TimeLineGraphItem[mainGroup.size()]);
Arrays.sort(fItemsArray,comparator);
int n = mainGroup.size();
int xIncrement=width/n;
double max=getMaxItem()*1.2;
// double min=getMinItem()*0.8;
for (int i = 0; i < n; i++) {
TimeLineGraphItem thisItem = fItemsArray[i];
thisItem.setX(xOffset + (i * xIncrement));
thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
}
if (referenceGroup==null)
return;
n = referenceGroup.size();
for (int i = 0; i < n; i++) {
TimeLineGraphItem thisItem = referenceGroup.get(i);
if (thisItem.timestamp==-1)
thisItem.setX(xOffset + (i * (width/n)));
else
setRelativeXPosition(thisItem,mainGroup);
thisItem.setY((int)(PADDING+((max-thisItem.value) * (height)/(yValueRange))));
}
}
private void setRelativeXPosition (TimeLineGraphItem thisItem, List<TimeLineGraphItem> items){
Comparator<TimeLineGraphItem> comparator=new TimeLineGraphItem.GraphItemComparator();
TimeLineGraphItem[] fItemsArray=items.toArray(new TimeLineGraphItem[items.size()]);
Arrays.sort(fItemsArray,comparator);
TimeLineGraphItem closestPrecedingItem=null;
long minimumTimeDiffPreceding=thisItem.timestamp;
TimeLineGraphItem closestFollowingItem=null;
long minimumTimeDiffFollowing=thisItem.timestamp;
for (int i=0;i<fItemsArray.length;i++){
TimeLineGraphItem anItem=fItemsArray[i];
long timeDiff=thisItem.timestamp-anItem.timestamp;
if (timeDiff>0&&timeDiff<minimumTimeDiffPreceding){
closestPrecedingItem=anItem;
minimumTimeDiffPreceding=thisItem.timestamp-anItem.timestamp;
}
if (timeDiff<=0&&Math.abs(timeDiff)<=minimumTimeDiffFollowing){
closestFollowingItem=anItem;
minimumTimeDiffFollowing=thisItem.timestamp-anItem.timestamp;
}
}
if (closestFollowingItem==null && closestPrecedingItem!=null)
thisItem.setX(closestPrecedingItem.x);
else if (closestFollowingItem!=null && closestPrecedingItem==null)
thisItem.setX(closestFollowingItem.x);
else{
if (closestFollowingItem == null || closestPrecedingItem == null) {
throw new RuntimeException("closestFollowingItem or closestPrecedingItem was unexpectedly null. Program error?");
}
long timeRange=closestFollowingItem.timestamp-closestPrecedingItem.timestamp;
int xRange=closestFollowingItem.x-closestPrecedingItem.x;
double increments=(xRange*1.0)/timeRange;
thisItem.setX((int)(Math.round((thisItem.timestamp-closestPrecedingItem.timestamp)*increments)+closestPrecedingItem.x));
}
}
}