/* * Copyright (c) 2004- michael lawley and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation * which accompanies this distribution, and is available by writing to * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Contributors: * michael lawley * * * */ package tefkat.plugin; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.util.Formatter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.BasicExtendedMetaData; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.IWorkbenchPartConstants; import org.eclipse.ui.PartInitException; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.ide.IGotoMarker; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.xsd.util.XSDResourceImpl; import tefkat.model.StratificationException; import tefkat.model.Transformation; import tefkat.model.parser.ParserEvent; import tefkat.model.parser.ParserListener; import antlr.ANTLRException; import antlr.RecognitionException; import antlr.TokenStreamHiddenTokenFilter; import antlr.debug.MessageEvent; import antlr.debug.MessageAdapter; import tefkat.model.parser.TefkatLexer; import tefkat.model.parser.TefkatMessageEvent; import tefkat.model.parser.TefkatParser; /** * @author lawley * */ public class TefkatModelEditor extends MultiPageEditorPart { final private static String PARSE_ERROR = TefkatPlugin.PLUGIN_ID + ".parser.error"; private ParserThread runner = new ParserThread(); private static Map SERIALIZATION_OPTIONS; static { SERIALIZATION_OPTIONS = new HashMap(); SERIALIZATION_OPTIONS.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.TRUE); SERIALIZATION_OPTIONS.put(XMLResource.OPTION_EXTENDED_META_DATA, new BasicExtendedMetaData()); SERIALIZATION_OPTIONS.put(XSDResourceImpl.XSD_TRACK_LOCATION, Boolean.TRUE); } private int EDITOR_PAGE = -1; private int XMI_PAGE = -1; private int STRATIFICATION_PAGE = -1; private TextEditor textEditor; private StyledText xmiText; private StyledText stratificationText; private TefkatModelOutlinePage outline = null; private Map startCharMap = new HashMap(); private Map endCharMap = new HashMap(); /** * */ public TefkatModelEditor() { super(); } /* (non-Javadoc) * @see org.eclipse.ui.IWorkbenchPart#dispose() */ public void dispose() { // TODO Auto-generated method stub super.dispose(); } /* (non-Javadoc) * @see org.eclipse.ui.part.MultiPageEditorPart#createPages() */ public void createPages() { createEditorPage(); createXMIPage(); createStratificationPage(); // IActionBars actionBars = getEditorSite().getActionBars(); // System.out.println(actionBars.getGlobalActionHandler(ActionFactory.PRINT.getId())); // System.out.println(ActionFactory.PRINT); // actionBars.setGlobalActionHandler(ActionFactory.PRINT.getId(), ActionFactory.PRINT.create(getEditorSite().getWorkbenchWindow())); } private void createEditorPage() { textEditor = new TefkatTextEditor(); textEditor.addPropertyListener(new IPropertyListener() { public void propertyChanged(final Object source, final int propId) { if (IWorkbenchPartConstants.PROP_DIRTY == propId && !textEditor.isDirty()) { runner.requestParse(); } } }); try { EDITOR_PAGE = addPage(textEditor, getEditorInput()); setPartName(getEditorInput().getName()); setPageText(EDITOR_PAGE, "Transformation"); } catch (PartInitException e) { ErrorDialog.openError( getSite().getShell(), "Error creating nested text editor", null, e.getStatus()); } } private void createXMIPage() { // EcoreEditor ecoreEditor = new org.eclipse.emf.ecore.presentation.EcoreEditor(); // ecoreEditor.createPartControl(getContainer()); Composite composite = new Composite(getContainer(), SWT.NONE); FillLayout layout = new FillLayout(); composite.setLayout(layout); xmiText = new StyledText(composite, SWT.H_SCROLL | SWT.V_SCROLL); xmiText.setEditable(false); XMI_PAGE = addPage(composite); setPageText(XMI_PAGE, "XMI Preview"); } private void createStratificationPage() { Composite composite = new Composite(getContainer(), SWT.NONE); FillLayout layout = new FillLayout(); composite.setLayout(layout); stratificationText = new StyledText(composite, SWT.H_SCROLL | SWT.V_SCROLL); stratificationText.setEditable(false); STRATIFICATION_PAGE = addPage(composite); setPageText(STRATIFICATION_PAGE, "Stratification"); } protected void pageChange(int newPageIndex) { super.pageChange(newPageIndex); if (newPageIndex == XMI_PAGE) { runner.requestParse(); } else if (newPageIndex == STRATIFICATION_PAGE) { runner.requestParse(); } } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor) */ public void doSave(IProgressMonitor monitor) { getEditor(EDITOR_PAGE).doSave(monitor); } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#doSaveAs() */ public void doSaveAs() { IEditorPart editor = getEditor(0); editor.doSaveAs(); setPageText(EDITOR_PAGE, editor.getTitle()); setInput(editor.getEditorInput()); } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#gotoMarker(org.eclipse.core.resources.IMarker) */ public void gotoMarker(IMarker marker) { setActivePage(EDITOR_PAGE); IGotoMarker gotoMarker = (IGotoMarker) getEditor(0).getAdapter(IGotoMarker.class); if (gotoMarker != null) { gotoMarker.gotoMarker(marker); } } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed() */ public boolean isSaveAsAllowed() { return true; } private final class TefkatTextEditor extends TextEditor { public TefkatTextEditor() { super(); setSourceViewerConfiguration(new TefkatModelSourceViewerConfiguration()); } /* (non-Javadoc) * @see org.eclipse.ui.IWorkbenchPart#dispose() */ public void dispose() { // TODO Auto-generated method stub super.dispose(); } /* (non-Javadoc) * @see org.eclipse.ui.texteditor.AbstractTextEditor#selectAndReveal(int, int, int, int) */ protected void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) { pageChange(EDITOR_PAGE); super.selectAndReveal(selectionStart, selectionLength, revealStart, revealLength); } } class ParserThread extends Thread { private volatile boolean parseRequested = false; synchronized final public void requestParse() { if (!parseRequested && !isAlive()) { start(); } parseRequested = true; notify(); } final public void run() { boolean doWork = false; while (!getContainer().isDisposed()) { synchronized (this) { if (parseRequested) { doWork = true; parseRequested = false; } else { doWork = false; try { wait(); } catch (InterruptedException e) { } } } if (doWork) { try { doParse(); try { Thread.sleep(500); // throttle the parsing } catch (InterruptedException e) { } } catch (Throwable t) { t.printStackTrace(); } } } } private void doParse() { if (null == textEditor.getEditorInput()) { // nothing to do return; } // TefkatPlugin.getPlugin().clearResourceSet(); // ResourceSet resourceSet = TefkatPlugin.getPlugin().getResourceSet(); ResourceSet resourceSet = new ResourceSetImpl(); // Need to use a new ExtendedMetaData instance to avoid cached ePackage instances. // SERIALIZATION_OPTIONS.put(XMLResource.OPTION_EXTENDED_META_DATA, new BasicExtendedMetaData(resourceSet.getPackageRegistry())); resourceSet.getLoadOptions().putAll(SERIALIZATION_OPTIONS); final IResource resource = (IResource) textEditor.getEditorInput().getAdapter(IResource.class); try { resource.deleteMarkers(IMarker.PROBLEM, false, IResource.DEPTH_INFINITE); resource.deleteMarkers(PARSE_ERROR, true, IResource.DEPTH_INFINITE); } catch (CoreException e1) { // TODO Log this } String editorText = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()).get(); Reader in = new StringReader(editorText); final TefkatLexer lexer = new TefkatLexer(in); lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken"); // create the filter TokenStreamHiddenTokenFilter filter = new TokenStreamHiddenTokenFilter(lexer); // hide not discard filter.hide(TefkatParser.COMMENT); filter.hide(TefkatParser.WS); TefkatParser parser = new TefkatParser(filter); final MessageAdapter messageListener = new MessageAdapter() { public void reportError(MessageEvent e) { if (e instanceof TefkatMessageEvent) { final TefkatMessageEvent tme = (TefkatMessageEvent) e; final Object src = tme.getSource(); createErrorMarker(resource, getMessage(tme.getLine(), e.getText()), tme.getLocation(), tme.getLine(), tme.getCharStart(), tme.getCharEnd()); } else { createErrorMarker(resource, e.getText(), lexer.getLine()); } } public void reportWarning(MessageEvent e) { if (e instanceof TefkatMessageEvent) { final TefkatMessageEvent tme = (TefkatMessageEvent) e; createWarningMarker(resource, getMessage(tme.getLine(), e.getText()), tme.getLine(), tme.getCharStart(), tme.getCharEnd()); } else { createWarningMarker(resource, e.getText(), lexer.getLine()); } } private String getMessage(int line, String msg) { Object[] args = {line, msg}; return new Formatter().format("%4d: %s", args).toString(); } }; parser.addMessageListener(messageListener); // store map of char position to parse terms startCharMap.clear(); endCharMap.clear(); parser.addParserListener(new ParserListener() { public void matched(ParserEvent e) { startCharMap.put(e.getObj(), new Integer(e.getStartChar())); endCharMap.put(e.getObj(), new Integer(e.getEndChar())); } }); URI uri = URI.createPlatformResourceURI(resource.getFullPath().toString()); final Resource res = resourceSet.createResource(uri); final OutputStream out = new ByteArrayOutputStream(); final StringBuffer sb = new StringBuffer(); try { parser.setResource(res); final Transformation transformation = parser.transformation(); res.save(out, SERIALIZATION_OPTIONS); // ModelUtils.resolveTrackingClassNames(transformation, parser.trackingMap); printStrata(sb, transformation.getStrata()); } catch (final RecognitionException e) { createErrorMarker(resource, e.toString(), e.getLine()); } catch (final ANTLRException e) { createErrorMarker(resource, e.toString(), lexer.getLine()); } catch (final StratificationException e) { createErrorMarker(resource, e.toString(), lexer.getLine()); sb.append(e.getMessage()).append("\n"); printStrata(sb, e.getStrata()); } catch (final Exception e) { createErrorMarker(resource, e.getMessage(), "line " + lexer.getLine(), lexer.getLine(), 1, 1); } finally { parser.removeMessageListener(messageListener); Display.getDefault().asyncExec(new Runnable() { public void run() { if (getContainer().isDisposed()) { return; } if (null != outline) { outline.setResource(res); } xmiText.setText(out.toString()); stratificationText.setText(sb.toString()); } }); } } private void printStrata(final StringBuffer sb, final List[] strata) { for (int i = 0; i < strata.length; i++) { if (strata[i].size() > 0) { sb.append("Stratum: ").append(i).append("\n"); for (Iterator itr = strata[i].iterator(); itr.hasNext(); ) { Object scope = itr.next(); sb.append("\t").append(scope).append("\n"); } } } } } private void createErrorMarker(final IResource resource, final String message, final int line) { createErrorMarker(resource, message, "line " + line, line, -1, -1); } private void createErrorMarker(final IResource resource, final String message, final String location, final int line, final int start, final int end) { System.err.println(message); Display.getDefault().asyncExec(new Runnable() { public void run() { try { Map map = new HashMap(6); if (start >= 0) { map.put(IMarker.CHAR_START, new Integer(start)); } if (end >= 0) { map.put(IMarker.CHAR_END, new Integer(end)); } if (line > 0) { map.put(IMarker.LINE_NUMBER, new Integer(line)); } map.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR)); map.put(IMarker.MESSAGE, message); map.put(IMarker.LOCATION, location); createMarker(resource, map); } catch (CoreException e) { // TODO Log later e.printStackTrace(); } } }); } private void createWarningMarker(final IResource resource, final String message, final int line) { createWarningMarker(resource, message, line, -1, -1); } private void createWarningMarker(final IResource resource, final String message, final int line, final int start, final int end) { Display.getDefault().asyncExec(new Runnable() { public void run() { try { Map map = new HashMap(6); if (start >= 0) { map.put(IMarker.CHAR_START, new Integer(start)); } if (end >= 0) { map.put(IMarker.CHAR_END, new Integer(end)); } if (line > 0) { map.put(IMarker.LINE_NUMBER, new Integer(line)); } map.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING)); map.put(IMarker.MESSAGE, message); map.put(IMarker.LOCATION, message); createMarker(resource, map); } catch (CoreException e) { // TODO Log later e.printStackTrace(); } } }); } private void createMarker(final IResource resource, final Map map) throws CoreException { ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { IMarker marker = resource.createMarker(PARSE_ERROR); marker.setAttributes(map); } }, null); } public Object getAdapter(Class adapter) { if (adapter.equals(IContentOutlinePage.class)) { if (null == outline) { outline = new TefkatModelOutlinePage(); outline.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); int s = Integer.MAX_VALUE; int e = Integer.MIN_VALUE; for (final Iterator itr = ((IStructuredSelection) selection).iterator(); itr.hasNext(); ) { final Object selectionElement = itr.next(); if (selectionElement instanceof EObject) { EObject obj = (EObject) selectionElement; Integer startChar = getStartChar(obj); Integer endChar = getEndChar(obj); if (null != startChar && startChar.intValue() >= 0 && null != endChar && endChar.intValue() >= 0) { s = Math.min(s, startChar.intValue()); e = Math.max(e, endChar.intValue()); } } } if (s <= e) { textEditor.setHighlightRange(s, e - s, true); textEditor.selectAndReveal(s, (e - s)); } else { textEditor.resetHighlightRange(); } } }); } return outline; } else if (adapter.equals(ITextEditor.class)) { return textEditor; } return super.getAdapter(adapter); } public Integer getStartChar(EObject obj) { while (obj != null) { Integer pos = (Integer) startCharMap.get(obj); if (pos != null) { return pos; } obj = obj.eContainer(); } return null; } public Integer getEndChar(EObject obj) { while (obj != null) { Integer pos = (Integer) endCharMap.get(obj); if (pos != null) { return pos; } obj = obj.eContainer(); } return null; } }