blob: f109120b1913ac28f7440e2cb81e67400ccd4f40 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
* Miguel Garcia (Tech Univ Hamburg-Harburg) - customization for EMF Generics
*******************************************************************************/
package org.eclipse.emf.emfatic.ui.editor;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.emfatic.core.generator.ecore.EcoreGenerator;
import org.eclipse.emf.emfatic.core.generics.util.OneToManyMap;
import org.eclipse.emf.emfatic.core.generics.util.OneToOneMap;
import org.eclipse.emf.emfatic.core.lang.gen.ast.BoundExceptWildcard;
import org.eclipse.emf.emfatic.core.lang.gen.ast.ClassDecl;
import org.eclipse.emf.emfatic.core.lang.gen.ast.CompUnit;
import org.eclipse.emf.emfatic.core.lang.gen.ast.EmfaticASTNode;
import org.eclipse.emf.emfatic.core.lang.gen.ast.Reference;
import org.eclipse.emf.emfatic.core.lang.gen.ast.TypeWithMulti;
import org.eclipse.emf.emfatic.core.lang.gen.ast.Wildcard;
import org.eclipse.emf.emfatic.ui.EmfaticUIPlugin;
import org.eclipse.emf.emfatic.ui.hyperlinks.EmfaticHyperlinkDetector;
import org.eclipse.emf.emfatic.ui.outline.EmfaticContentOutlinePage;
import org.eclipse.emf.emfatic.ui.partition.EmfaticDocumentProvider;
import org.eclipse.emf.emfatic.ui.preferences.PreferenceConstants;
import org.eclipse.emf.emfatic.ui.redsquiggles.EmfaticCSTChangeListener;
import org.eclipse.emf.emfatic.ui.views.TypesView;
import org.eclipse.gymnast.runtime.core.ast.ASTNode;
import org.eclipse.gymnast.runtime.core.outline.OutlineNode;
import org.eclipse.gymnast.runtime.ui.editor.LDTEditor;
import org.eclipse.gymnast.runtime.ui.editor.LDTSourceViewerConfiguration;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
public class EmfaticEditor extends LDTEditor implements IShowInTargetList,
IShowInSource {
private EmfaticEditorSelectionListener selectionListener = new EmfaticEditorSelectionListener(
this);
private EmfaticContentOutlinePage _emfaticContentOutlinePage;
private EmfaticKeyListener _keyListener = null;
private OutlineNode[] lastShownOutlineNodes = new OutlineNode[0];
private IViewReference _typesViewReference = null;
private HighlightingManager highlightingManager;
public EmfaticEditor() {
addParseTreeChangedListener(new EmfaticCSTChangeListener(this));
setDocumentProvider(new EmfaticDocumentProvider());
highlightingManager = new HighlightingManager();
PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(new ThemeChangeListener() {
@Override
public void themeChange() {
highlightingManager.initialiseDefaultColors();
refreshText();
}
});
highlightingManager.getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (highlightingManager.isColorPreference(event.getProperty())) {
refreshText();
}
}
});
}
public void refreshText() {
ISourceViewer viewer= getSourceViewer();
if (!(viewer instanceof ISourceViewerExtension2))
return;
((ISourceViewerExtension2)viewer).unconfigure();
initializeEditor();
viewer.configure(getSourceViewerConfiguration());
}
protected LDTSourceViewerConfiguration createSourceViewerConfiguration() {
return new EmfaticSourceViewerConfiguration(this);
}
@Override
public void doSave(IProgressMonitor progressMonitor) {
super.doSave(progressMonitor);
if(EmfaticUIPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.AUTO_GENERATE_ECORE))
generateEcoreFile();
}
/**
* Creates/updates the ecore file when saving the emf file
*/
private void generateEcoreFile() {
IEditorInput input = getEditorInput();
if(input instanceof IFileEditorInput) {
final IFile file=((IFileEditorInput)input).getFile();
// use a job to create/override the ecore file...
Job job = new Job("Generating Ecore Model for " + file.getName()) {
protected IStatus run(IProgressMonitor monitor) {
new EcoreGenerator().generate(file, monitor);
return Status.OK_STATUS;
}
};
// we might create a new file in the container
job.setRule(file.getParent());
job.schedule();
}
}
@Override
protected EmfaticOutlineConfiguration createOutlineConfiguration() {
return new EmfaticOutlineConfiguration(this);
}
private ProjectionSupport projectionSupport;
private ProjectionAnnotationModel annotationModel;
private Annotation[] oldAnnotations;
/**
* folding support, as explained in
* http://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html
*/
public void updateFoldingStructure(List<Position> positions) {
initFoldingSupport();
Annotation[] annotations = new Annotation[positions.size()];
// the new (annotation, position) pairs
HashMap<ProjectionAnnotation, Position> newAnnotations = new HashMap<ProjectionAnnotation, Position>();
for (int i = 0; i < positions.size(); i++) {
ProjectionAnnotation annotation = new ProjectionAnnotation();
newAnnotations.put(annotation, positions.get(i));
annotations[i] = annotation;
}
annotationModel.modifyAnnotations(oldAnnotations, newAnnotations, null);
oldAnnotations = annotations;
}
/**
* folding support, as explained in
* http://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html
*
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
initFoldingSupport();
selectionListener.install(getSelectionProvider());
_keyListener = new EmfaticKeyListener(this);
getSourceViewer().getTextWidget().addKeyListener(_keyListener);
}
private void initFoldingSupport() {
if (annotationModel == null) {
ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
projectionSupport = new ProjectionSupport(viewer,
getAnnotationAccess(), getSharedColors());
// see AntEditor for hovers displaying HTML
/*
* A summary is an annotation that gets created out of all
* annotations with a type that has been registered through this
* method and that are inside the folded region.
*
*/
projectionSupport
.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error");
projectionSupport
.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning");
projectionSupport.install();
// turn projection mode on
viewer.doOperation(ProjectionViewer.TOGGLE);
annotationModel = viewer.getProjectionAnnotationModel();
}
}
/**
* folding support, as explained in
* http://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html
*
*
* @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite,
* org.eclipse.jface.text.source.IVerticalRuler, int)
*/
protected ISourceViewer createSourceViewer(Composite parent,
IVerticalRuler ruler, int styles) {
fAnnotationAccess = getAnnotationAccess();
fOverviewRuler = createOverviewRuler(getSharedColors());
ISourceViewer viewer = new ProjectionViewer(parent, ruler,
getOverviewRuler(), isOverviewRulerVisible(), styles);
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(viewer);
// viewer.addTextListener(textListener);
return viewer;
}
/*
* LDT has already support for this (as well as for ITextListener). I
* realized after adding it here
*/
public EmfaticASTNode getClosestEnclosingASTNodeAt(int offset, Class<?> filter) {
return getClosestEnclosingASTNodeAt(offset, 0, filter);
}
public EmfaticASTNode getClosestEnclosingASTNodeAt(int offset, int length,
Class<?> filter) {
EmfaticASTNode node = getClosestEnclosingASTNodeAtWithin(
new HashSet<ASTNode>(), getParseRoot(), offset, length, filter);
return node;
}
private EmfaticASTNode getClosestEnclosingASTNodeAtWithin(Set<ASTNode>seenNodes, ASTNode within,
int offset, int length, Class<?> filter) {
if (within == null) {
return null;
}
if(seenNodes.contains(within))
return null;
seenNodes.add(within);
ASTNode node = within.getNodeAt(offset, 0);
// search within the for any ASTNode
for (int i = offset + 1; (node == null) && (i < offset + length); i++) {
node = getParseRoot().getNodeAt(i, 0);
}
if (node != null) {
/*
* An outer node fulfilling the filter condition may have been found
* which hides a more specific node also fulfilling the filter
*/
if (node.getChildren().length > 0) {
for (ASTNode child : node.getChildren()) {
ASTNode innerNode = getClosestEnclosingASTNodeAtWithin(
seenNodes, child, offset, length, filter);
if (innerNode != null) {
node = innerNode;
break;
}
}
}
/*
* search upwards till finding a node of type filter, or reaching
* the root
*/
while (!filter.isInstance(node) && (node.getParent() != null)) {
node = node.getParent();
}
}
if (filter.isInstance(node)) {
return (EmfaticASTNode) node;
} else {
return null;
}
}
@Override
public void dispose() {
if (selectionListener != null) {
selectionListener.uninstall(getSelectionProvider());
selectionListener = null;
}
super.dispose();
}
public OneToOneMap<ASTNode, EObject> getCstDecl2EcoreAST() {
CompUnit compUnit = (CompUnit) getParseRoot();
if (compUnit != null) {
return compUnit.getCstDecl2EcoreAST();
}
return null;
}
public OneToManyMap<EObject, ASTNode> getEcoreDecl2CstUse() {
CompUnit compUnit = (CompUnit) getParseRoot();
if (compUnit != null) {
return compUnit.getEcoreDecl2CstUse();
}
return null;
}
public IDocument getDocument() {
IDocument doc = null;
if (getDocumentProvider() == null) {
return doc;
}
doc = getDocumentProvider().getDocument(getEditorInput());
return doc;
}
public void openTarget(EmfaticASTNode linkTarget) {
setSelection(linkTarget, true);
}
public void setSelection(EmfaticASTNode reference, boolean moveCursor) {
if (reference == null) {
if (moveCursor) {
resetHighlightRange();
markInNavigationHistory();
}
return;
}
if (moveCursor) {
markInNavigationHistory();
}
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer == null) {
return;
}
StyledText textWidget = sourceViewer.getTextWidget();
if (textWidget == null) {
return;
}
try {
int offset = reference.getRangeStart();
if (offset < 0) {
return;
}
int length = reference.getRangeLength();
textWidget.setRedraw(false);
if (length > 0) {
setHighlightRange(offset, length, moveCursor);
}
if (!moveCursor) {
return;
}
if (offset > -1 && length > 0) {
sourceViewer.revealRange(offset, length);
// Selected region begins one index after offset
sourceViewer.setSelectedRange(offset, length);
markInNavigationHistory();
}
} catch (IllegalArgumentException x) {
x.printStackTrace();
} finally {
textWidget.setRedraw(true);
}
}
public void setContentOutlinePage(
EmfaticContentOutlinePage emfaticContentOutlinePage) {
_emfaticContentOutlinePage = emfaticContentOutlinePage;
}
protected EmfaticContentOutlinePage getContentOutlinePage() {
return _emfaticContentOutlinePage;
}
public EObject getEcoreDeclAtCursor() {
ISelection selection = getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection ts = (ITextSelection) selection;
int offset = ts.getOffset();
int length = ts.getLength();
ReferedEcoreDecl red = getReferedEcoreDecl(offset, length);
if (red == null || red.ecoreDecl == null) {
return null;
}
EObject ecoreDecl = red.ecoreDecl;
return ecoreDecl;
}
return null;
}
public EObject gotoDeclaration() {
EObject ecoreDecl = getEcoreDeclAtCursor();
if (ecoreDecl == null) {
return null;
}
EmfaticASTNode landingPlace = EmfaticHyperlinkDetector.getLandingPlace(
ecoreDecl, this);
setSelection(landingPlace, true);
return ecoreDecl;
}
public ReferedEcoreDecl getReferedEcoreDecl(int offset, int length) {
/*
* first chance: Wildcard
*/
EmfaticASTNode node = getClosestEnclosingASTNodeAt(offset, length,
Wildcard.class);
if (node != null) {
// it can still be an unbounded wildcard
node = ((Wildcard) node).getBoundExceptWildcard();
}
if (node == null) {
// second chance: BoundExceptWildcard
node = getClosestEnclosingASTNodeAt(offset, length,
BoundExceptWildcard.class);
if (node == null) {
// thir chance: TypeWithMult
node = getClosestEnclosingASTNodeAt(offset, length,
TypeWithMulti.class);
if (node == null) {
// fourth chance: Reference (for an opposite reference)
node = getClosestEnclosingASTNodeAt(offset, length,
Reference.class);
if (node == null) {
return null;
}
node = ((Reference) node).getOppositeName();
} else {
// leave a BoundExceptWildcard in node
node = ((TypeWithMulti) node).getName();
}
}
}
// actually the QualifiedID was placed in the bigMap
if (node instanceof BoundExceptWildcard) {
node = ((BoundExceptWildcard) node)
.getRawTNameOrTVarOrParamzedTName();
}
EObject ecoreDecl = getEcoreDecl2CstUse().getInv(node);
ReferedEcoreDecl res = new ReferedEcoreDecl();
res.ecoreDecl = ecoreDecl;
res.node = node;
return res;
}
public class ReferedEcoreDecl {
public EObject ecoreDecl;
public EmfaticASTNode node;
}
protected void createActions() {
super.createActions();
ResourceBundle bundle = EmfaticEditorMessages.getResourceBundle();
IAction action = new ContentAssistAction(bundle,
"ContentAssistProposal.", this); //$NON-NLS-1$
action
.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
setAction("ContentAssistProposal", action); //$NON-NLS-1$
markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
// TODO PlatformUI.getWorkbench().getHelpSystem().setHelp(action,
// helpContextId);
}
@Override
public OutlineNode[] getOutlineElements() {
OutlineNode[] candidateOutlineNodes = super.getOutlineElements();
if (candidateOutlineNodes != null && candidateOutlineNodes.length > 0) {
lastShownOutlineNodes = candidateOutlineNodes;
}
return lastShownOutlineNodes;
}
/**
* see p. 467 and p. 499 of JDGE 2nd Ed
*/
public String[] getShowInTargetIds() {
EObject ecoreDecl = getEcoreDeclAtCursor();
String[] res = null;
if (ecoreDecl == null) {
ecoreDecl = getEcoreDeclAtCursor2();
if (ecoreDecl == null) {
res = new String[] {};
} else {
res = new String[] {TypesView.ID };
}
} else {
res = new String[] { TypesView.ID };
}
return res;
}
private EObject getEcoreDeclAtCursor2() {
ISelection selection = getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection ts = (ITextSelection) selection;
int offset = ts.getOffset();
//int length = ts.getLength();
ASTNode node = getClosestEnclosingASTNodeAt(offset, ClassDecl.class);
if (node == null) {
return null;
}
EObject ecoreDecl = getCstDecl2EcoreAST().get(node);
return ecoreDecl;
}
return null;
}
/**
* see p. 467 and p. 499 of JDGE 2nd Ed
*/
public ShowInContext getShowInContext() {
if (getEditorInput() instanceof FileEditorInput) {
FileEditorInput fei = (FileEditorInput) getEditorInput();
return new ShowInContext(fei.getFile(), getSelectionProvider().getSelection());
}
return new ShowInContext(getEditorInput(), getSelectionProvider().getSelection());
}
public void showInTypeHierarchy(EClass openedDecl) {
if (!(openedDecl instanceof EClass)) {
return;
}
TypesView tv = getTypesView();
if (tv == null) {
return;
}
WeakReference<EClass> wrC = new WeakReference<EClass>(
(EClass) openedDecl);
tv.setInput(wrC, true);
}
public TypesView getTypesView() {
// 1) try if there's a cached reference
if (_typesViewReference != null) {
TypesView tv = (TypesView) _typesViewReference.getView(false);
if (tv != null) {
return tv;
}
}
// 2) look for the view opened by the user
IWorkbenchPage wp = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage();
IViewReference _typesViewReference = wp.findViewReference(TypesView.ID);
if (_typesViewReference != null) {
TypesView tv = (TypesView) _typesViewReference.getView(true);
if (tv != null) {
return tv;
}
}
// 3) open it programatically
try {
IViewPart tv = wp.showView(TypesView.ID);
return (TypesView) tv;
} catch (PartInitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}