/*******************************************************************************
* Copyright (c) 2004, 2008 Alternative Ideas Corporation. All rights reserved.
* This program and the accompanying materials are protected by United
* States and International copyright laws. They may not be inspected,
* copied, modified, transmitted, executed, or used in any way without
* the express written permission of the copyright holder.
*******************************************************************************/
package com.deltopia.ui.tests.internal;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
public class FolderTree extends Composite {
private static final boolean REDRAW_WHILE_CREATING_TREE_ITEMS = false;
private static final int STATS_CREATE_TREE_ITEMS_INVOCATIONS = 1;
private static final int STATS_CREATE_TREE_ITEMS_TIME = 0;
private static final int STATS_EXPANDED_FALSE = 2;
private static final int STATS_EXPANDED_TIME = 0;
private static final int STATS_EXPANDED_TRUE = 1;
private static final int STATS_SET_IMAGE_INVOCATIONS = 2;
/*
* Two TreeColumns reproduce our scenario of using TreeItem#setImage.
* However, a greater number of TreeColumns better depicts the effect of the
* optimization of Tree#imageIndex because it easily increases the number of
* TreeItem#setImage(int,Image) calls for TreeColumns with non-zero index.
*/
private static final int TREE_COLUMN_COUNT = 2;
private Text browseText;
/**
* The Image
to be used for file and folders that don't have
* an association through Program
.
*
* Allows close reproduction of a specific number
* TreeItem#setImage
calls that doesn't vary on the
* Program
associations of the machine running the test.
*
*/
private final Image defaultImage;
private final Map extension2Image = new HashMap();
private final Tree tree;
public FolderTree(Composite parent, int style) {
super(parent, style);
Control inputControls = createInputControls(this, style);
if (inputControls != null) {
inputControls.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING,
true, false));
}
Control commandControls = createCommandControls(this, style);
if (commandControls != null) {
commandControls.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING,
true, false));
}
tree = new Tree(this, SWT.BORDER);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
for (int columnIx = 0; columnIx < TREE_COLUMN_COUNT; columnIx++) {
TreeColumn column = new TreeColumn(tree, SWT.NONE);
column.setText(Integer.toString(tree.indexOf(column)));
}
if (tree.getColumnCount() > 0) {
tree.setHeaderVisible(true);
}
setLayout(new GridLayout(1, false));
defaultImage = new Image(getDisplay(), 16, 16);
}
private Control createCommandControls(Composite parent, int style) {
Composite commandControls = new Composite(parent, SWT.NONE);
int numColumns = 0;
final Button expandAllButton = new Button(commandControls, SWT.PUSH);
expandAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, false));
expandAllButton.setText("Expand All");
numColumns++;
final Button collapseAllButton = new Button(commandControls, SWT.PUSH);
collapseAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, false));
collapseAllButton.setText("Collapse All");
numColumns++;
final Button getExpandedButton = new Button(commandControls, SWT.PUSH);
getExpandedButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, false));
getExpandedButton.setText("TreeItem.getExpanded()");
numColumns++;
final Button setTopItemButton = new Button(commandControls, SWT.PUSH);
setTopItemButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, false));
setTopItemButton.setText("Tree.setTopItem(TreeItem)");
numColumns++;
final Listener listener = new Listener() {
public void handleEvent(Event e) {
final Widget widget = e.widget;
if (widget == expandAllButton) {
setExpanded(null, true);
} else if (widget == collapseAllButton) {
setExpanded(null, false);
} else if (widget == getExpandedButton) {
final long[] stats = new long[3];
getExpanded(null, stats);
System.out
.println("TreeItem.getExpanded()= { invocations= { true= "
+ stats[STATS_EXPANDED_TRUE]
+ ", false= "
+ stats[STATS_EXPANDED_FALSE]
+ " }, time= "
+ stats[STATS_EXPANDED_TIME]
+ " }");
} else if (widget == setTopItemButton) {
handleSetTopItemButtonSelection();
}
}
};
expandAllButton.addListener(SWT.Selection, listener);
collapseAllButton.addListener(SWT.Selection, listener);
getExpandedButton.addListener(SWT.Selection, listener);
setTopItemButton.addListener(SWT.Selection, listener);
if (numColumns > 0) {
GridLayout commandControlsLayout = new GridLayout();
commandControlsLayout.makeColumnsEqualWidth = false;
commandControlsLayout.marginHeight = 0;
commandControlsLayout.marginWidth = 0;
commandControlsLayout.numColumns = numColumns;
commandControls.setLayout(commandControlsLayout);
} else {
commandControls.dispose();
commandControls = null;
}
return commandControls;
}
private Control createInputControls(Composite parent, int style) {
final Composite inputControls = new Composite(parent, SWT.NONE);
browseText = new Text(inputControls, SWT.BORDER | SWT.MULTI
| SWT.READ_ONLY | SWT.WRAP);
browseText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Button browseButton = new Button(inputControls, SWT.PUSH);
browseButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING,
false, false));
browseButton.setText("Browse...");
final Listener listener = new Listener() {
public void handleEvent(Event e) {
final Widget widget = e.widget;
if (widget == browseText) {
switch (e.type) {
case SWT.Modify:
setFolder(browseText.getText());
break;
default:
throw new IllegalArgumentException("e.type"); //$NON-NLS-1$
}
} else if (widget == browseButton) {
switch (e.type) {
case SWT.Selection:
DirectoryDialog dialog = new DirectoryDialog(
browseButton.getShell());
String selection = dialog.open();
if (selection != null) {
browseText.setText(selection);
}
break;
default:
throw new IllegalArgumentException("e.type"); //$NON-NLS-1$
}
} else {
throw new IllegalArgumentException("e.widget"); //$NON-NLS-1$
}
}
};
browseText.addListener(SWT.Modify, listener);
browseButton.addListener(SWT.Selection, listener);
GridLayout inputControlsLayout = new GridLayout();
inputControlsLayout.makeColumnsEqualWidth = false;
inputControlsLayout.marginHeight = 0;
inputControlsLayout.marginWidth = 0;
inputControlsLayout.numColumns = 2;
inputControls.setLayout(inputControlsLayout);
return inputControls;
}
private void createTreeItems(TreeItem parentItem, File file, long[] stats) {
if (parentItem == null) {
tree.removeAll();
} else {
parentItem.removeAll();
}
File[] files = file.listFiles();
if (files != null) {
final int style = SWT.NONE;
for (int fileIx = 0; fileIx < files.length; fileIx++) {
final long start = System.currentTimeMillis();
TreeItem item = (parentItem == null) ? new TreeItem(tree, style)
: new TreeItem(parentItem, style);
final long time = System.currentTimeMillis() - start;
stats[STATS_CREATE_TREE_ITEMS_TIME] += time;
stats[STATS_CREATE_TREE_ITEMS_INVOCATIONS]++;
file = files[fileIx];
item.setText(file.getName());
setImage(item, file, stats);
if (file.isDirectory()) {
createTreeItems(item, file, stats);
}
}
}
}
private void getExpanded(TreeItem item, long[] stats) {
TreeItem[] items;
if (item == null) {
items = tree.getItems();
} else {
items = item.getItems();
}
for (int itemIx = 0; itemIx < items.length; itemIx++) {
item = items[itemIx];
final long start = System.currentTimeMillis();
boolean expanded = item.getExpanded();
final long time = System.currentTimeMillis() - start;
stats[STATS_EXPANDED_TIME] += time;
final int index = expanded ? STATS_EXPANDED_TRUE
: STATS_EXPANDED_FALSE;
stats[index]++;
getExpanded(item, stats);
}
}
private void getViewableTreeItems(TreeItem item, List result) {
TreeItem[] items;
if (item == null) {
items = tree.getItems();
} else {
items = item.getItems();
}
for (int itemIx = 0; itemIx < items.length; itemIx++) {
item = items[itemIx];
result.add(item);
if (item.getExpanded()) {
getViewableTreeItems(item, result);
}
}
}
private void handleSetTopItemButtonSelection() {
ScrollBar verticalBar = tree.getVerticalBar();
List items = new ArrayList();
getViewableTreeItems(null, items);
int itemCount = items.size();
int minimum = verticalBar.getMinimum();
int range = verticalBar.getMaximum() - minimum;
int newSelection = verticalBar.getSelection() + range / 10;
int topItemIndex = (int) ((newSelection - minimum) * (itemCount / (double) range));
if ((0 <= topItemIndex) && (topItemIndex < itemCount)) {
tree.setTopItem((TreeItem) items.get(topItemIndex));
}
}
private void setExpanded(TreeItem item, boolean expanded) {
TreeItem[] items;
if (item == null) {
items = tree.getItems();
} else {
items = item.getItems();
}
for (int itemIx = 0; itemIx < items.length; itemIx++) {
item = items[itemIx];
if (expanded) {
item.setExpanded(expanded);
setExpanded(item, expanded);
} else {
setExpanded(item, expanded);
item.setExpanded(expanded);
}
}
}
public void setFolder(String folder) {
if (folder == null) {
folder = ""; //$NON-NLS-1$
}
if ((browseText != null) && (browseText.isDisposed() == false)
&& (browseText.getText().equals(folder) == false)) {
browseText.setText(folder);
}
if (REDRAW_WHILE_CREATING_TREE_ITEMS == false) {
tree.setRedraw(false);
}
try {
final long[] stats = new long[3];
TreeItem.setImage_invocations = 0;
TreeItem.setImage_time = 0;
createTreeItems(null, new File(folder), stats);
System.out.println("new TreeItem= { invocations= "
+ stats[STATS_CREATE_TREE_ITEMS_INVOCATIONS] + ", time= "
+ stats[STATS_CREATE_TREE_ITEMS_TIME] + " }");
System.out.println("TreeItem#setImage invocations in FolderTree= "
+ stats[STATS_SET_IMAGE_INVOCATIONS]);
System.out.println("TreeItem#setImage= { invocations= "
+ TreeItem.setImage_invocations + ", time= "
+ TreeItem.setImage_time + " }");
/*
* Size the TreeColumns not for the sake of performance testing but
* in the name of visibility.
*/
final int columnCount = tree.getColumnCount();
if (columnCount > 0) {
int width = tree.computeTrim(0, 0, 0, 0).width;
for (int columnIx = columnCount - 1; columnIx > 0; columnIx--) {
TreeColumn column = tree.getColumn(columnIx);
column.pack();
width += column.getWidth();
}
width = tree.getClientArea().width - width;
TreeColumn column = tree.getColumn(0);
if (width > 0) {
column.setWidth(width);
} else {
column.pack();
}
}
} finally {
if (REDRAW_WHILE_CREATING_TREE_ITEMS == false) {
tree.setRedraw(true);
}
}
}
private void setImage(TreeItem item, File file, long[] stats) {
/*
* The image of a TreeItem representing a specific File is the image
* associated with the File extension.
*/
String fileName = file.getName();
int extensionPos = fileName.lastIndexOf('.');
if (-1 != extensionPos) {
String extension = fileName.substring(extensionPos).toLowerCase();
/*
* Not that it's used in the tests but cache the images in order to
* get better performance and not run out of Image handles.
*/
Image image = (Image) extension2Image.get(extension);
if (image == null) {
Program program = Program.findProgram(extension);
if (program != null) {
ImageData imageData = program.getImageData();
if (imageData != null) {
image = new Image(item.getDisplay(), imageData);
extension2Image.put(extension, image);
}
}
if (image == null) {
image = defaultImage;
}
}
/*
* Invoke TreeItem#setImage in order to benchmark it. The column
* with index 0 is intentionally special-cased in order to invoke
* TreeItem#setImage(Image) and not TreeItem#setImage(int,Image) and
* thus have Widget#checkWidget() called twice per a
* TreeItem#setImage call (in case the situation is of interest).
*/
final int columnCount = tree.getColumnCount();
for (int columnIx = 0; columnIx < columnCount; columnIx++) {
if (columnIx == 0) {
item.setImage(image);
} else {
item.setImage(columnIx, image);
}
stats[STATS_SET_IMAGE_INVOCATIONS]++;
}
}
}
}