package com.github.vlsi.mat.calcite.editor; import org.apache.calcite.runtime.CalciteContextException; import org.apache.calcite.sql.parser.SqlParseException; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.parser.SqlParserUtil; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentPartitioner; import org.eclipse.jface.text.IUndoManagerExtension; import org.eclipse.jface.text.rules.FastPartitioner; import org.eclipse.jface.text.source.CompositeRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.LineNumberRulerColumn; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.mat.query.IResult; import org.eclipse.mat.query.registry.QueryResult; import org.eclipse.mat.ui.editor.AbstractEditorPane; import org.eclipse.mat.ui.editor.CompositeHeapEditorPane; import org.eclipse.mat.ui.editor.EditorPaneRegistry; import org.eclipse.mat.ui.util.PaneState; import org.eclipse.mat.ui.util.PaneState.PaneType; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.operations.RedoActionHandler; import org.eclipse.ui.operations.UndoActionHandler; import org.eclipse.ui.plugin.AbstractUIPlugin; import com.github.vlsi.mat.calcite.action.CommentLineAction; import com.github.vlsi.mat.calcite.action.ExecuteQueryAction; public class CalcitePane extends CompositeHeapEditorPane { private SourceViewer queryViewer; private StyledText queryString; private ExecuteQueryAction executeQueryAction; private ExecuteQueryAction explainQueryAction; private Action copyQueryStringAction; private Action commentLineAction; private Action contentAssistAction; public CalcitePane() { } @Override public void createPartControl(Composite parent) { SashForm sash = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH); int VERTICAL_RULER_WIDTH = 12; CompositeRuler ruler = new CompositeRuler(VERTICAL_RULER_WIDTH); ruler.addDecorator(0, new LineNumberRulerColumn()); queryViewer = new SourceViewer(sash, ruler, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI); queryViewer.configure(new CalciteSourceViewerConfiguration()); queryString = queryViewer.getTextWidget(); // The following setBackground(getBackground) results in proper white background in MACOS. // No sure why the background is gray otherwise. queryString.setBackground(queryString.getBackground()); queryString.setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT)); queryString.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN && (e.stateMask & SWT.MOD1) != 0) { executeQueryAction.run(); e.detail = SWT.TRAVERSE_NONE; } } }); queryString.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == ' ' && (e.stateMask & SWT.CTRL) != 0) { // ctrl space combination for content assist contentAssistAction.run(); } else if (e.character == '/' && (e.stateMask & SWT.MOD1) != 0) { commentLineAction.run(); e.doit = false; } else if (e.keyCode == SWT.F5) { executeQueryAction.run(); e.doit = false; } else if (e.keyCode == SWT.F10) { explainQueryAction.run(); e.doit = false; } } }); this.queryString.addFocusListener(new FocusListener() { public void focusGained(FocusEvent e) { IActionBars actionBars = CalcitePane.this.getEditor().getEditorSite().getActionBars(); actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), CalcitePane.this.copyQueryStringAction); actionBars.updateActionBars(); } public void focusLost(FocusEvent e) { } }); IDocument doc = createDocument(); SourceViewerConfiguration svc = new CalciteSourceViewerConfiguration(); IDocumentPartitioner partitioner = new FastPartitioner( new CalcitePartitionScanner(), svc.getConfiguredContentTypes(queryViewer)); partitioner.connect(doc); doc.setDocumentPartitioner(partitioner); queryViewer.setDocument(doc); queryViewer.configure(svc); queryString.selectAll(); createContainer(sash); makeActions(); installUndoRedoSupport(); } private void installUndoRedoSupport() { IUndoContext undoContext = ((IUndoManagerExtension) queryViewer.getUndoManager()).getUndoContext(); UndoActionHandler undoAction = new UndoActionHandler(getSite(), undoContext); RedoActionHandler redoAction = new RedoActionHandler(getSite(), undoContext); undoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO); redoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO); IActionBars actionBars = getEditor().getEditorSite().getActionBars(); actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undoAction); actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction); actionBars.updateActionBars(); } private IDocument createDocument() { IDocument doc = new Document(); doc.set("-- explain plan for -- or F10\n" + "-- Tables:\n" + "-- java.lang.BigInteger list of all BigIntegers\n" + "-- instanceof.java.lang.BigInteger BigIntegers and all subclasses\n" + "-- instanceof.java.lang.\"HashMap$Entry\" Entry and all subclasses\n" + "-- Virtual columns:\n" + "-- this['@shallow'] -- returns shallow heap size\n" + "-- this['@retained'] -- returns retained heap size\n" + "-- this['fieldName'] -- the same as getField(this, 'fieldName')\n" + "-- this['a.b.c'] -- the same as this['a']['b']['c']\n" + "-- Functions:\n" + "-- toString(any) returns string representation\n" + "-- getAddress(any) returns address of referenced object\n" + "-- getType(any) returns class name of referenced object\n" + "-- shallowSize(any) returns shallow heap size\n" + "-- retainedSize(any) returns retained heap size\n" + "-- length(array) returns length of array reference\n" + "-- getSize(collection or map or array) returns size of collection or array, or number of non-null elements in array\n" + "-- getByKey(map, key) returns element of map for key with given string representation\n" + "-- getField(any, field name) returns value of field for given object\n" + "select u.this, retainedSize(s.this) retained_size\n" + " from \"java.lang.String\" s\n" + " , \"java.net.URL\" u\n" + " where s.this = u.path\n"); return doc; } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { super.init(site, input); } @Override public void setFocus() { } @Override public String getTitle() { return "Calcite"; } @Override public Image getTitleImage() { return AbstractUIPlugin.imageDescriptorFromPlugin("MatCalcitePlugin", "resources/icons/plugin.png").createImage(); } private void makeActions() { executeQueryAction = new ExecuteQueryAction(this, null, false); explainQueryAction = new ExecuteQueryAction(this, null, true); commentLineAction = new CommentLineAction(queryString); IWorkbenchWindow window = this.getEditorSite().getWorkbenchWindow(); ActionFactory.IWorkbenchAction globalAction = ActionFactory.COPY.create(window); this.copyQueryStringAction = new Action() { public void run() { CalcitePane.this.queryString.copy(); } }; this.copyQueryStringAction.setAccelerator(globalAction.getAccelerator()); this.contentAssistAction = new Action() { @Override public void run() { queryViewer.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); } }; } public StyledText getQueryString() { return queryString; } public String highlightError(Throwable e) { Throwable t; SqlParserPos errPos = null; for(t = e; t!=null; t = t.getCause()) { if (t instanceof CalciteContextException) { CalciteContextException cce = (CalciteContextException) t; errPos = new SqlParserPos(cce.getPosLine(), cce.getPosColumn(), cce.getEndPosLine(), cce.getEndPosColumn()); break; } if (t instanceof SqlParseException) { SqlParseException spe = (SqlParseException) t; errPos = spe.getPos(); break; } } if (errPos == null) { return null; } String sql = queryViewer.getDocument().get(); StyleRange style = new StyleRange(); int start = SqlParserUtil.lineColToIndex(sql, errPos.getLineNum(), errPos.getColumnNum()); int end = SqlParserUtil.lineColToIndex(sql, errPos.getEndLineNum(), errPos.getEndColumnNum()) + 1; style.start = start; style.length = end - start; style.foreground = JFaceResources.getColorRegistry().get(JFacePreferences.ERROR_COLOR); style.underline = true; style.underlineStyle = SWT.UNDERLINE_SQUIGGLE; queryString.replaceStyleRanges(start, end - start, new StyleRange[] { style }); return t.getMessage(); } public void initQueryResult(QueryResult queryResult, PaneState state) { IResult subject = queryResult.getSubject(); // queryViewer.getDocument().set(subject.getOQLQuery()); AbstractEditorPane pane = EditorPaneRegistry.instance().createNewPane( subject, this.getClass()); if (state == null) { for (PaneState child : getPaneState().getChildren()) { if (queryString.getText().equals(child.getIdentifier())) { state = child; break; } } if (state == null) { state = new PaneState(PaneType.COMPOSITE_CHILD, getPaneState(), queryString.getText(), true); state.setImage(getTitleImage()); } } pane.setPaneState(state); createResultPane(pane, queryResult); } @Override public void contributeToToolBar(IToolBarManager manager) { manager.add(executeQueryAction); manager.add(explainQueryAction); super.contributeToToolBar(manager); } }