/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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
*******************************************************************************/
package de.gebit.integrity.eclipse.views;
import java.util.HashMap;
import java.util.Set;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import de.gebit.integrity.remoting.entities.setlist.SetList;
import de.gebit.integrity.remoting.entities.setlist.SetListEntry;
import de.gebit.integrity.remoting.entities.setlist.SetListEntryResultStates;
/**
* The custom content drawer for the main test execution tree. Takes care of test statement coloring.
*
* @author Rene Schneider - initial API and implementation
*
*/
public class TestTreeContentDrawer {
/**
* The set list to use.
*/
private SetList setList;
/**
* The set of breakpoints currently active.
*/
private Set<Integer> breakpointSet;
/**
* This color is used as neutral color.
*/
private Color nullColor;
/**
* Background color for successfully executed suite calls.
*/
private Color suiteSuccessColor;
/**
* Background color for suite calls that contain at least one failed element.
*/
private Color suiteFailureColor;
/**
* Background color for suite calls that contain at least one element that threw an exception during execution.
*/
private Color suiteExceptionColor;
/**
* Background color for successfully executed tests.
*/
private Color testSuccessColor;
/**
* Background color for tests that failed.
*/
private Color testFailureColor;
/**
* Background color for tests that threw an exception during execution.
*/
private Color testExceptionColor;
/**
* Background color for calls that threw an exception during execution.
*/
private Color callExceptionColor;
/**
* Background color for successfully executed calls.
*/
private Color callSuccessColor;
/**
* Background color for successfully executed assigns.
*/
private Color assignSuccessColor;
/**
* Color for suites that are currently being executed.
*/
private Color suiteInExecutionColor;
/**
* Color for calls/tests that are currently being executed.
*/
private Color callOrTestInExecutionColor;
/**
* Color for breakpoints.
*/
private Color breakpointColor;
/**
* The listener being attached to the tree.
*/
private Listener measureListener;
/**
* The listener being attached to the tree.
*/
private Listener eraseListener;
/**
* Maps forks identified by their names to unique colors.
*/
private HashMap<String, Color> forkColorMap = new HashMap<String, Color>();
/**
* The fork colors.
*/
private Color[] forkColors = new Color[8];
/**
* The number of pixels to indent for each "level" in the tree.
*/
private static final int INDENT_PIXELS = 19;
/**
* The number of pixels to indent the background color for the first level in the tree.
*/
private static final int INDENT_BASE = 42;
/**
* The width of the gradient in pixels.
*/
private static final int GRADIENT_WIDTH = 32;
/**
* The offset of the gradient in pixels.
*/
private static final int GRADIENT_OFFSET = -16;
/**
* Creates a new instance.
*
* @param aSetList
* the set list
* @param aBreakpointSet
* the breakpoint set
* @param aDisplay
* the display
*/
public TestTreeContentDrawer(SetList aSetList, Set<Integer> aBreakpointSet, Display aDisplay) {
setList = aSetList;
breakpointSet = aBreakpointSet;
nullColor = new Color(aDisplay, 255, 255, 255);
suiteSuccessColor = new Color(aDisplay, 156, 255, 189);
suiteFailureColor = new Color(aDisplay, 255, 130, 130);
suiteExceptionColor = new Color(aDisplay, 255, 248, 136);
testSuccessColor = suiteSuccessColor;
testFailureColor = suiteFailureColor;
testExceptionColor = suiteExceptionColor;
callExceptionColor = suiteExceptionColor;
callSuccessColor = new Color(aDisplay, 205, 255, 222);
callOrTestInExecutionColor = new Color(aDisplay, 198, 203, 255);
suiteInExecutionColor = new Color(aDisplay, 229, 231, 255);
breakpointColor = new Color(aDisplay, 0, 0, 0);
assignSuccessColor = new Color(aDisplay, 203, 255, 216);
forkColors[0] = new Color(aDisplay, 255, 0, 0);
forkColors[1] = new Color(aDisplay, 255, 255, 0);
forkColors[2] = new Color(aDisplay, 0, 255, 0);
forkColors[3] = new Color(aDisplay, 0, 0, 255);
forkColors[4] = new Color(aDisplay, 0, 255, 255);
forkColors[5] = new Color(aDisplay, 255, 0, 255);
forkColors[6] = new Color(aDisplay, 96, 57, 19);
forkColors[7] = new Color(aDisplay, 0, 0, 0);
}
/**
* Disposes the drawer instance, cleaning up resources in the process.
*
* @param aTree
*/
public void dispose(Tree aTree) {
aTree.removeListener(SWT.MeasureItem, measureListener);
aTree.removeListener(SWT.EraseItem, eraseListener);
nullColor.dispose();
suiteSuccessColor.dispose();
suiteFailureColor.dispose();
suiteExceptionColor.dispose();
testSuccessColor.dispose();
testFailureColor.dispose();
testExceptionColor.dispose();
callSuccessColor.dispose();
callExceptionColor.dispose();
callOrTestInExecutionColor.dispose();
suiteInExecutionColor.dispose();
breakpointColor.dispose();
assignSuccessColor.dispose();
for (int i = 0; i < forkColors.length; i++) {
forkColors[i].dispose();
}
}
/**
* Attaches the drawer to a given tree.
*
* @param aTree
* the tree
*/
public void attachToTree(final Tree aTree) {
measureListener = new Listener() {
@Override
public void handleEvent(Event anEvent) {
int tempMinWidth = aTree.getClientArea().width - anEvent.x;
if (anEvent.width < tempMinWidth) {
anEvent.width = tempMinWidth;
}
}
};
aTree.addListener(SWT.MeasureItem, measureListener);
eraseListener = new Listener() {
@Override
public void handleEvent(Event anEvent) {
TreeItem tempItem = (TreeItem) anEvent.item;
SetListEntry tempEntry = (SetListEntry) tempItem.getData();
SetListEntryResultStates tempResultState = setList.getResultStateForEntry(tempEntry);
if (tempResultState != null && tempResultState != SetListEntryResultStates.UNKNOWN) {
int tempInset = getTreeItemIndentation(tempItem);
Color tempOldForeground = anEvent.gc.getForeground();
Color tempOldBackground = anEvent.gc.getBackground();
anEvent.gc.setForeground(nullColor);
Color tempBackground = tempOldBackground;
switch (tempEntry.getType()) {
case SUITE:
switch (tempResultState) {
case EXCEPTION:
tempBackground = suiteExceptionColor;
break;
case FAILED:
tempBackground = suiteFailureColor;
break;
case SUCCESSFUL:
tempBackground = suiteSuccessColor;
break;
default:
break;
}
break;
case CALL:
switch (tempResultState) {
case SUCCESSFUL:
tempBackground = callSuccessColor;
break;
case EXCEPTION:
tempBackground = callExceptionColor;
break;
default:
break;
}
break;
case VARIABLE_ASSIGNMENT:
switch (tempResultState) {
case SUCCESSFUL:
tempBackground = assignSuccessColor;
break;
default:
break;
}
break;
case TEST:
case TABLETEST:
case RESULT:
switch (tempResultState) {
case SUCCESSFUL:
tempBackground = testSuccessColor;
break;
case EXCEPTION:
tempBackground = testExceptionColor;
break;
case FAILED:
tempBackground = testFailureColor;
break;
default:
break;
}
break;
case COMMENT:
default:
break;
}
anEvent.gc.setBackground(tempBackground);
anEvent.gc.fillGradientRectangle(anEvent.x + tempInset + GRADIENT_OFFSET, anEvent.y, GRADIENT_WIDTH,
anEvent.height, false);
anEvent.gc.fillRectangle(anEvent.x + tempInset + GRADIENT_WIDTH + GRADIENT_OFFSET, anEvent.y,
anEvent.width - (tempInset + GRADIENT_WIDTH + GRADIENT_OFFSET), anEvent.height);
anEvent.gc.setForeground(tempOldForeground);
anEvent.gc.setBackground(tempOldBackground);
anEvent.detail &= ~SWT.BACKGROUND;
anEvent.detail &= ~SWT.HOT;
} else if (setList.isEntryInExecution(tempEntry)) {
Color tempOldBackground = anEvent.gc.getBackground();
switch (tempEntry.getType()) {
case CALL:
case TEST:
case TABLETEST:
case VARIABLE_ASSIGNMENT:
anEvent.gc.setBackground(callOrTestInExecutionColor);
break;
case SUITE:
case SETUP:
case TEARDOWN:
anEvent.gc.setBackground(suiteInExecutionColor);
break;
default:
break;
}
anEvent.gc.fillRectangle(anEvent.x, anEvent.y, anEvent.width, anEvent.height);
anEvent.gc.setBackground(tempOldBackground);
anEvent.detail &= ~SWT.BACKGROUND;
anEvent.detail &= ~SWT.HOT;
}
String[] tempForkName = setList.getForkExecutingEntry(tempEntry);
if (tempForkName != null) {
Color tempOldBackground = anEvent.gc.getBackground();
anEvent.gc.setBackground(getForkColor(tempForkName[0]));
anEvent.gc.fillRectangle(anEvent.x, anEvent.y, 3, anEvent.height);
anEvent.gc.setBackground(tempOldBackground);
anEvent.detail &= ~SWT.BACKGROUND;
anEvent.detail &= ~SWT.HOT;
}
if (breakpointSet.contains(tempEntry.getId())) {
int tempOffset = tempForkName != null ? 3 : 0;
Color tempOldBackground = anEvent.gc.getBackground();
anEvent.gc.setBackground(breakpointColor);
anEvent.gc.fillPolygon(new int[] { anEvent.x + tempOffset, anEvent.y + 2,
anEvent.x + tempOffset + anEvent.height / 2, anEvent.y + 2 + (anEvent.height - 4) / 2,
anEvent.x + tempOffset, anEvent.y + (anEvent.height - 4) - 1 });
anEvent.gc.setBackground(tempOldBackground);
anEvent.detail &= ~SWT.BACKGROUND;
anEvent.detail &= ~SWT.HOT;
}
}
};
aTree.addListener(SWT.EraseItem, eraseListener);
}
private int getTreeItemIndentation(TreeItem anItem) {
int tempIndent = 0;
TreeItem tempParent = anItem.getParentItem();
while (tempParent != null) {
tempIndent++;
tempParent = tempParent.getParentItem();
}
return tempIndent * INDENT_PIXELS + INDENT_BASE;
}
private Color getForkColor(String aForkName) {
Color tempColor = forkColorMap.get(aForkName);
if (tempColor == null) {
if (forkColorMap.size() >= forkColors.length) {
tempColor = forkColors[forkColors.length - 1];
} else {
tempColor = forkColors[forkColorMap.size()];
}
forkColorMap.put(aForkName, tempColor);
}
return tempColor;
}
}