/* * Copyright (c) 2013 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.util.groovy.ast.viewer; import java.util.List; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.ast.VariableScope; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.eclipse.jface.action.Action; import org.eclipse.jface.layout.TreeColumnLayout; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import com.google.common.collect.ListMultimap; /** * A viewer for displaying Groovy AST nodes. * * @author Simon Templer */ public class ASTViewer { private class ViewContentProvider extends ArrayContentProvider implements ITreeContentProvider { private ListMultimap<Object, ?> children; @Override public void dispose() { children = null; } @SuppressWarnings("unchecked") @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { ASTToTreeVisitor v = new ASTToTreeVisitor(); if (newInput instanceof Iterable<?>) { for (Object node : ((Iterable<?>) newInput)) { if (node instanceof ASTNode) { if (node instanceof ClassNode) v.visitClass((ClassNode) node); } } } children = v.getChildren(); } @Override public Object getParent(Object child) { return null; } @Override public Object[] getChildren(Object parent) { if (children != null && parent instanceof ASTNode) { return children.get(parent).toArray(); } return new Object[0]; } @Override public boolean hasChildren(Object parent) { if (children != null && parent instanceof ASTNode) { return children.containsKey(parent); } return false; } } private class ViewLabelProvider extends LabelProvider { @Override public String getText(Object obj) { if (obj instanceof ASTNode) { ASTNode node = (ASTNode) obj; return node.getText(); } return super.getText(obj); } } private final TreeViewer treeViewer; private Action doubleClickAction; private final ITextViewer textViewer; private final Composite page; /** * Constructor. * * @param parent the parent composite * @param textViewer a text viewer where the code for an AST node should be * selected on double click, or <code>null</code> */ public ASTViewer(Composite parent, ITextViewer textViewer) { this.textViewer = textViewer; page = new Composite(parent, SWT.NONE); TreeColumnLayout layout = new TreeColumnLayout(); page.setLayout(layout); treeViewer = new TreeViewer(page, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); // new DrillDownAdapter(viewer); treeViewer.setContentProvider(new ViewContentProvider()); treeViewer.setLabelProvider(new ViewLabelProvider()); treeViewer.getTree().setHeaderVisible(true); TreeViewerColumn mainColumn = new TreeViewerColumn(treeViewer, SWT.NONE); mainColumn.setLabelProvider(new StyledCellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); StyledString text = new StyledString(); text.append(element.getClass().getSimpleName()); cell.setText(text.getString()); cell.setStyleRanges(text.getStyleRanges()); super.update(cell); } }); mainColumn.getColumn().setText("Node"); layout.setColumnData(mainColumn.getColumn(), new ColumnPixelData(200)); TreeViewerColumn infoColumn = new TreeViewerColumn(treeViewer, SWT.NONE); infoColumn.setLabelProvider(new StyledCellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); StyledString text = new StyledString(); if (element instanceof Parameter) { // getText not implemented for Parameter Parameter param = (Parameter) element; text.append(param.getName()); } else if (element instanceof ExpressionStatement) { // getText not properly implemented for ExpressionStatement text.append(((ExpressionStatement) element).getExpression().getText()); } else if (element instanceof ASTNode) { ASTNode node = (ASTNode) element; text.append(node.getText()); } else if (element instanceof Variable) { Variable var = (Variable) element; text.append(var.getName()); } else { text.append(element.toString()); } cell.setText(text.getString()); cell.setStyleRanges(text.getStyleRanges()); super.update(cell); } }); infoColumn.getColumn().setText("Text"); layout.setColumnData(infoColumn.getColumn(), new ColumnPixelData(300)); TreeViewerColumn propertiesColumn = new TreeViewerColumn(treeViewer, SWT.NONE); propertiesColumn.setLabelProvider(new StyledCellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); StyledString text = new StyledString(); if (element instanceof Variable || element instanceof ASTNode || element instanceof VariableScope) { ASTNodeUtil.addProperties(element, text); } cell.setText(text.getString()); cell.setStyleRanges(text.getStyleRanges()); super.update(cell); } }); propertiesColumn.getColumn().setText("Properties"); layout.setColumnData(propertiesColumn.getColumn(), new ColumnPixelData(1600)); makeActions(); if (textViewer != null) { addDoubleClickAction(); } } /** * @return the control */ public Control getControl() { return page; } /** * Set the input of the viewer to a set of AST Nodes * * @param ast the AST nodes */ public void setInput(List<ASTNode> ast) { treeViewer.setInput(ast); } private void makeActions() { doubleClickAction = new Action() { @Override public void run() { ISelection selection = treeViewer.getSelection(); Object obj = ((IStructuredSelection) selection).getFirstElement(); if (obj == null) { return; } if (obj instanceof ASTNode) { ASTNode node = (ASTNode) obj; if (node.getLineNumber() != -1) { try { int offset0 = textViewer.getDocument().getLineOffset( node.getLineNumber() - 1) + node.getColumnNumber() - 1; int offset1 = textViewer.getDocument().getLineOffset( node.getLastLineNumber() - 1) + node.getLastColumnNumber() - 1; textViewer.setSelectedRange(offset0, offset1 - offset0); textViewer.setTopIndex(node.getLineNumber() - 1); } catch (BadLocationException e) { // ignore } } } } }; } private void addDoubleClickAction() { treeViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { doubleClickAction.run(); } }); } /** * @return the internal tree viewer */ public TreeViewer getTreeViewer() { return treeViewer; } }