org.absmodels.abs.plugin.editor.ABSEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.absmodels.abs.plugin.editor.ABSEditor.java

Source

/** 
 * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. 
 * This file is licensed under the terms of the Modified BSD License.
 */
package org.absmodels.abs.plugin.editor;

import static org.absmodels.abs.plugin.util.Constants.*;
import static org.absmodels.abs.plugin.util.UtilityFunctions.standardExceptionHandling;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.absmodels.abs.plugin.Activator;
import org.absmodels.abs.plugin.builder.AbsNature;
import org.absmodels.abs.plugin.editor.decoration.ABSDecorationSupport;
import org.absmodels.abs.plugin.editor.outline.ABSContentOutlinePage;
import org.absmodels.abs.plugin.editor.outline.PackageAbsFileEditorInput;
import org.absmodels.abs.plugin.editor.reconciling.ABSReconcilingStrategy;
import org.absmodels.abs.plugin.editor.reconciling.AbsModelManager;
import org.absmodels.abs.plugin.editor.reconciling.CompilationUnitChangeListener;
import org.absmodels.abs.plugin.util.Constants;
import org.absmodels.abs.plugin.util.CoreControlUnit;
import org.absmodels.abs.plugin.util.InternalASTNode;
import org.absmodels.abs.plugin.util.UtilityFunctions;
import org.absmodels.abs.plugin.util.CoreControlUnit.ResourceBuildListener;
import org.absmodels.abs.plugin.util.CoreControlUnit.ResourceBuiltEvent;
import org.absmodels.abs.plugin.util.UtilityFunctions.EditorPosition;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import abs.frontend.ast.ASTNode;
import abs.frontend.ast.CompilationUnit;

/**
 * The editor for ABS file. Includes syntax highlighting and content assist for ABS files
 * as well as annotations for ABS errors.
 * 
 * @author tfischer, cseise, fstrauss, mweber
 *
 */
public class ABSEditor extends TextEditor implements CompilationUnitChangeListener {

    private final class UpdateEditorIcon implements ResourceBuildListener {
        @Override
        public void resourceBuilt(ResourceBuiltEvent builtevent) {
            final IResource editorres = (IResource) ABSEditor.this.getEditorInput().getAdapter(IResource.class);
            if (builtevent.hasChanged(editorres)) {

                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        updateEditorIcon(editorres);
                        // workaround for squiggly lines not vanishing after rebuild
                        //refresh();
                    }
                });
            }
        }
    }

    private ABSContentOutlinePage outlinePage = null;
    private ResourceBuildListener builtListener;
    private IPropertyChangeListener propertyChangeListener;
    private List<CompilationUnitChangeListener> modelChangeListeners = new ArrayList<CompilationUnitChangeListener>();
    private CompilationUnit compilationUnit;
    private ABSReconcilingStrategy reconciler;
    private volatile int caretPos;
    private AbsInformationPresenter informationPresenter;

    public ABSEditor() {
        super();
        setSourceViewerConfiguration(new ABSSourceViewerConfiguration(this));
        setDocumentProvider(new ABSDocumentProvider());
        builtListener = new UpdateEditorIcon();
        CoreControlUnit.addResourceBuildListener(builtListener);
        // if preferences of syntax highlighting change: Reinstall the PresentationReconciler to make the changes appear.
        propertyChangeListener = new IPropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent event) {
                ISourceViewer sourceviewer = ABSEditor.this.getSourceViewer();
                getSourceViewerConfiguration().getPresentationReconciler(sourceviewer).install(sourceviewer);
            }
        };
        Activator.getDefault().getPreferenceStore().addPropertyChangeListener(propertyChangeListener);

        //      this.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
        //         
        //         @Override
        //         public void selectionChanged(SelectionChangedEvent event) {
        //            System.out.println("selection = " + event.getSelection());
        //            
        //         }
        //      });
        //      this.getSourceViewer().getSelectedRange();
        //      StyledText styledText = (StyledText) getAdapter(Control.class);
        //      styledText.addCaretListener(new CaretListener() {
        //         
        //         @Override
        //         public void caretMoved(CaretEvent e) {
        //            System.out.println(e.caretOffset);
        //            
        //         }
        //      });

    }

    /**
     * Reinitializes the editor's source viewer based on the old editor input / document.
     *
     */
    private void reinitializeSourceViewer() {
        IEditorInput input = getEditorInput();
        IDocumentProvider documentProvider = getDocumentProvider();
        IAnnotationModel model = documentProvider.getAnnotationModel(input);
        IDocument document = documentProvider.getDocument(input);
        ISourceViewer fSourceViewer = getSourceViewer();

        if (document != null) {
            fSourceViewer.setDocument(document, model);
        }
    }

    /**
     * highlights the given line as the current instruction point
     * @param line the line the debugger is currently running in
     */
    public void highlightLine(int line) {
        IDocument doc = getDocumentProvider().getDocument(getEditorInput());
        int lineOffset;
        try {
            lineOffset = doc.getLineOffset(line);

            IResource resource = getResource();
            if (resource != null) { // can be null for files inside jars

                resource.deleteMarkers(Constants.CURRENT_IP_MARKER, false, IResource.DEPTH_ZERO);

                getSourceViewer().invalidateTextPresentation();

                IMarker marker = resource.createMarker(Constants.CURRENT_IP_MARKER);
                marker.setAttribute(IMarker.LINE_NUMBER, line);
                marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
                marker.setAttribute(IMarker.MESSAGE, "current instruction pointer");

            }
            if (getSourceViewer() instanceof SourceViewer) {
                SourceViewer sourceviewer = (SourceViewer) getSourceViewer();
                sourceviewer.setSelection(new TextSelection(lineOffset, 0), true);
                //sourceviewer.refresh();
            }
        } catch (CoreException e) {
            standardExceptionHandling(e);
        } catch (BadLocationException e) {
            standardExceptionHandling(e);
        }
    }

    /**
     * Highlights the given {@link ASTNode} in the editor.<br/>
     * Only one ASTNode at a time can be highlighted (This is a restriction of {@link ITextEditor}}).<br/><br/>
      *
     * @param edit The target editor
     * @param node The node that should be highlighted.
     */
    void highlightInEditor(ASTNode<?> node, boolean moveCursor) {

        EditorPosition pos = UtilityFunctions.getPosition(node);

        if (pos != null) {
            IDocument doc = getDocumentProvider().getDocument(getEditorInput());
            try {
                /* Calculate the position on the editor by retrieving the char offset
                 * of the position line and the target column in this line.
                 */
                int startOff = doc.getLineOffset(pos.getLinestart()) + pos.getColstart();
                int endOff = doc.getLineOffset(pos.getLineend()) + pos.getColend();

                if (startOff < 0) {
                    // TODO: This happens sometimes - new parser?
                    Activator.logException(new IllegalArgumentException("FIXME"));
                    return;
                }
                assert startOff > -1;
                assert endOff >= startOff;
                setHighlightRange(startOff, endOff - startOff, moveCursor);
            } catch (BadLocationException e) {
                /*
                 * Should not be thrown, as an ASTNode in a document must have a
                 * specific location.
                 */
            }
        }
    }

    /**
     * Highlights the a given {@link InternalASTNode} in the editor.<br/>
     * @see #highlightInEditor(ITextEditor, ASTNode)
     */
    public void highlightInEditor(InternalASTNode<?> node, boolean moveCursor) {
        if (node != null) {
            highlightInEditor(node.getASTNode(), moveCursor);
        }
    }

    /**
     * removes the the highlighting set by {@link #highlightLine(int)}.
     */
    public void removeHighlighting() {
        IResource resource = getResource();
        if (resource == null) {
            // can be null for files inside jars
            return;
        }
        try {
            resource.deleteMarkers(Constants.CURRENT_IP_MARKER, false, IResource.DEPTH_INFINITE);
            getSourceViewer().invalidateTextPresentation();
        } catch (CoreException e) {
            standardExceptionHandling(e);
        }
    }

    @Override
    protected SourceViewerDecorationSupport getSourceViewerDecorationSupport(ISourceViewer viewer) {
        if (fSourceViewerDecorationSupport == null) {
            fSourceViewerDecorationSupport = new ABSDecorationSupport(viewer, getOverviewRuler(),
                    getAnnotationAccess(), getSharedColors());
            configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport);
        }
        return fSourceViewerDecorationSupport;
    }

    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        super.init(site, input);

        // activate abseditorscope when part is active
        // for example: F3 (JumpToDeclaration) is only active if this editor scope is enabled
        IContextService cs = (IContextService) getSite().getService(IContextService.class);
        cs.activateContext(ABSEDITOR_CONTEXT_ID);
    }

    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        // listen to changes of the caret position:
        getSelectionProvider().addPostSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                ISelection selection = event.getSelection();
                if (selection instanceof ITextSelection) {
                    ITextSelection ts = (ITextSelection) selection;
                    caretPos = ts.getOffset();
                }
            }
        });
        initCompilationUnit();
    }

    public IPostSelectionProvider getSelectionProvider() {
        return (IPostSelectionProvider) super.getSelectionProvider();
    }

    private void initCompilationUnit() {
        AbsNature absNature = UtilityFunctions.getAbsNature(getResource());
        if (absNature != null) {
            AbsModelManager modelManager = absNature.getModelManager();
            compilationUnit = modelManager.getCompilationUnit(getAbsoluteFilePath());
        } else {
            // we are looking at abslang.abs or a file inside a jar-package
            IURIEditorInput uriInput = (IURIEditorInput) getEditorInput().getAdapter(IURIEditorInput.class);

            if (uriInput != null) {
                // We're looking e.g. at abslang.abs which only exists in memory.

                // create an empty model which only contains abslang.abs:
                absNature = new AbsNature();
                absNature.emptyModel();
                File f = new File(uriInput.getURI());
                String path = f.getAbsolutePath();
                compilationUnit = absNature.getCompilationUnit(path);
            }
        }
    }

    @Override
    @SuppressWarnings("rawtypes")
    public Object getAdapter(Class key) {
        if (IContentOutlinePage.class.equals(key)) {
            if (outlinePage == null) {
                outlinePage = new ABSContentOutlinePage(this);
            }
            return outlinePage;
        }
        return super.getAdapter(key);
    }

    @Override
    protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
        IPreferenceStore store = getPreferenceStore();
        store.setValue(LOCATION_TYPE_NEAR_TEXTSTYLE_KEY, LOCATION_TYPE_NEAR_TEXTSTYLE_VALUE);
        store.setValue(LOCATION_TYPE_FAR_TEXTSTYLE_KEY, LOCATION_TYPE_FAR_TEXTSTYLE_VALUE);
        store.setValue(LOCATION_TYPE_SOMEWHERE_TEXTSTYLE_KEY, LOCATION_TYPE_SOMEWHERE_TEXTSTYLE_VALUE);
        super.configureSourceViewerDecorationSupport(support);

        char[] matchChars = { '(', ')', '[', ']', '{', '}' }; //which brackets to match
        ICharacterPairMatcher matcher = new DefaultCharacterPairMatcher(matchChars,
                IDocumentExtension3.DEFAULT_PARTITIONING);
        support.setCharacterPairMatcher(matcher);
        support.setMatchingCharacterPainterPreferenceKeys(EDITOR_MATCHING_BRACKETS, EDITOR_MATCHING_BRACKETS_COLOR);

        //Enable bracket highlighting in the preference store
        store.setDefault(EDITOR_MATCHING_BRACKETS, true);
        store.setDefault(EDITOR_MATCHING_BRACKETS_COLOR, Constants.DEFAULT_MATCHING_BRACKETS_COLOR);
    }

    @Override
    protected void doSetInput(IEditorInput input) throws CoreException {
        super.doSetInput(input);
    }

    public IResource getResource() {
        return (IResource) getEditorInput().getAdapter(IResource.class);
    }

    /**
     * Throws a {@link SWTException} if the display is disposed
     * @param editorres the resource of the editor input
     */
    private void updateEditorIcon(IResource editorres) {
        try {
            int sev = editorres.findMaxProblemSeverity(MARKER_TYPE, true, IResource.DEPTH_INFINITE);
            if (sev == IMarker.SEVERITY_INFO) {
                setTitleImage(getEditorInput().getImageDescriptor().createImage());
                return;
            }
            ISharedImages simages = PlatformUI.getWorkbench().getSharedImages();
            ImageDescriptor overlayIcon = null;
            switch (sev) {
            case IMarker.SEVERITY_WARNING:
                overlayIcon = simages.getImageDescriptor(ISharedImages.IMG_DEC_FIELD_WARNING);
                break;
            case IMarker.SEVERITY_ERROR:
                overlayIcon = simages.getImageDescriptor(ISharedImages.IMG_DEC_FIELD_ERROR);
                break;
            }
            Image resourceImage = getEditorInput().getImageDescriptor().createImage();
            final DecorationOverlayIcon icon = new DecorationOverlayIcon(resourceImage, overlayIcon,
                    IDecoration.BOTTOM_LEFT);
            setTitleImage(icon.createImage());
        } catch (CoreException e) {
            standardExceptionHandling(e);
        }
    }

    @Override
    public void dispose() {
        CoreControlUnit.removeResourceBuildListener(builtListener);
        Activator.getDefault().getPreferenceStore().removePropertyChangeListener(propertyChangeListener);
        super.dispose();
    }

    public void openInformation(String title, String message) {
        MessageDialog.openInformation(getSite().getShell(), title, message);
    }

    public void openError(String title, String message) {
        MessageDialog.openError(getSite().getShell(), title, message);
    }

    public IProject getProject() {
        if (getResource() != null) {
            return getResource().getProject();
        }
        PackageAbsFileEditorInput storageInput = (PackageAbsFileEditorInput) getEditorInput()
                .getAdapter(PackageAbsFileEditorInput.class);
        if (storageInput != null) {
            // we are looking at a file inside a jar package
            return storageInput.getFile().getProject();
        }
        return null;
    }

    /**
     * returns the absolute file path to the file opened by the editor
     * or "<unknown>" if no such file exists 
     */
    public String getAbsoluteFilePath() {
        File f = getFile();
        if (f != null) {
            return f.getAbsolutePath();
        }
        PackageAbsFileEditorInput storageInput = (PackageAbsFileEditorInput) getEditorInput()
                .getAdapter(PackageAbsFileEditorInput.class);
        if (storageInput != null) {
            // we are looking at a file inside a jar package
            return storageInput.getFile().getAbsoluteFilePath();
        }
        return "<unknown>";
    }

    /**
     * returns the file shown by the editor
     * or null if the current resource is not a file
     */
    public File getFile() {
        if (getResource() instanceof IFile) {
            IFile iFile = (IFile) getResource();
            return iFile.getLocation().toFile();
        }
        return null;
    }

    /**
     * adds a listener which is notified whenever the underlying
     * compilationUnit of this editor changes
     */
    public void addModelChangeListener(CompilationUnitChangeListener modelChangeListener) {
        this.modelChangeListeners.add(modelChangeListener);
        if (compilationUnit != null) {
            modelChangeListener.onCompilationUnitChange(compilationUnit);
        }
    }

    public void removeModelChangeListener(CompilationUnitChangeListener modelChangeListener) {
        this.modelChangeListeners.remove(modelChangeListener);
    }

    @Override
    public void onCompilationUnitChange(CompilationUnit newCu) {
        this.compilationUnit = newCu;
        for (CompilationUnitChangeListener mcl : modelChangeListeners) {
            mcl.onCompilationUnitChange(newCu);
        }
    }

    /**
     * parses the current contents of the editor
     * and updates the compilationUnit
     * @param documentOffset 
     */
    public void reconcile(boolean withTypechecks) {
        if (reconciler != null) {
            reconciler.reconcile(getAbsNature(), withTypechecks);
        }
    }

    public void setReconciler(ABSReconcilingStrategy absReconcilingStrategy) {
        this.reconciler = absReconcilingStrategy;
    }

    /**
     * returns the current compilationUnit or null 
     * when viewing files outside an ABS project
     */
    public synchronized InternalASTNode<CompilationUnit> getCompilationUnit() {
        if (getAbsNature() == null || compilationUnit == null) {
            return null;
        }
        return new InternalASTNode<CompilationUnit>(compilationUnit, getAbsNature());
    }

    public AbsNature getAbsNature() {
        return UtilityFunctions.getAbsNature(getProject());
    }

    public void setCaretPos(int caretPos) {
        this.caretPos = caretPos;
    }

    public int getCaretPos() {
        return caretPos;
    }

    public AbsInformationPresenter getInformationPresenter() {
        if (informationPresenter == null) {
            informationPresenter = new AbsInformationPresenter();
            informationPresenter.install(getSourceViewer());
        }
        return informationPresenter;
    }

}