| /******************************************************************************* |
| * Copyright (c) 2010, 2011 Obeo. |
| * 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: |
| * Obeo - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.mylyn.docs.intent.client.ui.editor; |
| |
| import com.google.common.collect.Sets; |
| |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| 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.common.util.TreeIterator; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jface.operation.IRunnableContext; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentPartitioner; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.rules.FastPartitioner; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.mylyn.docs.intent.client.ui.IntentEditorActivator; |
| import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.IntentAnnotationModelManager; |
| import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.AbstractIntentImageAnnotation; |
| import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.IntentImageAnnotationDisposer; |
| import org.eclipse.mylyn.docs.intent.client.ui.editor.scanner.IntentPartitionScanner; |
| import org.eclipse.mylyn.docs.intent.client.ui.logger.IntentUiLogger; |
| import org.eclipse.mylyn.docs.intent.client.ui.repositoryconnection.EditorElementListAdapter; |
| import org.eclipse.mylyn.docs.intent.collab.common.logger.IIntentLogger.LogType; |
| import org.eclipse.mylyn.docs.intent.collab.common.logger.IntentLogger; |
| import org.eclipse.mylyn.docs.intent.collab.common.query.IntentDocumentQuery; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.ReadWriteRepositoryObjectHandler; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryObjectHandler; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.IntentCommand; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.ReadOnlyException; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.SaveException; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.impl.ReadOnlyRepositoryObjectHandlerImpl; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.impl.ReadWriteRepositoryObjectHandlerImpl; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.impl.notification.elementList.ElementListAdapter; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.impl.notification.elementList.ElementListNotificator; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.impl.notification.typeListener.TypeNotificator; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.notification.Notificator; |
| import org.eclipse.mylyn.docs.intent.collab.handlers.notification.RepositoryChangeNotification; |
| import org.eclipse.mylyn.docs.intent.collab.repository.Repository; |
| import org.eclipse.mylyn.docs.intent.compare.IntentASTMerger; |
| import org.eclipse.mylyn.docs.intent.compare.MergingException; |
| import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatus; |
| import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatusManager; |
| import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatusSeverity; |
| import org.eclipse.mylyn.docs.intent.core.compiler.CompilerPackage; |
| import org.eclipse.mylyn.docs.intent.core.document.IntentDocument; |
| import org.eclipse.mylyn.docs.intent.core.document.IntentGenericElement; |
| import org.eclipse.mylyn.docs.intent.core.document.IntentStructuredElement; |
| import org.eclipse.mylyn.docs.intent.core.document.UnitInstruction; |
| import org.eclipse.mylyn.docs.intent.core.modelingunit.ExternalContentReference; |
| import org.eclipse.mylyn.docs.intent.core.query.IntentHelper; |
| import org.eclipse.mylyn.docs.intent.markup.markup.Image; |
| import org.eclipse.mylyn.docs.intent.parser.IntentParser; |
| import org.eclipse.mylyn.docs.intent.parser.modelingunit.ParseException; |
| import org.eclipse.mylyn.docs.intent.serializer.ParsedElementPosition; |
| import org.eclipse.ui.internal.editors.text.WorkspaceOperationRunner; |
| import org.eclipse.ui.texteditor.AbstractDocumentProvider; |
| |
| /** |
| * DocumentProvider for the IntentDocument documents. |
| * |
| * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> |
| * @author <a href="mailto:william.piers@obeo.fr">William Piers</a> |
| */ |
| // Suppress Warnings added for using WorkspaceOperationRunner |
| @SuppressWarnings("restriction") |
| public class IntentDocumentProvider extends AbstractDocumentProvider implements RepositoryClient { |
| |
| /** |
| * The repository to use for creating and closing GET and POST connections. |
| */ |
| private Repository repository; |
| |
| /** |
| * The repository object handler managing the notifications related to the handled elements and allowing |
| * to save the elements on the repository. |
| */ |
| private RepositoryObjectHandler listenedElementsHandler; |
| |
| /** |
| * Root for the handled document. |
| */ |
| private EObject documentRoot; |
| |
| /** The operation runner. */ |
| private WorkspaceOperationRunner fOperationRunner; |
| |
| /** |
| * The editor associated to this document provider. |
| */ |
| private IntentEditor associatedEditor; |
| |
| /** |
| * The AnnotatioModelManager. |
| */ |
| private IntentAnnotationModelManager annotationModelManager; |
| |
| /** |
| * Represents the last createdDocument. |
| */ |
| private IntentEditorDocument createdDocument; |
| |
| /** |
| * Represents the partitioner used to identify the partitions of the document. |
| */ |
| private IDocumentPartitioner partitioner; |
| |
| /** |
| * A flag indicating whether the current document has syntax errors or not. |
| */ |
| private boolean hasSyntaxErrors; |
| |
| /** |
| * An internal {@link Job} used to save the document in non-UI thread. |
| */ |
| private SaveIntentDocumentJob saveJob; |
| |
| /** |
| * IntentDocumentProvider constructor. |
| * |
| * @param editor |
| * the editor associated to this document Provider |
| */ |
| public IntentDocumentProvider(IntentEditor editor) { |
| this.associatedEditor = editor; |
| this.annotationModelManager = new IntentAnnotationModelManager(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#canSaveDocument(java.lang.Object) |
| */ |
| @Override |
| public boolean canSaveDocument(Object element) { |
| return hasSyntaxErrors || super.canSaveDocument(element); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createAnnotationModel(java.lang.Object) |
| */ |
| @Override |
| protected IAnnotationModel createAnnotationModel(Object element) throws CoreException { |
| // We use an AnnotationModelManager to handle the create annotationModel |
| Assert.isNotNull(annotationModelManager); |
| |
| // Step 1: create annotations for all compilation statuses |
| for (CompilationStatus status : IntentHelper.getAllStatus((IntentGenericElement)documentRoot)) { |
| |
| // We use the annotationModelManager to create annotations |
| ParsedElementPosition posit = createdDocument.getIntentPosition(status.getTarget()); |
| if (posit == null) { |
| posit = new ParsedElementPosition(0, 0); |
| } |
| |
| if (!status.getSeverity().equals(CompilationStatusSeverity.INFO)) { |
| annotationModelManager.addAnnotationFromStatus( |
| this.listenedElementsHandler.getRepositoryAdapter(), status, |
| new Position(posit.getOffset(), posit.getDeclarationLength())); |
| } |
| } |
| |
| // Step 2: create annotations for all ExternalContentReferences |
| for (ExternalContentReference reference : IntentHelper.getAllContainedElements( |
| ExternalContentReference.class, (IntentGenericElement)documentRoot)) { |
| annotationModelManager.updateAnnotationFromElementToRender(reference, |
| createdDocument.getIntentPosition(reference)); |
| } |
| |
| // Step 3: create annotations for all images links |
| for (Image imageLink : IntentHelper.getAllContainedElements(Image.class, |
| (IntentGenericElement)documentRoot)) { |
| annotationModelManager.updateAnnotationFromElementToRender(imageLink, |
| createdDocument.getIntentPosition(imageLink)); |
| } |
| annotationModelManager.getAnnotationModel().addAnnotationModelListener( |
| new IntentImageAnnotationDisposer()); |
| return annotationModelManager.getAnnotationModel(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getAnnotationModel(java.lang.Object) |
| */ |
| @Override |
| public IAnnotationModel getAnnotationModel(Object element) { |
| return annotationModelManager.getAnnotationModel(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createDocument(java.lang.Object) |
| */ |
| @Override |
| protected IDocument createDocument(Object element) throws CoreException { |
| if (!(element instanceof IntentEditorInput)) { |
| IStatus status = new Status(IStatus.ERROR, IntentEditorActivator.PLUGIN_ID, |
| "Cannot open an Intent editor on a document of type " |
| + element.getClass().getCanonicalName() + " (must be IntentEditorInput) "); |
| throw new CoreException(status); |
| } |
| if (((IntentEditorInput)element).getRepository() == null) { |
| IStatus status = new Status(IStatus.ERROR, IntentEditorActivator.PLUGIN_ID, |
| "Cannot open Intent editor : document is not available."); |
| throw new CoreException(status); |
| } |
| |
| setRepository(((IntentEditorInput)element).getRepository()); |
| |
| // We obtain the root of the document |
| documentRoot = ((IntentEditorInput)element).getIntentElement(); |
| |
| // TODO check for notifications issues: |
| // the following command was added to avoid infinite loop caused by the fact that the serialization |
| // occurs during the repository first compilation. |
| ((IntentEditorInput)element).getRepositoryAdapter().execute(new IntentCommand() { |
| |
| public void execute() { |
| createdDocument = new IntentEditorDocument(documentRoot, associatedEditor); |
| } |
| }); |
| |
| if (createdDocument != null) { |
| partitioner = new FastPartitioner(new IntentPartitionScanner(), |
| IntentPartitionScanner.LEGAL_CONTENT_TYPES); |
| partitioner.connect(createdDocument); |
| createdDocument.setDocumentPartitioner(partitioner); |
| subscribeRepository(((IntentEditorInput)element).getRepositoryAdapter()); |
| } |
| return createdDocument; |
| } |
| |
| /** |
| * Registers listeners in the repository used by the given editor input. |
| * |
| * @param repositoryAdapter |
| * the repository adapter previously created by the editorInput |
| */ |
| private void subscribeRepository(RepositoryAdapter repositoryAdapter) { |
| // Step 1 : creation of the Handler in the correct mode |
| final RepositoryObjectHandler elementHandler = createElementHandler(repositoryAdapter, false); |
| addRepositoryObjectHandler(elementHandler); |
| |
| // Step 2 : creation of a Notificator listening changes on this element and compilation |
| // errors. |
| final Set<EObject> listenedObjects = new LinkedHashSet<EObject>(); |
| listenedObjects.add(new IntentDocumentQuery(repositoryAdapter).getOrCreateIntentDocument()); |
| final ElementListAdapter adapter = new EditorElementListAdapter(); |
| |
| Notificator listenedElementsNotificator = new ElementListNotificator(listenedObjects, adapter, |
| repositoryAdapter); |
| Notificator compilationStatusNotificator = new TypeNotificator( |
| Sets.newLinkedHashSet(CompilerPackage.eINSTANCE.getCompilationStatusManager() |
| .getEAllStructuralFeatures())); |
| elementHandler.addNotificator(listenedElementsNotificator); |
| elementHandler.addNotificator(compilationStatusNotificator); |
| } |
| |
| /** |
| * Creates the element handler matching the given mode. |
| * |
| * @param repositoryAdapter |
| * the repository adapter |
| * @param readOnlyMode |
| * the access mode |
| * @return the handler |
| */ |
| private static RepositoryObjectHandler createElementHandler(RepositoryAdapter repositoryAdapter, |
| boolean readOnlyMode) { |
| boolean isReadOnly = readOnlyMode; |
| RepositoryObjectHandler elementHandler = null; |
| if (!isReadOnly) { |
| try { |
| elementHandler = new ReadWriteRepositoryObjectHandlerImpl(repositoryAdapter); |
| } catch (ReadOnlyException e) { |
| IntentLogger |
| .getInstance() |
| .log(LogType.WARNING, |
| "The Intent Editor has insufficient rights (read-only) to save modifications on the repository. A read-only context will be used instead."); |
| isReadOnly = true; |
| } |
| } |
| |
| if (isReadOnly) { |
| elementHandler = new ReadOnlyRepositoryObjectHandlerImpl(); |
| elementHandler.setRepositoryAdapter(repositoryAdapter); |
| } |
| return elementHandler; |
| } |
| |
| /** |
| * Refreshes the outline View. |
| * |
| * @param newAST |
| * the newAST to base the outline on |
| */ |
| public void refreshOutline(EObject newAST) { |
| associatedEditor.refreshOutlineView(newAST); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#doSaveDocument(org.eclipse.core.runtime.IProgressMonitor, |
| * java.lang.Object, org.eclipse.jface.text.IDocument, boolean) |
| */ |
| @Override |
| protected void doSaveDocument(IProgressMonitor monitor, Object element, final IDocument document, |
| boolean overwrite) throws CoreException { |
| if (document instanceof IntentEditorDocument) { |
| // Disabling edition on the document until it gets saved |
| ((IntentEditorDocument)document).setIsBeingSaved(true); |
| this.associatedEditor.updateReadOnlyMode(); |
| if (saveJob != null) { |
| saveJob.cancel(); |
| } |
| |
| // Launch a save job (in non-UI thread) |
| saveJob = new SaveIntentDocumentJob((IntentEditorDocument)document); |
| saveJob.schedule(); |
| } |
| } |
| |
| /** |
| * Merges the document content according to the given AST. |
| * |
| * @param document |
| * the document |
| * @param localAST |
| * the AST |
| */ |
| private void merge(final IntentEditorDocument document, final EObject localAST) { |
| // Then we try to merge the parsed AST with the old one |
| final IntentASTMerger merger = new IntentASTMerger(); |
| boolean mustUndo = false; |
| try { |
| final EObject remoteAST = (EObject)document.getAST(); |
| try { |
| if (localAST != null && remoteAST != null && localAST.eClass().equals(remoteAST.eClass())) { |
| merger.mergeFromLocalToRepository(localAST, remoteAST); |
| } else { |
| this.createSyntaxErrorAnnotation("Unrecognized content: unable to merge " |
| + localAST.eClass().getName() + " with " + remoteAST.eClass().getName() + ".", 0, |
| document.getLength()); |
| } |
| } catch (MergingException e) { |
| mustUndo = true; |
| IntentUiLogger.logError(e); |
| } |
| |
| } catch (NullPointerException npe) { // FIXME catch NPE ?? |
| mustUndo = true; |
| IntentUiLogger.logError(npe); |
| } |
| |
| if (mustUndo) { |
| try { |
| ((ReadWriteRepositoryObjectHandler)listenedElementsHandler).undo(); |
| } catch (ReadOnlyException e) { |
| IntentUiLogger.logError(e); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#isModifiable(java.lang.Object) |
| */ |
| @Override |
| public boolean isModifiable(Object element) { |
| boolean isModifiable = true; |
| if (element instanceof IntentEditorDocument) { |
| isModifiable = !((IntentEditorDocument)element).isBeingSaved(); |
| } else if (this.createdDocument != null) { |
| isModifiable = !this.createdDocument.isBeingSaved(); |
| } |
| return isModifiable; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#isReadOnly(java.lang.Object) |
| */ |
| @Override |
| public boolean isReadOnly(Object element) { |
| boolean isReadOnly = true; |
| if (element instanceof IntentEditorDocument) { |
| isReadOnly = ((IntentEditorDocument)element).isBeingSaved(); |
| } else if (this.createdDocument != null) { |
| isReadOnly = this.createdDocument.isBeingSaved(); |
| } |
| return isReadOnly; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getOperationRunner(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| protected IRunnableContext getOperationRunner(IProgressMonitor monitor) { |
| if (fOperationRunner == null) { |
| fOperationRunner = new WorkspaceOperationRunner(); |
| } |
| fOperationRunner.setProgressMonitor(monitor); |
| return fOperationRunner; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient#handleChangeNotification(org.eclipse.mylyn.docs.intent.collab.handlers.notification.RepositoryChangeNotification) |
| */ |
| public void handleChangeNotification(RepositoryChangeNotification notification) { |
| // If the received notification indicates the deletion of the root of the associated document |
| if (handleRootHasBeenDeleted(notification)) { |
| return; |
| } |
| |
| // For each object modified indicated by this notification |
| for (EObject modifiedObject : notification.getImpactedElements()) { |
| // For all documents that have been opened on this object |
| if (modifiedObject.equals(new IntentDocumentQuery(listenedElementsHandler.getRepositoryAdapter()) |
| .getOrCreateIntentDocument())) { |
| handleContentHasChanged(modifiedObject); |
| } else { |
| // update annotations (if the compilation status manager has changed) |
| handleCompilationStatusHasChanged(modifiedObject); |
| // refreshing images |
| for (ExternalContentReference reference : IntentHelper.getAllContainedElements( |
| ExternalContentReference.class, (IntentGenericElement)documentRoot)) { |
| annotationModelManager.updateAnnotationFromElementToRender(reference, |
| createdDocument.getIntentPosition(reference)); |
| } |
| for (Image imageLink : IntentHelper.getAllContainedElements(Image.class, |
| (IntentGenericElement)documentRoot)) { |
| annotationModelManager.updateAnnotationFromElementToRender( |
| imageLink, |
| createdDocument.getIntentPosition(imageLink.eContainer().eContainer() |
| .eContainer())); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Update the annotation model by translating each compilationStatus associated to the given element as an |
| * Annotation. |
| * |
| * @param modifiedElement |
| * the element to use for updating the AnnotationModel (children will also be updated) |
| * @param relatedDocument |
| * the document to use for obtaining informations about element positions |
| */ |
| private void updateAnnotationModelFromCompilationStatusAndChildren(IntentGenericElement modifiedElement, |
| IntentEditorDocument relatedDocument) { |
| // Update the root |
| updateAnnotationModelFromCompilationStatus(modifiedElement, relatedDocument); |
| |
| // And all children |
| TreeIterator<EObject> eAllContents = modifiedElement.eAllContents(); |
| while (eAllContents.hasNext()) { |
| EObject next = eAllContents.next(); |
| if (next instanceof IntentGenericElement) { |
| updateAnnotationModelFromCompilationStatus((IntentGenericElement)next, relatedDocument); |
| } |
| } |
| } |
| |
| /** |
| * Update the annotation model by translating each compilationStatus associated to the given element as an |
| * Annotation. |
| * |
| * @param modifiedElement |
| * the element to use for updating the AnnotationModel |
| * @param relatedDocument |
| * the document to use for obtaining informations about element positions |
| */ |
| private void updateAnnotationModelFromCompilationStatus(IntentGenericElement modifiedElement, |
| IntentEditorDocument relatedDocument) { |
| // Step 1 : removing all the invalid compilation status relative to the modifiedElement |
| annotationModelManager.removeInvalidCompilerAnnotations( |
| this.listenedElementsHandler.getRepositoryAdapter(), modifiedElement); |
| |
| // Step 2 : updating the concerned documents |
| for (CompilationStatus statusToAdd : modifiedElement.getCompilationStatus()) { |
| |
| // Step 2.1 : we determine the position of the annotation to create by |
| // using the informations hold by the IntentDocument. |
| ParsedElementPosition parsedElementPosition = relatedDocument.getIntentPosition(statusToAdd |
| .getTarget()); |
| if (parsedElementPosition == null) { |
| parsedElementPosition = new ParsedElementPosition(0, 0); |
| } |
| Position position = new Position(parsedElementPosition.getOffset(), |
| parsedElementPosition.getDeclarationLength()); |
| if (!statusToAdd.getSeverity().equals(CompilationStatusSeverity.INFO)) { |
| // Step 2.2 : Adding this annotation to the model (will update overview and vertical rulers of |
| // the editor) |
| annotationModelManager.addAnnotationFromStatus( |
| this.listenedElementsHandler.getRepositoryAdapter(), statusToAdd, position); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient#addRepositoryObjectHandler(org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryObjectHandler) |
| */ |
| public void addRepositoryObjectHandler(RepositoryObjectHandler handler) { |
| handler.addClient(this); |
| listenedElementsHandler = handler; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient#removeRepositoryObjectHandler(org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryObjectHandler) |
| */ |
| public void removeRepositoryObjectHandler(RepositoryObjectHandler handler) { |
| handler.removeClient(this); |
| listenedElementsHandler = null; |
| |
| } |
| |
| /** |
| * Returns the RepositoryObjectHandler associated to this document provider. |
| * |
| * @return the listenedElementsHandler associated to this document provider |
| */ |
| public RepositoryObjectHandler getListenedElementsHandler() { |
| return listenedElementsHandler; |
| } |
| |
| /** |
| * Sets the repository to use for saving and closing getConnexion. |
| * |
| * @param repository |
| * the repository to use for saving and closing getConnexion |
| */ |
| public void setRepository(Repository repository) { |
| this.repository = repository; |
| this.repository.register(this); |
| } |
| |
| /** |
| * Returns the repository to use for saving and closing getConnexion. |
| * |
| * @return the repository to use for saving and closing getConnexion |
| */ |
| public Repository getRepository() { |
| return repository; |
| } |
| |
| /** |
| * Unregister from the repository, the connection and the handler used by this document provider. |
| */ |
| public void close() { |
| if (this.repository != null) { |
| // TODO ??? at this time cause project explorer desynchronization. Repository is saved anyway |
| // // If the editor is editable, we undo all the unsaved modifications |
| // if (this.associatedEditor.isEditable()) { |
| // try { |
| // this.listenedElementsHandler.getRepositoryAdapter().undo(); |
| // } catch (ReadOnlyException e) { |
| // // The readOnly property has already been tested by calling isEditable. |
| // } |
| // } |
| this.repository.unregister(this); |
| } |
| if (this.listenedElementsHandler != null) { |
| this.listenedElementsHandler.getRepositoryAdapter().closeContext(); |
| this.listenedElementsHandler.removeClient(this); |
| this.listenedElementsHandler.stop(); |
| } |
| /* |
| * Dispose images create through IntentImageAnnotations |
| */ |
| Iterator<?> annotationIterator = annotationModelManager.getAnnotationModel().getAnnotationIterator(); |
| while (annotationIterator.hasNext()) { |
| Object annotation = annotationIterator.next(); |
| if (annotation instanceof AbstractIntentImageAnnotation) { |
| IntentImageAnnotationDisposer.disposeImage((AbstractIntentImageAnnotation)annotation); |
| } |
| } |
| |
| } |
| |
| /** |
| * Creates a syntax error annotation at the given offset, of the given length. |
| * |
| * @param message |
| * the message associated to this syntax error |
| * @param offset |
| * offset of the syntax error annotation |
| * @param length |
| * length of the syntax error annotation. |
| */ |
| public void createSyntaxErrorAnnotation(String message, int offset, int length) { |
| this.annotationModelManager.createSyntaxErrorAnnotation(message, offset, length); |
| |
| } |
| |
| /** |
| * Removes all the syntax error annotations from the managed annotation model. |
| */ |
| public void removeSyntaxErrors() { |
| this.annotationModelManager.removeSyntaxErrorsAnnotations(); |
| |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient#dispose() |
| */ |
| public void dispose() { |
| listenedElementsHandler.removeClient(this); |
| listenedElementsHandler = null; |
| } |
| |
| /** |
| * Handles the fact that a root has been deleted. |
| * |
| * @param notification |
| * the root deletion notification |
| * @return true if a root has been deleted |
| */ |
| private boolean handleRootHasBeenDeleted(RepositoryChangeNotification notification) { |
| if (notification.getImpactedElements().size() < 1) { |
| createdDocument.unsynchronize(); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Handles the fact that a compilation status has changed. |
| * |
| * @param modifiedObject |
| * the compilation status manager |
| */ |
| private void handleCompilationStatusHasChanged(EObject modifiedObject) { |
| if (modifiedObject instanceof CompilationStatusManager) { |
| updateAnnotationModelFromCompilationStatusAndChildren((IntentGenericElement)documentRoot, |
| createdDocument); |
| } |
| } |
| |
| /** |
| * Handles the fact that the content has changed. |
| * |
| * @param modifiedObject |
| * the modified object |
| */ |
| private void handleContentHasChanged(EObject modifiedObject) { |
| if (modifiedObject instanceof IntentStructuredElement || modifiedObject instanceof UnitInstruction) { |
| EObject newDocumentRoot = modifiedObject; |
| if (!(documentRoot instanceof IntentDocument)) { |
| |
| // Get the new version of the documentRoot |
| URI oldDocumentRootURI = EcoreUtil.getURI(documentRoot); |
| String oldDocumentRootFragment = null; |
| if (oldDocumentRootURI != null) { |
| oldDocumentRootFragment = oldDocumentRootURI.fragment(); |
| } |
| if (oldDocumentRootFragment != null) { |
| try { |
| newDocumentRoot = modifiedObject.eResource().getEObject(oldDocumentRootFragment); |
| // CHECKSTYLE:OFF |
| } catch (Exception e) { |
| // CHECKSTYLE:ON |
| // Silent catch : the modifiedObject will be used as new root |
| } |
| } |
| } |
| documentRoot = newDocumentRoot; |
| createdDocument.setAST(newDocumentRoot); |
| createdDocument.reloadFromAST(); |
| |
| // In any case, we launch the syntax coloring |
| partitioner.computePartitioning(0, 1); |
| |
| // Finally, we refresh the outline |
| refreshOutline(documentRoot); |
| } |
| } |
| |
| /** |
| * Internal job used to save the editor in non-UI thread. |
| * |
| * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> |
| */ |
| private final class SaveIntentDocumentJob extends Job { |
| |
| /** |
| * Job name. |
| */ |
| private static final String SAVE_INTENT_DOCUMENT_JOB_NAME = "Save Intent editor"; |
| |
| /** |
| * The {@link IntentEditorDocument} to save. |
| */ |
| private IntentEditorDocument document; |
| |
| /** |
| * Default constructor. |
| * |
| * @param document |
| * the {@link IntentEditorDocument} to save |
| */ |
| public SaveIntentDocumentJob(IntentEditorDocument document) { |
| super(SAVE_INTENT_DOCUMENT_JOB_NAME); |
| this.document = document; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| final EObject localAST; |
| try { |
| hasSyntaxErrors = false; |
| removeSyntaxErrors(); |
| |
| String rootCompleteLevel = null; |
| if (documentRoot instanceof IntentStructuredElement) { |
| rootCompleteLevel = ((IntentStructuredElement)documentRoot).getCompleteLevel(); |
| } |
| |
| localAST = new IntentParser().parse(document.get(), rootCompleteLevel); |
| |
| associatedEditor.refreshTitle(localAST); |
| final RepositoryAdapter repositoryAdapter = listenedElementsHandler.getRepositoryAdapter(); |
| repositoryAdapter.execute(new IntentCommand() { |
| |
| public void execute() { |
| try { |
| merge((IntentEditorDocument)document, localAST); |
| ((IntentEditorDocument)document).reloadFromAST(true); |
| repositoryAdapter.save(); |
| } catch (ReadOnlyException e) { |
| IntentUiLogger.logError(e); |
| } catch (SaveException e) { |
| IntentUiLogger.logError(e); |
| } |
| } |
| |
| }); |
| } catch (ParseException e) { |
| createSyntaxErrorAnnotation(e.getMessage(), e.getErrorOffset(), e.getErrorLength()); |
| hasSyntaxErrors = true; |
| } |
| ((IntentEditorDocument)document).setIsBeingSaved(false); |
| associatedEditor.updateReadOnlyMode(); |
| return Status.OK_STATUS; |
| } |
| |
| } |
| } |