| /** |
| * Copyright (c) 2009-2010 Thales Corporate Services S.A.S. |
| * 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-v2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * Contributors: |
| * Thales Corporate Services S.A.S - initial API and implementation |
| */ |
| package org.eclipse.egf.pattern.ui.editors; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.operations.IOperationHistory; |
| import org.eclipse.core.commands.operations.IOperationHistoryListener; |
| import org.eclipse.core.commands.operations.IUndoContext; |
| import org.eclipse.core.commands.operations.IUndoableOperation; |
| import org.eclipse.core.commands.operations.ObjectUndoContext; |
| import org.eclipse.core.commands.operations.OperationHistoryEvent; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.egf.common.ui.helper.ThrowableHandler; |
| import org.eclipse.egf.core.EGFCorePlugin; |
| import org.eclipse.egf.core.domain.PlatformResourceLoadedListener; |
| import org.eclipse.egf.core.domain.PlatformResourceLoadedListener.ResourceListener; |
| import org.eclipse.egf.core.domain.PlatformResourceLoadedListener.ResourceUser; |
| import org.eclipse.egf.core.platform.EGFPlatformPlugin; |
| import org.eclipse.egf.core.ui.l10n.CoreUIMessages; |
| import org.eclipse.egf.model.fcore.FcorePackage; |
| import org.eclipse.egf.model.pattern.Pattern; |
| import org.eclipse.egf.model.pattern.PatternPackage; |
| import org.eclipse.egf.pattern.ui.Activator; |
| import org.eclipse.egf.pattern.ui.Messages; |
| import org.eclipse.egf.pattern.ui.editors.pages.ImplementationPage; |
| import org.eclipse.egf.pattern.ui.editors.pages.OverviewPage; |
| import org.eclipse.egf.pattern.ui.editors.pages.PatternEditorPage; |
| import org.eclipse.egf.pattern.ui.editors.pages.SpecificationPage; |
| import org.eclipse.emf.common.command.BasicCommandStack; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EContentAdapter; |
| import org.eclipse.emf.edit.domain.IEditingDomainProvider; |
| import org.eclipse.emf.transaction.TransactionalEditingDomain; |
| import org.eclipse.emf.workspace.IWorkspaceCommandStack; |
| import org.eclipse.emf.workspace.ResourceUndoContext; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.forms.editor.FormEditor; |
| import org.eclipse.ui.forms.editor.IFormPage; |
| |
| /** |
| * @author Thomas Guiu |
| */ |
| public class PatternEditor extends FormEditor implements ResourceUser, IEditingDomainProvider { |
| |
| public static final String ID = "org.eclipse.egf.pattern.ui.pattern.editor.id"; //$NON-NLS-1$ |
| |
| /** |
| * UndoContext |
| */ |
| protected ObjectUndoContext undoContext; |
| |
| /** |
| * TransactionalEditingDomain |
| */ |
| private TransactionalEditingDomain editingDomain; |
| |
| /** |
| * Whether or not user saved this resource in this editor |
| */ |
| protected boolean userHasSavedResource; |
| |
| /** |
| * Whether or not this resource has been externally changed |
| */ |
| protected boolean resourceHasBeenExternallyChanged; |
| |
| /** |
| * Whether or not this resource has been removed |
| */ |
| protected boolean resourceHasBeenRemoved; |
| |
| /** |
| * Current pattern |
| */ |
| private Pattern pattern; |
| |
| /** |
| * Current title |
| */ |
| private String partName; |
| |
| private final ResourceListener resourceListener = new ResourceListener() { |
| |
| public void resourceMoved(final Resource movedResource, final URI oldURI) { |
| // TODO: partially implemented, we do need to handle .pt |
| if (movedResource == getResource()) { |
| resourceHasBeenExternallyChanged = false; |
| resourceHasBeenRemoved = false; |
| userHasSavedResource = false; |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| setInputWithNotify(new PatternEditorInput(movedResource, ((PatternEditorInput) getEditorInput()).getID())); |
| firePropertyChange(PROP_TITLE); |
| } |
| |
| }); |
| } |
| } |
| |
| public void resourceDeleted(final Resource deletedResource) { |
| if ((deletedResource == getResource())) { |
| if (isDirty() == false) { |
| // just close now without prompt |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| getSite().getPage().closeEditor(PatternEditor.this, false); |
| } |
| |
| }); |
| } else { |
| resourceHasBeenRemoved = true; |
| } |
| } |
| } |
| |
| public void resourceReloaded(final Resource reloadedResource) { |
| |
| if (reloadedResource == getResource()) { |
| |
| resourceHasBeenExternallyChanged = false; |
| resourceHasBeenRemoved = false; |
| userHasSavedResource = false; |
| |
| // Create a new input |
| final PatternEditorInput newEditorInput = new PatternEditorInput(reloadedResource, ((PatternEditorInput) getEditorInput()).getID()); |
| |
| // Check whether or not this pattern is still alive |
| if (newEditorInput.getPattern() != null) { |
| |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| |
| // Do we have something to do |
| if (pattern != newEditorInput.getPattern()) { |
| |
| // Remove previous adapters |
| removePatternChangeAdapter(); |
| // Store reloaded pattern |
| pattern = newEditorInput.getPattern(); |
| // Add new adapters |
| addPatternChangeAdapter(); |
| |
| getOperationHistory().dispose(undoContext, true, true, true); |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| setInputWithNotify(newEditorInput); |
| for (PatternEditorPage page : pages) { |
| page.rebind(newEditorInput); |
| } |
| // Update title |
| firePropertyChange(PROP_TITLE); |
| |
| } |
| |
| } |
| |
| }); |
| |
| } else { |
| |
| // just close now without prompt |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| getSite().getPage().closeEditor(PatternEditor.this, false); |
| } |
| |
| }); |
| |
| } |
| |
| } |
| |
| } |
| |
| public void externalUpdate(final Resource changedResource) { |
| if (changedResource == getResource()) { |
| resourceHasBeenExternallyChanged = true; |
| } |
| } |
| |
| public void internalUpdate(final Resource changedResource) { |
| |
| if (changedResource == getResource()) { |
| |
| resourceHasBeenExternallyChanged = false; |
| resourceHasBeenRemoved = false; |
| userHasSavedResource = false; |
| |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } |
| |
| }); |
| |
| } |
| |
| } |
| |
| }; |
| |
| /** |
| * This listens for when the outline becomes active |
| */ |
| protected IPartListener partListener = new IPartListener() { |
| |
| public void partActivated(IWorkbenchPart p) { |
| if (p == PatternEditor.this) { |
| handleActivate(); |
| } |
| } |
| |
| public void partBroughtToTop(IWorkbenchPart p) { |
| // Nothing to do |
| } |
| |
| public void partClosed(IWorkbenchPart p) { |
| // Nothing to do |
| } |
| |
| public void partDeactivated(IWorkbenchPart p) { |
| // Nothing to do |
| } |
| |
| public void partOpened(IWorkbenchPart p) { |
| // Nothing to do |
| } |
| |
| }; |
| |
| private final List<PatternEditorPage> pages = new ArrayList<PatternEditorPage>(); |
| |
| /** |
| * Adapter used to update the problem indication when resources are demanded |
| * loaded. |
| */ |
| protected EContentAdapter editorResourceAdapter = new EContentAdapter() { |
| |
| @Override |
| public void notifyChanged(Notification notification) { |
| // Process Resource who belongs to a resource set |
| if (notification.getNotifier() instanceof Resource) { |
| switch (notification.getFeatureID(Resource.class)) { |
| case Resource.RESOURCE__URI: { |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } |
| |
| }); |
| break; |
| } |
| } |
| } else { |
| super.notifyChanged(notification); |
| } |
| } |
| |
| @Override |
| protected void setTarget(Resource innerTarget) { |
| basicSetTarget(innerTarget); |
| } |
| |
| @Override |
| protected void unsetTarget(Resource innerTarget) { |
| basicUnsetTarget(innerTarget); |
| } |
| |
| }; |
| |
| // The current pattern adapter |
| AdapterImpl patternAdapter = new AdapterImpl() { |
| |
| @Override |
| public void notifyChanged(final Notification msg) { |
| if (FcorePackage.Literals.NAMED_MODEL_ELEMENT__NAME.equals(msg.getFeature())) { |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| // Title update |
| if (partName != null && partName.equals(msg.getNewValue()) == false) { |
| partName = (String) msg.getNewValue(); |
| setPartName(partName); |
| } |
| } |
| |
| }); |
| } else if (PatternPackage.Literals.PATTERN__CONTAINER.equals(msg.getFeature()) && msg.getNewValue() == null) { |
| // Removed Pattern |
| // just close now without prompt |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| getSite().getPage().closeEditor(PatternEditor.this, false); |
| } |
| |
| }); |
| |
| } |
| } |
| |
| }; |
| |
| public PatternEditor() { |
| initializeEditingDomain(); |
| } |
| |
| public boolean userHasSavedResource() { |
| return userHasSavedResource; |
| } |
| |
| public boolean resourceHasBeenExternallyChanged() { |
| return resourceHasBeenExternallyChanged; |
| } |
| |
| /** |
| * Handles activation of the editor or it's associated views. |
| */ |
| protected void handleActivate() { |
| if (resourceHasBeenRemoved) { |
| if (handleDirtyConflict()) { |
| getSite().getPage().closeEditor(PatternEditor.this, false); |
| } |
| } else if (resourceHasBeenExternallyChanged) { |
| handleChangedResource(); |
| } |
| } |
| |
| /** |
| * Handles what to do with changed resource on activation. |
| */ |
| protected void handleChangedResource() { |
| if (isDirty() == false || handleDirtyConflict()) { |
| PlatformResourceLoadedListener.getResourceManager().reloadResource(getResource()); |
| } |
| } |
| |
| /** |
| * This is for implementing {@link IEditorPart} and simply saves the model |
| * file. |
| */ |
| @Override |
| public void doSave(IProgressMonitor progressMonitor) { |
| // Do the work within an operation because this is a long running |
| // activity that modifies the |
| // workbench. |
| WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { |
| |
| // This is the method that gets invoked when the operation runs. |
| @Override |
| public void execute(final IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { |
| try { |
| // Save Resource |
| getEditingDomain().runExclusive(new Runnable() { |
| |
| public void run() { |
| Resource resourceToSave = getResource(); |
| try { |
| // Save the resource to the file system. |
| long timeStamp = resourceToSave.getTimeStamp(); |
| resourceToSave.save(Collections.EMPTY_MAP); |
| if (resourceToSave.getTimeStamp() != timeStamp) { |
| userHasSavedResource = true; |
| } |
| } catch (Exception e) { |
| ThrowableHandler.handleThrowable(Activator.getDefault().getPluginID(), e); |
| } |
| } |
| |
| }); |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| }; |
| |
| try { |
| // This runs the options, and shows progress. |
| new ProgressMonitorDialog(getSite().getShell()).run(true, false, operation); |
| // Refresh the necessary state. |
| ((BasicCommandStack) editingDomain.getCommandStack()).saveIsDone(); |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } catch (InvocationTargetException ite) { |
| ThrowableHandler.handleThrowable(Activator.getDefault().getPluginID(), ite); |
| } catch (InterruptedException e) { |
| Assert.isTrue(false, "This operation can not be canceled."); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public void doSaveAs() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public IOperationHistory getOperationHistory() { |
| return ((IWorkspaceCommandStack) editingDomain.getCommandStack()).getOperationHistory(); |
| } |
| |
| public ObjectUndoContext getUndoContext() { |
| return undoContext; |
| } |
| |
| /** |
| * Add the Adapter add for refreshing the editor title |
| */ |
| private void addPatternChangeAdapter() { |
| if (pattern != null && pattern.eAdapters().contains(patternAdapter) == false) { |
| pattern.eAdapters().add(patternAdapter); |
| } |
| } |
| |
| /** |
| * Remove the Adapter add for refreshing the editor title |
| */ |
| private void removePatternChangeAdapter() { |
| if (pattern != null && pattern.eAdapters().contains(patternAdapter)) { |
| pattern.eAdapters().remove(patternAdapter); |
| } |
| } |
| |
| /** |
| * Gets the title tool tip text of this part. |
| * |
| * @return the tool tip text |
| */ |
| @Override |
| public String getTitleToolTip() { |
| if (getEditorInput() == null) { |
| return super.getTitleToolTip(); |
| } |
| return EGFPlatformPlugin.getPlatformURIConverter().normalize(getResource().getURI()).toString(); |
| } |
| |
| @SuppressWarnings("rawtypes") |
| @Override |
| public Object getAdapter(Class key) { |
| if (key.equals(IUndoContext.class)) { |
| return undoContext; |
| } |
| return super.getAdapter(key); |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| return false; |
| } |
| |
| @Override |
| public boolean isDirty() { |
| if (getResource() == null) { |
| return false; |
| } |
| return getResource().isModified(); |
| } |
| |
| @Override |
| protected void addPages() { |
| try { |
| addPage(new OverviewPage(this)); |
| addPage(new SpecificationPage(this)); |
| addPage(new ImplementationPage(this)); |
| } catch (PartInitException e) { |
| Activator.getDefault().logError(e); |
| } |
| } |
| |
| public Resource getResource() { |
| PatternEditorInput input = (PatternEditorInput) getEditorInput(); |
| if (input == null) |
| throw new IllegalStateException(); |
| return input.getResource(); |
| } |
| |
| public TransactionalEditingDomain getEditingDomain() { |
| return editingDomain; |
| } |
| |
| /** |
| * The <code>MultiPageEditorExample</code> implementation of this method |
| * checks that the input is an instance of <code>IFileEditorInput</code>. |
| */ |
| @Override |
| public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException { |
| if (editorInput instanceof PatternEditorInput == false) { |
| throw new PartInitException(Messages.Editor_wrong_input); |
| } |
| setSite(site); |
| setInputWithNotify(editorInput); |
| site.getPage().addPartListener(partListener); |
| pattern = ((PatternEditorInput) getEditorInput()).getPattern(); |
| partName = pattern.getName(); |
| resourceHasBeenExternallyChanged = PlatformResourceLoadedListener.getResourceManager().resourceHasBeenExternallyChanged(getResource()); |
| PlatformResourceLoadedListener.getResourceManager().addObserver(this); |
| // populate operation history if applicable |
| PlatformResourceLoadedListener.getResourceManager().populateUndoContext(getOperationHistory(), undoContext, getResource()); |
| addPatternChangeAdapter(); |
| setPartName(pattern.getName()); |
| } |
| |
| protected void initializeEditingDomain() { |
| editingDomain = TransactionalEditingDomain.Registry.INSTANCE.getEditingDomain(EGFCorePlugin.EDITING_DOMAIN_ID); |
| undoContext = new ObjectUndoContext(this, "undoContext label"); //$NON-NLS-1$ |
| getOperationHistory().addOperationHistoryListener(historyListener); |
| editingDomain.getResourceSet().eAdapters().add(editorResourceAdapter); |
| } |
| |
| @Override |
| public void dispose() { |
| // if init failed, dispose should not called this |
| if (getEditorInput() != null && getEditorInput() instanceof PatternEditorInput) { |
| getSite().getPage().removePartListener(partListener); |
| removePatternChangeAdapter(); |
| } |
| // Initialized in initializeEditingDomain, if init failed, this must be disposed |
| PlatformResourceLoadedListener.getResourceManager().removeObserver(this); |
| getOperationHistory().removeOperationHistoryListener(historyListener); |
| getOperationHistory().dispose(undoContext, true, true, true); |
| editingDomain.getResourceSet().eAdapters().remove(editorResourceAdapter); |
| super.dispose(); |
| } |
| |
| private final IOperationHistoryListener historyListener = new IOperationHistoryListener() { |
| |
| public void historyNotification(final OperationHistoryEvent event) { |
| Set<Resource> affectedResources = ResourceUndoContext.getAffectedResources(event.getOperation()); |
| switch (event.getEventType()) { |
| case OperationHistoryEvent.DONE: |
| if (affectedResources.contains(getResource())) { |
| final IUndoableOperation operation = event.getOperation(); |
| // remove the default undo context so that we can have |
| // independent undo/redo of independent resource changes |
| operation.removeContext(((IWorkspaceCommandStack) getEditingDomain().getCommandStack()).getDefaultUndoContext()); |
| // add our undo context to populate our undo menu |
| operation.addContext(undoContext); |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } |
| |
| }); |
| } |
| break; |
| case OperationHistoryEvent.UNDONE: |
| case OperationHistoryEvent.REDONE: |
| if (affectedResources.contains(getResource())) { |
| getSite().getShell().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| } |
| |
| }); |
| } |
| break; |
| } |
| } |
| |
| }; |
| |
| public void addPage(PatternEditorPage page) throws PartInitException { |
| pages.add(page); |
| addPage((IFormPage) page); |
| } |
| |
| /** |
| * Shows a dialog that asks if conflicting changes should be discarded. |
| */ |
| protected boolean handleDirtyConflict() { |
| return MessageDialog.openQuestion(getSite().getShell(), CoreUIMessages._UI_FileConflict_label, CoreUIMessages._WARN_FileConflict); |
| } |
| |
| public ResourceListener getListener() { |
| return resourceListener; |
| } |
| |
| } |