/******************************************************************************* * 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.internal.ui.editor; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.regex.Pattern; import org.deved.antlride.common.ui.AntlrImages; import org.deved.antlride.core.AntlrCore; import org.deved.antlride.core.AntlrLanguageTargetName; import org.deved.antlride.core.build.AntlrBuildUnit; import org.deved.antlride.core.build.AntlrBuildUnitRepository; import org.deved.antlride.core.interpreter.AntlrInterpreter; import org.deved.antlride.core.interpreter.DefaultAntlrInterpreter; import org.deved.antlride.core.launch.AntlrLauncher; import org.deved.antlride.core.model.IGrammar; import org.deved.antlride.core.model.IRule; import org.deved.antlride.core.model.ast.ModelElementQuery; import org.deved.antlride.core.model.evaluation.AntlrResultListener; import org.deved.antlride.core.model.evaluation.IEvalElement; import org.deved.antlride.core.model.evaluation.IResultEvalElement; import org.deved.antlride.core.model.test.AntlrTestCase; import org.deved.antlride.core.model.test.AntlrTestSuite; import org.deved.antlride.internal.ui.views.interpreter.AntlrInterpreterMessages; import org.deved.antlride.ui.AntlrUIHelper; import org.deved.antlride.ui.text.AntlrInputSourceViewer; import org.deved.antlride.ui.text.AntlrTextSelection; import org.deved.antlride.ui.views.interpreter.EvalElementViewer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.text.Document; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.services.IDisposable; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; public class AntlrInterpreterPage extends Viewer implements IDisposable { private class DropDownMenu extends Action implements IMenuCreator { private Menu fMenu; private Action[] fDelegates; public DropDownMenu(Action... delegates) { super(delegates[0].getText(), delegates.length > 1 ? IAction.AS_DROP_DOWN_MENU : IAction.AS_PUSH_BUTTON); setImageDescriptor(delegates[0].getImageDescriptor()); setToolTipText(delegates[0].getToolTipText()); setMenuCreator(this); fDelegates = delegates; } public void dispose() { if (fMenu != null) fMenu.dispose(); fDelegates = null; } public Menu getMenu(Control parent) { fMenu = new Menu(parent); for (int i = 1; i < fDelegates.length; i++) { ActionContributionItem item = new ActionContributionItem( fDelegates[i]); item.fill(fMenu, -1); } return fMenu; } public Menu getMenu(Menu parent) { return null; } @Override public void run() { fDelegates[0].run(); } @Override public void setEnabled(boolean enabled) { for (int i = 1; i < fDelegates.length; i++) { fDelegates[i].setEnabled(enabled); } // super.setEnabled(enabled); } public void setEnabledAll(boolean enabled) { for (int i = 0; i < fDelegates.length; i++) { fDelegates[i].setEnabled(enabled); } super.setEnabled(enabled); } } private class ArrayContentProvider implements ITreeContentProvider { public Object[] getChildren(Object parentElement) { return null; } public Object getParent(Object element) { return null; } public boolean hasChildren(Object element) { return false; } public Object[] getElements(Object element) { return (Object[]) element; } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } private class RunAction extends Action { public RunAction() { setToolTipText(AntlrInterpreterMessages.GrammarInterpreter_Run); setImageDescriptor(AntlrImages.getDescriptor(AntlrImages.RUN)); } @Override public void run() { runTest(sourceViewer.getTextWidget().getText()); } } private class RunJavaAction extends Action { public RunJavaAction() { setText("Run (Java)"); setToolTipText("Run (Java)"); setImageDescriptor(AntlrImages.getDescriptor(AntlrImages.RUN_JAVA)); } @Override public void run() { launch(AntlrLanguageTargetName.Java, "run"); } } private class DebugJavaAction extends Action { public DebugJavaAction() { setText("Debug (Java)"); setToolTipText("Debug (Java)"); setImageDescriptor(AntlrImages .getDescriptor(AntlrImages.DEBUG_JAVA)); } @Override public void run() { launch(AntlrLanguageTargetName.Java, "debug"); } } private class NewTestCaseAction extends Action { public NewTestCaseAction() { setToolTipText("New test case"); setImageDescriptor(AntlrImages .getDescriptor(AntlrImages.NEW_TEST_CASE)); } @Override public void run() { selectTestCase(null); } } private class SaveTestCaseAction extends Action { public SaveTestCaseAction() { setToolTipText("Save test case"); setImageDescriptor(AntlrImages .getDescriptor(AntlrImages.SAVE_TEST_CASE)); } @Override public void run() { String input = (String) ((Document) sourceViewer.getInput()).get(); if (selectedTestCase == null) { InputDialog dlg = new InputDialog(getControl().getShell(), "Save test case", "Name:", "", new IInputValidator() { public String isValid(String text) { boolean matches = Pattern.matches( "[a-zA-Z][a-zA-Z_0-9\\-]*", text); if (!matches) return "Invalid test name"; return testSuite.exists(selectedRule .getElementName(), text) ? "Duplicated test name" : null; } }); if (dlg.open() == InputDialog.OK) { selectedTestCase = testSuite.create(selectedRule .getElementName(), dlg.getValue(), input); } } if (selectedTestCase != null) { try { testSuite.save(selectedTestCase, input); AntlrTestCase[] list = getTestList(selectedRule); testViewer.setInput(list); testViewer.setSelection(new StructuredSelection( selectedTestCase)); runTest(sourceViewer.getTextWidget().getText()); } catch (IOException e1) { e1.printStackTrace(); } } } } private Control control; private TreeViewer ruleViewer; private Text ruleNameText; private IGrammar grammar; private Text testNameText; private TreeViewer testViewer; private IRule selectedRule; private Label description; private AntlrInputSourceViewer sourceViewer; private AntlrTestCase selectedTestCase; private AntlrTestSuite testSuite; private AntlrInterpreter interpreter; private EvalElementViewer evalViewer; private ToolItem clearRuleFilter; private ToolItem clearTestFilter; private boolean rebuild; private AntlrMultiPageEditor pageEditor; private DropDownMenu dropDownMenu; private boolean nativeLaunchersEnabled; private NewTestCaseAction newTestCaseAction; private SaveTestCaseAction saveTestCaseAction; public AntlrInterpreterPage(AntlrMultiPageEditor pageEditor, Composite composite) { control = createControl(composite); this.pageEditor = pageEditor; } private void createRulePanel(Composite parent) { Composite container = new Composite(parent, SWT.NONE); container.setLayout(new GridLayout(3, false)); Label label = new Label(container, SWT.NONE); label.setText("Rule:"); label.setToolTipText("Search Rule"); ruleNameText = new Text(container, SWT.BORDER); GridData gd = new GridData(GridData.FILL_HORIZONTAL); ruleNameText.setLayoutData(gd); ruleNameText.setToolTipText("Search Rule"); ruleNameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { setRules(grammar); } }); ToolBar bar = new ToolBar(container, SWT.HORIZONTAL); clearRuleFilter = new ToolItem(bar, SWT.PUSH); clearRuleFilter.setToolTipText("Clear"); clearRuleFilter.setImage(AntlrImages.getImage(AntlrImages.CLEAR)); clearRuleFilter.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { ruleNameText.setText(""); setRules(grammar); } public void widgetDefaultSelected(SelectionEvent e) { } }); ruleViewer = new TreeViewer(container, SWT.SINGLE | SWT.FULL_SELECTION); gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 3; ruleViewer.setUseHashlookup(true); ruleViewer.getControl().setLayoutData(gd); ruleViewer.addFilter(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { IRule rule = (IRule) element; String startWith = ruleNameText.getText(); if (startWith.length() > 0) { return rule.getElementName().startsWith(startWith); } return true; } }); ruleViewer.setContentProvider(new ArrayContentProvider()); ruleViewer.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { return ((IRule) element).getElementName(); } @Override public Image getImage(Object element) { return AntlrImages.getImage(AntlrImages.RULE); } }); ruleViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event .getSelection(); IRule rule = null; if (!selection.isEmpty()) { rule = (IRule) selection.getFirstElement(); } AntlrTestCase[] list = getTestList(rule); testViewer.setInput(list); updateRuleSelection(rule); dropDownMenu.setEnabled(false); } }); } private void createTestPanel(Composite parent) { Composite container = new Composite(parent, SWT.NONE); container.setLayout(new GridLayout(3, false)); Label label = new Label(container, SWT.NONE); label.setText("Test:"); label.setToolTipText("Search Test"); testNameText = new Text(container, SWT.BORDER); GridData gd = new GridData(GridData.FILL_HORIZONTAL); testNameText.setLayoutData(gd); testNameText.setToolTipText("Search Test"); testNameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { testViewer.setInput(getTestList(selectedRule)); selectTest(0); } }); ToolBar bar = new ToolBar(container, SWT.HORIZONTAL); clearTestFilter = new ToolItem(bar, SWT.PUSH); clearTestFilter.setToolTipText("Clear"); clearTestFilter.setImage(AntlrImages.getImage(AntlrImages.CLEAR)); clearTestFilter.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { testNameText.setText(""); testViewer.setInput(getTestList(selectedRule)); } public void widgetDefaultSelected(SelectionEvent e) { } }); testViewer = new TreeViewer(container, SWT.SINGLE | SWT.FULL_SELECTION); testViewer.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { AntlrTestCase testCase = (AntlrTestCase) element; return testCase.getName(); } }); testViewer.addFilter(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { AntlrTestCase testcase = (AntlrTestCase) element; String startWith = testNameText.getText(); return testcase.getName().startsWith(startWith); } }); testViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); AntlrTestCase testCase = null; if (!selection.isEmpty()) { testCase = (AntlrTestCase) ((IStructuredSelection) selection) .getFirstElement(); } selectTestCase(testCase); } }); testViewer.setContentProvider(new ArrayContentProvider()); gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 3; testViewer.getControl().setLayoutData(gd); } protected Control createControl(Composite parent) { SashForm mainPanel = new SashForm(parent, SWT.HORIZONTAL); // rulePanel | inputPanel // ----------|----------- // testPanel | graphPanel SashForm leftPanel = new SashForm(mainPanel, SWT.VERTICAL); createRulePanel(leftPanel); createTestPanel(leftPanel); SashForm rightPanel = new SashForm(mainPanel, SWT.VERTICAL); createInputPanel(rightPanel); createGraphPanel(rightPanel); rightPanel.setWeights(new int[] { 30, 70 }); mainPanel.setWeights(new int[] { 20, 80 }); return mainPanel; } private void createGraphPanel(Composite parent) { GridData gd; Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, true)); ToolBar bar = new ToolBar(composite, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalAlignment = SWT.END; bar.setLayoutData(gd); evalViewer = new EvalElementViewer(composite); gd = new GridData(GridData.FILL_BOTH); evalViewer.getControl().setLayoutData(gd); evalViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { AntlrUIHelper.select(sourceViewer.getTextWidget(), (AntlrTextSelection) event.getSelection()); } }); // add actions IToolBarManager barManager = new ToolBarManager(bar); barManager.add(evalViewer.getAction(EvalElementViewer.ZOOM_IN_ACTION)); barManager.add(evalViewer.getAction(EvalElementViewer.ZOOM_OUT_ACTION)); barManager.add(evalViewer .getAction(EvalElementViewer.CLEAR_DIAGRAM_ACTION)); barManager.add(new Separator()); barManager.add(evalViewer.getAction(EvalElementViewer.EXPORT_ACTION)); barManager.update(true); } private void createInputPanel(Composite parent) { GridData gd; Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(2, false)); description = new Label(composite, SWT.NONE); gd = new GridData(GridData.FILL_HORIZONTAL); description.setLayoutData(gd); ToolBar bar = new ToolBar(composite, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); Collection<Action> dropdownActions = new ArrayList<Action>(3); RunAction run = new RunAction(); dropdownActions.add(run); AntlrLauncher[] launchers = getLaunchers(AntlrLanguageTargetName.Java, null); if (launchers.length > 0) { dropdownActions.add(new RunJavaAction()); dropdownActions.add(new DebugJavaAction()); } // DropDownMenu ToolBarManager barManager = new ToolBarManager(bar); dropDownMenu = new DropDownMenu(dropdownActions .toArray(new Action[dropdownActions.size()])); barManager.add(dropDownMenu); barManager.add(new Separator()); newTestCaseAction = new NewTestCaseAction(); barManager.add(newTestCaseAction); saveTestCaseAction = new SaveTestCaseAction(); barManager.add(saveTestCaseAction); barManager.update(true); sourceViewer = new AntlrInputSourceViewer(composite, true); gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 2; sourceViewer.getControl().setLayoutData(gd); sourceViewer.getTextWidget().addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.F5) { runTest(sourceViewer.getTextWidget().getText()); } } }); } private AntlrLauncher[] getLaunchers(AntlrLanguageTargetName language, String filter) { try { Bundle bundle = AntlrCore.getDefault().getBundle(); BundleContext bundleContext = bundle.getBundleContext(); ServiceReference[] serviceReferences = bundleContext .getServiceReferences(AntlrLauncher.class.getName(), filter); if (serviceReferences != null) { AntlrLauncher[] launchers = new AntlrLauncher[serviceReferences.length]; for (int i = 0; i < launchers.length; i++) { launchers[i] = (AntlrLauncher) bundleContext .getService(serviceReferences[i]); } return launchers; } } catch (InvalidSyntaxException e) { e.printStackTrace(); } return new AntlrLauncher[0]; } private void launch(AntlrLanguageTargetName language, String mode) { try { evalViewer.clear(); AntlrLauncher[] launchers = getLaunchers(language, "(mode=" + mode + ")"); AntlrBuildUnit unit = AntlrBuildUnitRepository.create(grammar); launchers[0].setResultListener(new AntlrResultListener() { public void setResult(IEvalElement input) { evalViewer.setInput(input); } }); if ("debug".equals(mode)) { pageEditor.setActivePage(0); } launchers[0].launch(rebuild, unit, selectedTestCase); if (rebuild) { rebuild = false; } } catch (CoreException e) { e.printStackTrace(); } } private void runTest(String text) { IResultEvalElement result = interpreter.interpret(selectedRule, text); IEvalElement evalElement = result.getResult(); evalViewer.setInput(evalElement); } @Override public Control getControl() { return control; } public void dispose() { if (interpreter != null) interpreter.endSession(); if (sourceViewer != null) sourceViewer.dispose(); if (evalViewer != null) evalViewer.dispose(); } @Override public Object getInput() { return null; } @Override public ISelection getSelection() { return null; } @Override public void refresh() { } @Override public void setInput(Object input) { rebuild = false; if (grammar != input) { grammar = (IGrammar) input; enabled(!grammar.isTreeParserGrammar()); rebuild = true; sourceViewer.setInput(""); sourceViewer.setGrammar(grammar); nativeLaunchersEnabled = grammar.isCombinedGrammar() || grammar.isParserGrammar(); evalViewer.clear(); if (interpreter != null) { interpreter.endSession(); } ProgressMonitorDialog dlg = new ProgressMonitorDialog(getControl() .getShell()); try { dlg.run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { interpreter = new DefaultAntlrInterpreter(); final IStatus status = interpreter.beginSession( monitor, grammar); if (status.isOK()) { testSuite = interpreter.getTestSuite(); Display.getDefault().asyncExec(new Runnable() { public void run() { setRules(grammar); } }); } else { Display.getDefault().asyncExec(new Runnable() { public void run() { enabled(false); updateRuleSelection(null); ErrorDialog.openError(getControl() .getShell(), "Interpreter", "The interpreter is disabled", status); } }); } } }); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void enabled(boolean enabled) { ruleNameText.setEditable(enabled); testNameText.setEditable(enabled); clearRuleFilter.setEnabled(enabled); clearTestFilter.setEnabled(enabled); newTestCaseAction.setEnabled(enabled); saveTestCaseAction.setEnabled(enabled); dropDownMenu.setEnabledAll(enabled); } private void setRules(IGrammar grammar) { IRule[] rules = ModelElementQuery.collectRules(grammar, true); ruleViewer.setInput(rules); if (rules.length > 0) { selectRule(0); } } private void selectTestCase(AntlrTestCase testCase) { StringBuilder buffer = new StringBuilder(); sourceViewer.setInput(""); evalViewer.clear(); if (selectedRule != null) { buffer.append(selectedRule.getElementName()); } try { if (testCase != null) { sourceViewer.setInput(testCase.getInput()); buffer.append(" :: "); buffer.append(testCase.getName()); } } catch (IOException e) { e.printStackTrace(); } dropDownMenu.setEnabled(testCase != null && nativeLaunchersEnabled); description.setText(buffer.toString()); this.selectedTestCase = testCase; } private AntlrTestCase[] getTestList(IRule rule) { if (rule != null) { try { AntlrTestCase[] list = testSuite.list(rule.getElementName()); return list; } catch (IOException e) { e.printStackTrace(); } } return new AntlrTestCase[0]; } private void selectRule(int i) { TreeItem[] items = ruleViewer.getTree().getItems(); if (i >= 0 && i < items.length) { IRule rule = (IRule) items[i].getData(); ruleViewer.setSelection(new StructuredSelection(rule)); } else { updateRuleSelection(null); } } private void selectTest(int i) { TreeItem[] items = testViewer.getTree().getItems(); if (i >= 0 && i < items.length) { AntlrTestCase testcase = (AntlrTestCase) items[i].getData(); testViewer.setSelection(new StructuredSelection(testcase)); } } private void updateRuleSelection(IRule rule) { boolean buttonsEnabled = rule != null; dropDownMenu.setEnabled(buttonsEnabled && nativeLaunchersEnabled); description.setText(rule == null ? "" : rule.getElementName()); this.selectedRule = rule; } @Override public void setSelection(ISelection selection, boolean reveal) { } }