/*******************************************************************************
* Copyright (c) 2007, 2008 Edgar Espina.
* 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 org.deved.antlride.ui.views.interpreter;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.deved.antlride.common.ui.AntlrImages;
import org.deved.antlride.core.model.IGrammar;
import org.deved.antlride.core.model.IRule;
import org.deved.antlride.core.model.IToken;
import org.deved.antlride.core.model.evaluation.EvalElementKind;
import org.deved.antlride.core.model.evaluation.IEvalElement;
import org.deved.antlride.core.model.evaluation.IExceptionEvalElement;
import org.deved.antlride.core.model.evaluation.ITokenEvalElement;
import org.deved.antlride.internal.ui.editor.AntlrEditor;
import org.deved.antlride.internal.ui.views.interpreter.AntlrFigure;
import org.deved.antlride.internal.ui.views.interpreter.AntlrInterpreterMessages;
import org.deved.antlride.ui.AntlrUI;
import org.deved.antlride.ui.action.DropDownMenu;
import org.deved.antlride.ui.text.AntlrTextSelection;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.draw2d.ActionEvent;
import org.eclipse.draw2d.ActionListener;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.SWTGraphics;
import org.eclipse.draw2d.ScalableLayeredPane;
import org.eclipse.draw2d.ScrollPane;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
public class EvalElementViewer extends Viewer {
private Figure fRootFigure;
private int horizontalGap = 5;
private int verticalGap = 20;
private LightweightSystem lws;
private double fScale = 0.1;
private ScalableLayeredPane fScalableLayeredPane;
private IEvalElement fInput;
private ActionListener fSelectionActionListener;
private ScrollPane fScrollPane;
private Canvas fCanvas;
private Map<Integer, Action> fActions;
private List<IExceptionEvalElement> fExceptions;
private TableViewer fErrors;
private SashForm fControl;
public static final int ZOOM_IN_ACTION = 1001;
public static final int ZOOM_OUT_ACTION = 1002;
public static final int CLEAR_DIAGRAM_ACTION = 1003;
public static final int EXPORT_ACTION = 1004;
private class ClearDiagramAction extends Action {
public ClearDiagramAction() {
setText(AntlrInterpreterMessages.GrammarInterpreter_Clear_Diagram);
setToolTipText(AntlrInterpreterMessages.GrammarInterpreter_Clear_Diagram);
setImageDescriptor(AntlrImages.getDescriptor(AntlrImages.CLEAR));
}
@Override
public void run() {
clear();
}
}
protected class ExportAsImage extends Action {
private int format;
private String formatName;
public ExportAsImage(String formatName, int format) {
super("Export as " + formatName, IAction.AS_PUSH_BUTTON);
this.formatName = formatName;
this.format = format;
}
@Override
public void run() {
FileDialog dialog = new FileDialog(getControl().getShell(),
SWT.SAVE);
final String imageFile = fInput.getElementName() + "." + formatName;
dialog.setFileName(imageFile);
final String path = dialog.open();
if (path != null) {
ProgressMonitorDialog monitor = new ProgressMonitorDialog(
getControl().getShell());
try {
monitor.run(false, false, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException,
InterruptedException {
exportAsImage(monitor, new File(path), format);
}
});
} catch (InvocationTargetException e) {
ErrorDialog.openError(getControl().getShell(),
"Parse Tree Exporter", null, new Status(
IStatus.ERROR, AntlrUI.PLUGIN_ID, imageFile
+ " can't be exported", e
.getTargetException()));
AntlrUI.error(e.getTargetException());
} catch (InterruptedException e) {
AntlrUI.error(e);
}
}
}
}
private class ZoomInAction extends Action {
public ZoomInAction() {
setText(AntlrInterpreterMessages.GrammarInterpreter_Zoom_In);
setToolTipText(AntlrInterpreterMessages.GrammarInterpreter_Zoom_In);
setImageDescriptor(AntlrImages.getDescriptor(AntlrImages.ZOOM_IN));
}
@Override
public void run() {
zoomIn();
}
}
private class ZoomOutAction extends Action {
public ZoomOutAction() {
setText(AntlrInterpreterMessages.GrammarInterpreter_Zoom_Out);
setToolTipText(AntlrInterpreterMessages.GrammarInterpreter_Zoom_Out);
setImageDescriptor(AntlrImages.getDescriptor(AntlrImages.ZOOM_OUT));
}
@Override
public void run() {
zoomOut();
}
}
private class ExceptionLabelProvider extends LabelProvider {
@Override
public Image getImage(Object element) {
ISharedImages sharedImages = PlatformUI.getWorkbench()
.getSharedImages();
return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
}
@Override
public String getText(Object element) {
IExceptionEvalElement exception = (IExceptionEvalElement) element;
StringBuilder text = new StringBuilder(exception.getElementName());
text.append(": ");
text.append(exception.getMessage());
return text.toString();
}
}
public EvalElementViewer(Composite composite) {
fExceptions = new ArrayList<IExceptionEvalElement>();
createControl(composite);
}
protected void createControl(Composite parent) {
fControl = new SashForm(parent, SWT.VERTICAL);
fControl.setLayoutData(new GridData(GridData.FILL_BOTH));
// graph
fCanvas = new Canvas(fControl, SWT.BORDER);
fCanvas.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_WHITE));
this.lws = new LightweightSystem(fCanvas);
fScalableLayeredPane = new ScalableLayeredPane();
fScrollPane = new ScrollPane();
fScrollPane.setContents(fScalableLayeredPane);
fSelectionActionListener = new ModelFigureSelectionAction();
// errors
fErrors = new TableViewer(fControl);
fErrors.setLabelProvider(new ExceptionLabelProvider());
fErrors.setContentProvider(new ArrayContentProvider());
fErrors.addDoubleClickListener(new SelectExceptionListener());
Table table = fErrors.getTable();
TableColumn column = new TableColumn(table, SWT.LEFT);
column.setText("Problems");//$NON-NLS-1
column.setWidth(600);
table.setLinesVisible(true);
table.setHeaderVisible(true);
// spliter
fControl.setWeights(new int[] { 70, 30 });
// actions
createActions();
}
@Override
public Control getControl() {
return fControl;
}
@Override
public Object getInput() {
return fInput;
}
@Override
public ISelection getSelection() {
return StructuredSelection.EMPTY;
}
@Override
public void refresh() {
}
@Override
public void setInput(Object input) {
// fInput = (IEvalElement) input;
fExceptions.clear();
draw((IEvalElement) input);
fActions.get(EXPORT_ACTION).setEnabled(true);
fErrors.setInput(fExceptions);
}
@Override
public void setSelection(ISelection selection, boolean reveal) {
}
private void draw(IEvalElement input) {
clear();
this.fInput = input;
fRootFigure = new Figure();
fRootFigure.setLayoutManager(new XYLayout());
// build graph
draw(input, 10, 10);
fScalableLayeredPane.setScale(1.0);
fScalableLayeredPane.add(fRootFigure);
lws.setContents(fScrollPane);
fScrollPane.setContents(fScalableLayeredPane);
}
public void zoomIn() {
double scale = fScalableLayeredPane.getScale();
scale += zoomFactor();
fScalableLayeredPane.setScale(scale);
}
public void zoomOut() {
double scale = fScalableLayeredPane.getScale();
scale -= zoomFactor();
if (scale <= 0) {
return;
}
fScalableLayeredPane.setScale(scale);
}
private double zoomFactor() {
return fScale;// / 10;
}
public void clear() {
try {
if (fRootFigure != null) {
fScrollPane.setContents(null);
fScalableLayeredPane.remove(fRootFigure);
}
if (fInput != null) {
fInput.clear();
}
fExceptions.clear();
fErrors.setInput(fExceptions);
} catch (Exception ex) {
} finally {
if (fRootFigure != null)
fRootFigure.erase();
fRootFigure = null;
}
}
public void dispose() {
getControl().dispose();
}
private void select(IEvalElement evalElement) {
ISelection editorSelection = null;
int line = -1;
int offset = -1;
int length = -1;
if (evalElement.getElementKind() == EvalElementKind.RULE) {
IGrammar grammar = evalElement.getGrammar();
String ruleName = evalElement.getElementName();
IRule rule = grammar.findRule(ruleName);
if (rule != null) {
editorSelection = new TextSelection(rule.getName()
.sourceStart(), ruleName.length());
} else {
IToken token = grammar.findToken(ruleName);
if (token != null) {
editorSelection = new TextSelection(token.getName()
.sourceStart(), ruleName.length());
}
}
} else if (evalElement.getElementKind() == EvalElementKind.TOKEN) {
ITokenEvalElement token = (ITokenEvalElement) evalElement;
line = token.getLine();
offset = token.getColumn();
length = token.getText().length();
} else if (evalElement.getElementKind() == EvalElementKind.EXCEPTION) {
IExceptionEvalElement token = (IExceptionEvalElement) evalElement;
line = token.getLine();
offset = token.getColumn();
length = 0;// token.getText().length();
}
// update editor selection
IWorkbenchPage page = DLTKUIPlugin.getActivePage();
if (page != null && editorSelection != null) {
IEditorPart part = page.getActiveEditor();
if (part != null) {
AntlrEditor editor = (AntlrEditor) part
.getAdapter(AntlrEditor.class);
ISelectionProvider selectionProvider = editor
.getSelectionProvider();
selectionProvider.setSelection(editorSelection);
}
}
if (line != -1) {
ISelection selection = new AntlrTextSelection(line, offset, length);
fireSelectionChanged(new SelectionChangedEvent(this, selection));
}
}
@SuppressWarnings("unchecked")
private void draw(IEvalElement tree, int initialX, int initialY) {
// create initial graph
traverse(tree, initialX, initialY);
// create connectors
createAnchors(tree);
// set contraints for each figure
LayoutManager layout = this.fRootFigure.getLayoutManager();
List<IFigure> figures = this.fRootFigure.getChildren();
for (IFigure f : figures) {
if (f instanceof AntlrFigure) {
AntlrFigure figure = (AntlrFigure) f;
layout.setConstraint(figure, new Rectangle(figure.x, figure.y,
figure.width, figure.height));
}
}
}
private void createAnchors(IEvalElement tree) {
if (tree.isLeaf())
return;
int childCount = tree.getElementCount();
AntlrFigure parent = (AntlrFigure) tree.getUserData();
// create connection
int x1 = parent.x + (parent.width / 2);
int y1 = parent.y + parent.height;
int x2 = x1;
int y2 = y1 + ((childCount == 1) ? verticalGap : verticalGap / 2);
int middle = x1;
createConnection(parent, x1, y1, x2, y2);
if (childCount > 1) {
AntlrFigure first = (AntlrFigure) tree.firstElement().getUserData();
AntlrFigure last = (AntlrFigure) tree.lastElement().getUserData();
x1 = first.x + first.width / 2;
x2 = last.x + last.width / 2 + 1;
y1 = y1 + verticalGap / 2;
y2 = y1;
if (x2 < middle) {
x2 = middle;
}
createConnection(parent, x1, y1, x2, y2);
}
y1 = parent.y + parent.height + verticalGap / 2;
y2 = parent.y + parent.height + verticalGap;
for (int index = 0; index < childCount; index++) {
IEvalElement treeChild = tree.getElement(index);
// create link
AntlrFigure child = (AntlrFigure) treeChild.getUserData();
x1 = child.x + (child.width / 2);
x2 = x1;
if (childCount > 1) {
createConnection(parent, x1, y1, x2, y2);
}
createAnchors(treeChild);
}
}
private void createConnection(IFigure owner, int x1, int y1, int x2, int y2) {
IFigure figure = new Figure();
figure.setBorder(new LineBorder(ColorConstants.black, 1));
fRootFigure.add(figure);
LayoutManager layout = this.fRootFigure.getLayoutManager();
if (x1 == x2) {
x2 = 1;
} else {
x2 -= x1;
}
if (y1 == y2) {
y2 = 1;
} else {
y2 -= y1;
}
layout.setConstraint(figure, new Rectangle(x1, y1, x2, y2));
}
private int traverse(IEvalElement element, int x, int y) {
if (element.getElementKind() == EvalElementKind.EXCEPTION
&& !fExceptions.contains(element)) {
fExceptions.add((IExceptionEvalElement) element);
}
// create figure
AntlrFigure figure = createFigure(element, x, y);
// reset tree payload
element.setUserData(figure);
// traverse childs
for (IEvalElement child : element) {
x = traverse(child, x, y + figure.height + verticalGap);
}
int minLeft = figure.x;
int maxWidth = maxSpan(element);
if (element.getElementCount() >= 1) {
alignChild(element, minLeft, maxWidth);
}
// add figure
addFigure(figure);
return maxWidth + horizontalGap;
}
private void alignChild(IEvalElement tree, int left, int maxWidth) {
AntlrFigure childFigure = (AntlrFigure) tree.getUserData();
int offset = left + ((maxWidth - left - childFigure.width) / 2);
childFigure.x = offset;
if (tree.getElementCount() == 1) {
alignChild(tree.firstElement(), left, maxWidth);
}
}
private int maxSpan(IEvalElement tree) {
AntlrFigure f = (AntlrFigure) tree.getUserData();
int max = f.x + f.width;
int childCount = tree.getElementCount();
if (childCount > 0) {
int index = childCount - 1;
IEvalElement child = tree.getElement(index);
int childMax = maxSpan(child);
if (childMax > max) {
max = childMax;
}
}
return max;
}
private AntlrFigure createFigure(IEvalElement evalElement, int x, int y) {
AntlrFigure figure = new AntlrFigure(evalElement, x, y);
figure.addActionListener(fSelectionActionListener);
return figure;
}
private void addFigure(IFigure figure) {
fRootFigure.add(figure);
}
public Action getAction(int action) {
return fActions.get(action);
}
protected void createActions() {
fActions = new HashMap<Integer, Action>();
fActions.put(CLEAR_DIAGRAM_ACTION, new ClearDiagramAction());
fActions.put(ZOOM_IN_ACTION, new ZoomInAction());
fActions.put(ZOOM_OUT_ACTION, new ZoomOutAction());
DropDownMenu exportAction = new DropDownMenu(new ExportAsImage("png",
SWT.IMAGE_PNG), new ExportAsImage("jpeg", SWT.IMAGE_JPEG));
exportAction.setImageDescriptor(AntlrImages
.getDescriptor(AntlrImages.SAVE_AS));
exportAction.setEnabled(false);
fActions.put(EXPORT_ACTION, exportAction);
}
private class ModelFigureSelectionAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
AntlrFigure figure = (AntlrFigure) event.getSource();
select(figure.element);
}
}
private class SelectExceptionListener implements IDoubleClickListener {
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event
.getSelection();
IExceptionEvalElement exception = (IExceptionEvalElement) selection
.getFirstElement();
select(exception);
}
}
private void exportAsImage(IProgressMonitor monitor, File file, int format) {
monitor.beginTask("Parse Tree Exporter", 3);
IFigure figure = fRootFigure;
int maxX = 0;
int maxW = 0;
monitor.subTask("Calculating bounds");
@SuppressWarnings("unchecked")
List<IFigure> figures = figure.getChildren();
for (IFigure childFigure : figures) {
if (childFigure instanceof AntlrFigure) {
int x = childFigure.getBounds().x;
if (x > maxX) {
maxX = x;
maxW = childFigure.getBounds().width;
}
}
}
Rectangle r = Rectangle.SINGLETON;
r.x = figure.getBounds().x;
r.y = figure.getBounds().y;
r.height = figure.getBounds().height + 10;
r.width = maxX + maxW + 15;
monitor.worked(1);
OutputStream out = null;
Image image = null;
GC gc = null;
Graphics g = null;
try {
monitor.subTask("Creating image");
image = new Image(getControl().getDisplay(), r.width, r.height);
gc = new GC(image);
g = new SWTGraphics(gc);
g.translate(r.x * -1, r.y * -1);
figure.paint(g);
monitor.worked(1);
ImageLoader imageLoader = new ImageLoader();
imageLoader.data = new ImageData[] { image.getImageData() };
monitor.subTask("Writing to disk");
out = new BufferedOutputStream(new FileOutputStream(file));
imageLoader.save(out, format);
monitor.worked(1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (g != null) {
g.dispose();
}
if (gc != null) {
gc.dispose();
}
if (image != null) {
image.dispose();
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
monitor.done();
}
}
}