/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation. * 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 com.ibm.wala.ide.ui; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.ApplicationWindow; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; import com.ibm.wala.util.PlatformUtil; import com.ibm.wala.util.WalaException; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.graph.Graph; import com.ibm.wala.viz.NodeDecorator; /** * A class to view a WALA {@link Graph} with an SWT {@link TreeViewer} */ @SuppressWarnings("unchecked") public class SWTTreeViewer extends AbstractJFaceRunner { protected Graph graphInput; protected Collection<? extends Object> rootsInput = null; protected NodeDecorator nodeDecoratorInput = null; final protected List<IAction> popUpActions = new LinkedList<IAction>(); public SWTTreeViewer() { super(); } public Graph getGraphInput() { return graphInput; } public void setGraphInput(Graph newGraphInput) { graphInput = newGraphInput; } public Collection<? extends Object> getRootsInput() { return rootsInput; } public void setRootsInput(Collection<? extends Object> newRootsInput) { rootsInput = newRootsInput; } public NodeDecorator getNodeDecoratorInput() { return nodeDecoratorInput; } public void setNodeDecoratorInput(NodeDecorator newNodeDecoratorInput) { nodeDecoratorInput = newNodeDecoratorInput; } public List<IAction> getPopUpActions() { return popUpActions; } @Override public String toString() { StringBuffer result = new StringBuffer(super.toString()); result.append(", graphInput: "); result.append(graphInput); result.append(", rootsInput: "); result.append(rootsInput); result.append(", NodeDecoratorInput: "); result.append(nodeDecoratorInput); result.append(", popUpActions: "); result.append(popUpActions); result.append(')'); return result.toString(); } public void run() throws WalaException { if (getRootsInput() == null) { throw new WalaException("null roots input in " + getClass()); } final ApplicationWindow w = new GraphViewer(getGraphInput()); setApplicationWindow(w); w.setBlockOnOpen(true); if (PlatformUI.isWorkbenchRunning()) { // run the code on the UI thread Display d = PlatformUI.getWorkbench().getDisplay(); Runnable r = new Runnable() { @Override public void run() { try { w.open(); } catch (Exception e) { e.printStackTrace(); } } }; if (isBlockInput()) { d.syncExec(r); } else { d.asyncExec(r); } } else { if (PlatformUtil.onMacOSX()) { // the Mac does not like running the Window code in another thread // side-effect: we always block input on Mac w.open(); Display.getCurrent().dispose(); } else { Runnable r = new Runnable() { @Override public void run() { w.open(); Display.getCurrent().dispose(); } }; Thread t = new Thread(r); t.start(); if (isBlockInput()) { try { t.join(); } catch (InterruptedException e) { throw new WalaException("unexpected interruption", e); } } } } } /** * @throws IllegalStateException */ public IStructuredSelection getSelection() throws IllegalStateException { GraphViewer viewer = (GraphViewer) getApplicationWindow(); if (viewer == null || viewer.treeViewer == null) { throw new IllegalStateException(); } return (IStructuredSelection) viewer.treeViewer.getSelection(); } /** * @author sfink * * An SWT window to visualize a graph */ private class GraphViewer extends ApplicationWindow { /** * Graph to visualize */ private final Graph graph; /** * JFace component implementing the tree viewer */ private TreeViewer treeViewer; /** * @throws WalaException */ public GraphViewer(Graph graph) throws WalaException { super(null); this.graph = graph; if (graph == null) { throw new WalaException("null graph for SWT viewer"); } } /* * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite) */ @Override protected Control createContents(Composite parent) { treeViewer = new TreeViewer(parent); treeViewer.setContentProvider(new GraphContentProvider()); treeViewer.setLabelProvider(new GraphLabelProvider()); treeViewer.setInput(getGraphInput()); // create a pop-up menu if (getPopUpActions().size() > 0) { MenuManager mm = new MenuManager(); treeViewer.getTree().setMenu(mm.createContextMenu(treeViewer.getTree())); for (Iterator<IAction> it = getPopUpActions().iterator(); it.hasNext();) { mm.add(it.next()); } } return treeViewer.getTree(); } /** * @author sfink * * Simple wrapper around an EObjectGraph to provide content for a tree viewer. */ private class GraphContentProvider implements ITreeContentProvider { /* * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { // do nothing for now } /* * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // for now do nothing, since we're not dealing with listeners } /* * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren(Object parentElement) { Object[] result = new Object[graph.getSuccNodeCount(parentElement)]; int i = 0; for (Iterator it = graph.getSuccNodes(parentElement); it.hasNext();) { result[i++] = it.next(); } return result; } /* * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ @Override public Object getParent(Object element) { // TODO Auto-generated method stub Assertions.UNREACHABLE(); return null; } /* * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren(Object element) { return graph.getSuccNodeCount(element) > 0; } /* * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements(Object inputElement) { Collection<? extends Object> roots = getRootsInput(); Assertions.productionAssertion(roots != null); Assertions.productionAssertion(roots.size() >= 1); return roots.toArray(); } } /** * Simple graph label provider. TODO: finish this implementation. */ private class GraphLabelProvider extends LabelProvider { final NodeDecorator d = getNodeDecoratorInput(); @Override public String getText(Object element) { try { return (d == null) ? super.getText(element) : d.getLabel(element); } catch (WalaException e) { e.printStackTrace(); Assertions.UNREACHABLE(); return null; } } } } }