/**
* <copyright>
*
* Copyright (c) 2008,2010 Eclipse Modeling Project and others.
* 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
*
* Contributors:
* E.D.Willink - initial API and implementation
*
* </copyright>
*
* $Id: CommonTextEditor.java,v 1.3 2010/03/22 01:15:44 ewillink Exp $
*/
package org.eclipse.ocl.examples.editor.ui.imp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.CommandStackListener;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.imp.editor.LanguageServiceManager;
import org.eclipse.imp.editor.UniversalEditor;
import org.eclipse.imp.parser.ISourcePositionLocator;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewerExtension6;
import org.eclipse.jface.text.IUndoManager;
import org.eclipse.jface.text.IUndoManagerExtension;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.examples.editor.ui.ICreationFactory;
import org.eclipse.ocl.examples.editor.ui.OCLExamplesEditorPlugin;
import org.eclipse.ocl.examples.editor.ui.cst.ASTOutlinePage;
import org.eclipse.ocl.examples.editor.ui.cst.CSTOutline;
import org.eclipse.ocl.examples.editor.ui.cst.CSTOutlinePage;
import org.eclipse.ocl.examples.editor.ui.cst.ICSTOutlinePage;
import org.eclipse.ocl.examples.editor.ui.text.ITextEditorWithUndoContext;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
import org.eclipse.text.undo.IDocumentUndoManager;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;
public class CommonTextEditor extends UniversalEditor implements ITextEditorWithUndoContext
{
/**
* This keeps track of the editing domain that is used to track all changes to the model.
*/
protected AdapterFactoryEditingDomain editingDomain;
/**
* This is the property sheet page.
*/
protected IPropertySheetPage propertySheetPage = null;
/**
* This is the AST outline page.
*/
protected IContentOutlinePage astOutlinePage = null;
/**
* This is the CST outline page.
*/
protected ICSTOutlinePage cstOutlinePage = null;
/**
* This is the list of extra views for the Show In menu.
*/
protected IShowInTargetList showInTargetList = null;
private Composite container;
public CommonTextEditor() {
initializeEditingDomain();
}
protected IContentOutlinePage createASTOutlinePage() {
return new ASTOutlinePage(this);
}
protected ICSTOutlinePage createCSTOutlinePage() {
return new CSTOutlinePage(this);
}
@Override
public void createPartControl(Composite parent) {
this.container = parent;
super.createPartControl(parent);
}
protected IPropertySheetPage createPropertySheetPage() {
PropertySheetPage page = new CommonPropertySheetPage(this);
page.setPropertySourceProvider(new AdapterFactoryContentProvider(getAdapterFactory()));
return page;
}
protected IShowInTargetList createShowInTargetList() {
return new IShowInTargetList()
{
public String[] getShowInTargetIds() {
return new String[] {
"org.eclipse.ui.views.PropertySheet",
"org.eclipse.ui.views.ContentOutline",
CSTOutline.VIEW_ID
};
}
};
}
@Override
public void doSave(IProgressMonitor progressMonitor) {
//
// Bugzilla 224324 part 1. Ensure that partial text operation does not continue across save.
//
IDocument document = getDocumentProvider().getDocument(getEditorInput());
if (document != null) {
IDocumentUndoManager documentUndoManager = DocumentUndoManagerRegistry.getDocumentUndoManager(document);
if (documentUndoManager != null)
documentUndoManager.commit();
}
super.doSave(progressMonitor);
}
public EditingDomainActionBarContributor getActionBarContributor() {
return (EditingDomainActionBarContributor) getEditorSite().getActionBarContributor();
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class key) {
if (key.equals(IPropertySheetPage.class)) {
if (propertySheetPage == null)
propertySheetPage = createPropertySheetPage();
return propertySheetPage;
}
if (key.equals(IContentOutlinePage.class)) {
if (astOutlinePage == null)
astOutlinePage = createASTOutlinePage();
return astOutlinePage;
}
if (key.equals(ICSTOutlinePage.class)) {
if (cstOutlinePage == null)
cstOutlinePage = createCSTOutlinePage();
return cstOutlinePage;
}
if (key.equals(IShowInTargetList.class)) {
if (showInTargetList == null)
showInTargetList = createShowInTargetList();
return showInTargetList;
}
return super.getAdapter(key);
}
public AdapterFactory getAdapterFactory() {
return editingDomain.getAdapterFactory();
}
public AdapterFactoryEditingDomain getAdapterFactoryEditingDomain() {
return editingDomain;
}
protected Object getASTNode(TextSelection selection, ICommonParseResult parseResult) {
Resource ast = parseResult.getAST();
Object node = getCSTNode(selection, parseResult);
if (node instanceof CSTNode) {
for (Object cstNode = node; cstNode instanceof CSTNode; cstNode = ((EObject) cstNode).eContainer()) {
node = getCorrespondingASTNodeIn((CSTNode) cstNode, ast);
if (node != null)
break;
}
}
return node;
}
protected List<Object> getASTNodes(ISelection selection, ICommonParseResult parseResult) {
List<Object> unwrappedSelections = new ArrayList<Object>(((IStructuredSelection)selection).size());
for (Iterator<?> i = ((IStructuredSelection)selection).iterator(); i.hasNext(); ) {
Object node = parseResult.getASTNode(i.next());
if (node != null)
unwrappedSelections.add(node);
}
return unwrappedSelections;
}
public ISelection getASTSelection(ISelection selection, ICommonParseResult parseResult) {
if (selection instanceof TextSelection) {
Object node = getASTNode((TextSelection) selection, parseResult);
selection = node != null ? new StructuredSelection(node) : StructuredSelection.EMPTY;
}
else if ((selection instanceof IStructuredSelection) && !selection.isEmpty()) {
List<Object> unwrappedSelections = getASTNodes(selection, parseResult);
selection = new StructuredSelection(unwrappedSelections);
}
return selection;
}
protected List<Object> getASTorCSTNodes(ISelection selection, ICommonParseResult parseResult) {
List<Object> unwrappedSelections = new ArrayList<Object>(((IStructuredSelection)selection).size());
for (Iterator<?> i = ((IStructuredSelection)selection).iterator(); i.hasNext(); ) {
Object node = parseResult.getASTorCSTNode(i.next());
if (node != null)
unwrappedSelections.add(node);
}
return unwrappedSelections;
}
public ISelection getASTorCSTSelection(ISelection selection, ICommonParseResult parseResult) {
if (selection instanceof TextSelection) {
Object node = getASTNode((TextSelection) selection, parseResult);
selection = node != null ? new StructuredSelection(node) : StructuredSelection.EMPTY;
}
else if ((selection instanceof IStructuredSelection) && !selection.isEmpty()) {
List<Object> unwrappedSelections = getASTorCSTNodes(selection, parseResult);
selection = new StructuredSelection(unwrappedSelections);
}
return selection;
}
public IAnnotationModel getAnnotationModel() {
return getDocumentProvider().getAnnotationModel(getEditorInput());
}
public Annotation[] getAnnotations() {
IAnnotationModel annotationModel = getAnnotationModel();
List<Annotation> annotationList = new ArrayList<Annotation>();
for (Iterator<?> i = annotationModel.getAnnotationIterator(); i.hasNext(); ) {
Annotation annotation = (Annotation) i.next();
if (PARSE_ANNOTATION_TYPE.equals(annotation.getType()))
annotationList.add(annotation);
}
return annotationList.toArray(new Annotation[annotationList.size()]);
}
protected Object getCSTNode(TextSelection selection, ICommonParseResult parseResult) {
int length = selection.getLength();
int offset = selection.getOffset();
ISourcePositionLocator nodeLocator = parseResult.getSourcePositionLocator();
return nodeLocator.findNode(parseResult.getCST(), offset, offset+length);
}
public List<CSTNode> getCSTNodes(ISelection selection, ICommonParseResult parseResult) {
List<CSTNode> unwrappedSelections = new ArrayList<CSTNode>(((IStructuredSelection)selection).size());
for (Iterator<?> i = ((IStructuredSelection)selection).iterator(); i.hasNext(); ) {
CSTNode node = parseResult.getCSTNode(i.next());
if (node != null)
unwrappedSelections.add(node);
}
return unwrappedSelections;
}
public ISelection getCSTSelection(ISelection selection, ICommonParseResult parseResult) {
if (selection instanceof TextSelection) {
Object node = getCSTNode((TextSelection) selection, parseResult);
selection = node != null ? new StructuredSelection(node) : StructuredSelection.EMPTY;
}
else if ((selection instanceof IStructuredSelection) && !selection.isEmpty()) {
List<CSTNode> unwrappedSelections = getCSTNodes(selection, parseResult);
selection = new StructuredSelection(unwrappedSelections);
}
return selection;
}
/**
* Return the AST node corresponding to cstNode provided it forms part of
* the node hierarchy of ast, return null otherwise.
* <p>
* FIXME This method is used to avoid location of AST outline elements for CST
* contexts following references to definition model.
*/
protected Object getCorrespondingASTNodeIn(CSTNode cstNode, Resource ast) {
Object node = cstNode.getAst();
if (node == null) {
return null;
}
if (!(node instanceof EObject)) {
return null;
}
// if (((EObject)node).eResource() != ast) {
// return null;
// }
return node;
}
public ICreationFactory getCreationFactory() {
return getParseController().getCreationFactory();
}
public Display getDisplay() {
return getShell().getDisplay();
}
/**
* Override to return xxxDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactories()
* @return
*/
protected Collection<AdapterFactory> getDomainAdapterFactories() { // FIXME abstract to force derived implementation
List<AdapterFactory> domainAdapterFactories = new ArrayList<AdapterFactory>();
domainAdapterFactories.add(new EcoreItemProviderAdapterFactory());
domainAdapterFactories.add(new ReflectiveItemProviderAdapterFactory());
return domainAdapterFactories;
}
public AdapterFactoryEditingDomain getEditingDomain() {
return editingDomain;
}
public LanguageServiceManager getLanguageServiceManager() {
return fLanguageServiceManager;
}
@Override
public ICommonParseController getParseController() {
return (ICommonParseController) super.getParseController();
}
public ISelectionChangedListener getSelectionListener() {
return getSelectionChangedListener();
}
public Shell getShell() {
return getSite().getShell();
}
public IUndoContext getUndoContext() {
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension6) {
IUndoManager undoManager= ((ITextViewerExtension6)sourceViewer).getUndoManager();
if (undoManager instanceof IUndoManagerExtension)
return ((IUndoManagerExtension)undoManager).getUndoContext();
}
return null;
}
/**
* This sets up the editing domain for the model editor.
*/
protected void initializeEditingDomain() {
// Create an adapter factory that yields item providers.
//
ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
adapterFactory
.addAdapterFactory(new ResourceItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new EcoreItemProviderAdapterFactory());
adapterFactory
.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
// Create the command stack that will notify this editor as commands are
// executed.
//
BasicCommandStack commandStack = new BasicCommandStack();
// Add a listener to set the most recent command's affected objects to
// be the selection of the viewer with focus.
//
commandStack.addCommandStackListener(new CommandStackListener() {
public void commandStackChanged(final EventObject event) {
getContainer().getDisplay().asyncExec(new Runnable() {
public void run() {
firePropertyChange(IEditorPart.PROP_DIRTY);
// Try to select the affected objects.
//
// Command mostRecentCommand = ((CommandStack) event
// .getSource()).getMostRecentCommand();
// if (mostRecentCommand != null) {
// setSelectionToViewer(mostRecentCommand
// .getAffectedObjects());
// }
// if (propertySheetPage != null
// && !propertySheetPage.getControl().isDisposed()) {
// propertySheetPage.refresh();
// }
}
});
}
private Widget getContainer() {
return container;
}
});
// Create the editing domain with a special command stack.
//
editingDomain = new AdapterFactoryEditingDomain(adapterFactory,
commandStack, new HashMap<Resource, Boolean>());
}
public boolean refresh() {
ICommonParseController parseController = getParseController();
ICommonParseResult parseResult = parseController.getCurrentResult();
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(100); // FIXME Sleep !!!!!
} catch (InterruptedException e) {
}
if (parseController.getCurrentResult() != parseResult)
return true;
}
return false;
}
@Override
public void refreshMarkerAnnotations(String problemMarkerType) {
if (OCLExamplesEditorPlugin.ANNOTATION_UPDATE.isActive())
OCLExamplesEditorPlugin.ANNOTATION_UPDATE.println("Updating '" + problemMarkerType + "' for '" + getEditorInput().getName() + "'");
//
// FIXME create the correct annotation model from the outset
// Delete the resource markers which should never have been copied to the annotation model.
//
IDocumentProvider documentProvider = getDocumentProvider();
if (documentProvider != null) {
IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput());
if (annotationModel != null) {
try {
@SuppressWarnings("unchecked")
Iterator<Annotation> annotationIterator = annotationModel.getAnnotationIterator();
for (Iterator<Annotation> annIter = annotationIterator; annIter.hasNext(); ) {
Annotation ann = annIter.next();
if (ann instanceof MarkerAnnotation) {
if (OCLExamplesEditorPlugin.ANNOTATION_DELETE.isActive()) {
IMarker marker = ((MarkerAnnotation)ann).getMarker();
String lineNumber = String.valueOf(marker.getAttribute(IMarker.LINE_NUMBER));
String charStart = String.valueOf(marker.getAttribute(IMarker.CHAR_START));
String charEnd = String.valueOf(marker.getAttribute(IMarker.CHAR_END));
String message = String.valueOf(marker.getAttribute(IMarker.MESSAGE));
OCLExamplesEditorPlugin.ANNOTATION_DELETE.println("Lose '" + lineNumber + ":" + charStart + "-" + charEnd + ": " + message);
}
annotationModel.removeAnnotation(ann);
}
}
} catch (CoreException e) {
}
}
}
super.refreshMarkerAnnotations(problemMarkerType);
}
}