diff options
author | rschnekenbu | 2010-07-01 09:38:38 +0000 |
---|---|---|
committer | rschnekenbu | 2010-07-01 09:38:38 +0000 |
commit | f5a2428600e522a436f1fd879646a635768dff71 (patch) | |
tree | 6487ccfc23df205fb5a62ba1cdbaa9025aee5c71 /plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain | |
parent | 3b4d15be804ace03f2a09d1449bb0563dd769382 (diff) | |
download | org.eclipse.papyrus-f5a2428600e522a436f1fd879646a635768dff71.tar.gz org.eclipse.papyrus-f5a2428600e522a436f1fd879646a635768dff71.tar.xz org.eclipse.papyrus-f5a2428600e522a436f1fd879646a635768dff71.zip |
Share project "org.eclipse.xtext.gmf.glue" into "svn+ssh://dev.eclipse.org/svnroot/modeling/org.eclipse.mdt.papyrus"
Diffstat (limited to 'plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain')
4 files changed, 504 insertions, 0 deletions
diff --git a/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/ChangeAggregatorAdapter.java b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/ChangeAggregatorAdapter.java new file mode 100644 index 00000000000..c6279d42563 --- /dev/null +++ b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/ChangeAggregatorAdapter.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.gmf.glue.editingdomain; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.EContentAdapter; +import org.eclipse.xtext.resource.XtextResource; + +import com.google.common.collect.Lists; + +/** + * @author Knut Wannheden - Initial contribution and API + * @author Jan Koehnlein + */ +public class ChangeAggregatorAdapter extends EContentAdapter { + + private Collection<EObject> modifiedObjects = new LinkedHashSet<EObject>(); + + private boolean isRecording = false; + + private boolean isSuspended = false; + + @Override + public void notifyChanged(Notification notification) { + super.notifyChanged(notification); + + if (!doRecord(notification)) + return; + + if (notification.getNotifier() instanceof EObject) { + recordObjectModification((EObject) notification.getNotifier()); + } + } + + protected void recordObjectModification(EObject obj) { + if (obj.eResource() == null || false == obj.eResource() instanceof XtextResource) + modifiedObjects.remove(obj); + else + modifiedObjects.add(obj); + } + + protected boolean doRecord(Notification notification) { + if (!isRecording || isSuspended || notification.isTouch()) + return false; + + switch (notification.getEventType()) { + case Notification.ADD: + case Notification.ADD_MANY: + case Notification.MOVE: + case Notification.REMOVE: + case Notification.REMOVE_MANY: + case Notification.SET: + case Notification.UNSET: + return true; + default: + return false; + } + } + + private void reset() { + modifiedObjects.clear(); + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + */ + public void beginRecording() { + reset(); + isRecording = true; + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + */ + public void endRecording() { + isRecording = false; + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @param isSuspended + */ + public void setSuspended(boolean isSuspended) { + this.isSuspended = isSuspended; + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @return List<EOject> + */ + public List<EObject> getModificationRoots() { + Map<Resource, List<EObject>> resource2ChangePathMap = new HashMap<Resource, List<EObject>>(); + for (EObject eObject : modifiedObjects) { + if (!eObject.eIsProxy()) { + Resource resource = eObject.eResource(); + List<EObject> resourceChangePath = resource2ChangePathMap.get(resource); + if (resourceChangePath == null) { + resourceChangePath = allContainers(eObject); + resource2ChangePathMap.put(resource, resourceChangePath); + } else { + resourceChangePath.retainAll(allContainers(eObject)); + } + } + } + List<EObject> modificationRoots = new ArrayList<EObject>(resource2ChangePathMap.size()); + for (List<EObject> changePath : resource2ChangePathMap.values()) { + if (!changePath.isEmpty()) { + modificationRoots.add(changePath.get(changePath.size() - 1)); + } + } + return modificationRoots; + } + + private LinkedList<EObject> allContainers(EObject eObject) { + final LinkedList<EObject> allContainers = Lists.newLinkedList(); + allContainers.add(eObject); + EObject currentContainer = eObject.eContainer(); + final Resource resource = eObject.eResource(); + while (currentContainer != null && resource == currentContainer.eResource()) { + allContainers.addFirst(currentContainer); + currentContainer = currentContainer.eContainer(); + } + return allContainers; + } + + /** + * Only attach to XtextResources + */ + @Override + protected void setTarget(Resource target) { + if (target instanceof XtextResource) { + super.setTarget(target); + } + } + + @Override + protected void setTarget(EObject target) { + if (target.eResource() instanceof XtextResource) { + super.setTarget(target); + } + } + + @Override + public boolean isAdapterForType(Object type) { + return type == ChangeAggregatorAdapter.class; + } + +}
\ No newline at end of file diff --git a/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/SemanticRootUnloadListener.java b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/SemanticRootUnloadListener.java new file mode 100644 index 00000000000..264294f95b1 --- /dev/null +++ b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/SemanticRootUnloadListener.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.gmf.glue.editingdomain; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.transaction.NotificationFilter; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.ResourceSetListener; +import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; + +/** + * Reloads the semantic root element (the element of the diagram) on changes and refreshes the diagram. GMF does by + * default not listen to such events which can occur in an {@link XtextResource}, if the document changes close to the + * root element. + * + * Activate an instance of this in the {@link EditPart#activate()} method of the {@link DiagramEditPart}. + * + * @author koehnlein + */ +public class SemanticRootUnloadListener implements ResourceSetListener { + + private DiagramEditPart rootEditPart; + private EObject semanticRootElement; + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @param rootEditPart + * + */ + public SemanticRootUnloadListener(DiagramEditPart rootEditPart) { + this.rootEditPart = rootEditPart; + this.semanticRootElement = rootEditPart.resolveSemanticElement(); + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + */ + public void activate() { + rootEditPart.getEditingDomain().addResourceSetListener(this); + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + */ + public void deactivate() { + rootEditPart.getEditingDomain().removeResourceSetListener(this); + } + + public NotificationFilter getFilter() { + return new NotificationFilter.Custom() { + @Override + public boolean matches(Notification notification) { + int featureID = notification.getFeatureID(Resource.class); + Object notifier = notification.getNotifier(); + int eventType = notification.getEventType(); + return notification.getOldValue() == semanticRootElement && featureID == Resource.RESOURCE__CONTENTS + && (eventType == Notification.REMOVE || eventType == Notification.SET) + && notifier instanceof Resource; + } + }; + } + + public boolean isAggregatePrecommitListener() { + return false; + } + + public boolean isPostcommitOnly() { + return true; + } + + public boolean isPrecommitOnly() { + return false; + } + + public void resourceSetChanged(ResourceSetChangeEvent event) { + semanticRootElement = rootEditPart.resolveSemanticElement(); + rootEditPart.refresh(); + } + + public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { + return null; + } + +} diff --git a/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/UpdateXtextResourceTextCommand.java b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/UpdateXtextResourceTextCommand.java new file mode 100644 index 00000000000..3f8eeed176d --- /dev/null +++ b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/UpdateXtextResourceTextCommand.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.gmf.glue.editingdomain; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.common.core.command.CommandResult; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; +import org.eclipse.xtext.resource.XtextResource; + +/** + * A Command that deactivates the {@link ChangeAggregatorAdapter} and updates a textual section of an Xtext model in an + * Xtext resource. Used to avoid cycles in the change aggregation. + * + * @author koehnlein + */ +public class UpdateXtextResourceTextCommand { + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @param xtextResource + * @param offset + * @param length + * @param newText + * @return Command + */ + public static Command createEMFCommand(final XtextResource xtextResource, final int offset, final int length, + final String newText) { + final TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(xtextResource); + if (editingDomain == null) { + return null; + } + ResourceSet resourceSet = editingDomain.getResourceSet(); + final ChangeAggregatorAdapter changeAggregator = (ChangeAggregatorAdapter) EcoreUtil.getAdapter(resourceSet + .eAdapters(), ChangeAggregatorAdapter.class); + return new RecordingCommand(editingDomain, "update xtext resource") { + @Override + protected void doExecute() { + try { + if (changeAggregator != null) { + changeAggregator.setSuspended(true); + } + xtextResource.update(offset, length, newText); + xtextResource.setModified(true); + } finally { + if (changeAggregator != null) { + changeAggregator.setSuspended(false); + } + } + + } + }; + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @param xtextResource + * @param offset + * @param length + * @param newText + * @return ICommand + */ + public static ICommand createUpdateCommand(final XtextResource xtextResource, final int offset, final int length, + final String newText) { + final TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(xtextResource); + if (editingDomain == null) { + return null; + } + ResourceSet resourceSet = editingDomain.getResourceSet(); + final ChangeAggregatorAdapter changeAggregator = (ChangeAggregatorAdapter) EcoreUtil.getAdapter(resourceSet + .eAdapters(), ChangeAggregatorAdapter.class); + return new AbstractTransactionalCommand(editingDomain, "update xtext resource", null) { + @Override + protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) + throws ExecutionException { + try { + if (changeAggregator != null) { + changeAggregator.setSuspended(true); + } + xtextResource.update(offset, length, newText); + xtextResource.setModified(true); + return CommandResult.newOKCommandResult(); + } catch (Exception exc) { + return CommandResult.newErrorCommandResult(exc); + } finally { + if (changeAggregator != null) { + changeAggregator.setSuspended(false); + } + } + } + }; + } + +} diff --git a/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/XtextNodeModelReconciler.java b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/XtextNodeModelReconciler.java new file mode 100644 index 00000000000..e78882ae6cf --- /dev/null +++ b/plugins/core/org.eclipse.xtext.gmf.glue/src/org/eclipse/xtext/gmf/glue/editingdomain/XtextNodeModelReconciler.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.gmf.glue.editingdomain; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.operations.IOperationHistory; +import org.eclipse.core.commands.operations.IOperationHistoryListener; +import org.eclipse.core.commands.operations.OperationHistoryEvent; +import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.TransactionalEditingDomainEvent; +import org.eclipse.emf.transaction.TransactionalEditingDomainListener; +import org.eclipse.emf.transaction.TransactionalEditingDomain.Lifecycle; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.emf.workspace.IWorkspaceCommandStack; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.xtext.gmf.glue.Activator; +import org.eclipse.xtext.parsetree.CompositeNode; +import org.eclipse.xtext.parsetree.NodeAdapter; +import org.eclipse.xtext.parsetree.NodeUtil; +import org.eclipse.xtext.parsetree.reconstr.Serializer; +import org.eclipse.xtext.resource.XtextResource; + +/** + * Reconciles the node models of all XtextResources in a TransactionalEditingDomain with semantic changes. + * + * @author koehnlein + */ +public class XtextNodeModelReconciler extends AdapterImpl implements TransactionalEditingDomainListener, + IOperationHistoryListener { + + private TransactionalEditingDomain editingDomain; + + private ChangeAggregatorAdapter changeAggregator; + + private XtextNodeModelReconciler(TransactionalEditingDomain editingDomain) { + this.editingDomain = editingDomain; + Lifecycle lifecycle = TransactionUtil.getAdapter(editingDomain, Lifecycle.class); + lifecycle.addTransactionalEditingDomainListener(this); + changeAggregator = new ChangeAggregatorAdapter(); + editingDomain.getResourceSet().eAdapters().add(changeAggregator); + CommandStack commandStack = editingDomain.getCommandStack(); + if (commandStack instanceof IWorkspaceCommandStack) { + IOperationHistory operationHistory = ((IWorkspaceCommandStack) commandStack).getOperationHistory(); + operationHistory.addOperationHistoryListener(this); + } + changeAggregator.beginRecording(); + } + + public void editingDomainDisposing(TransactionalEditingDomainEvent event) { + changeAggregator.endRecording(); + CommandStack commandStack = editingDomain.getCommandStack(); + if (commandStack instanceof IWorkspaceCommandStack) { + IOperationHistory operationHistory = ((IWorkspaceCommandStack) commandStack).getOperationHistory(); + operationHistory.removeOperationHistoryListener(this); + } + editingDomain.getResourceSet().eAdapters().remove(changeAggregator); + Lifecycle lifecycle = TransactionUtil.getAdapter(editingDomain, Lifecycle.class); + lifecycle.removeTransactionalEditingDomainListener(XtextNodeModelReconciler.this); + } + + public void transactionClosed(TransactionalEditingDomainEvent event) { + // ignore + } + + public void transactionClosing(TransactionalEditingDomainEvent event) { + // ignore + } + + public void transactionInterrupted(TransactionalEditingDomainEvent event) { + // ignore + } + + public void transactionStarted(TransactionalEditingDomainEvent event) { + // ignore + } + + public void transactionStarting(TransactionalEditingDomainEvent event) { + // ignore + } + + /** + * This element comes from the XText/GMF integration example, and was not originally documented. + * @param editingDomain + * @return XtextNodeModelReconciler + * + */ + public static XtextNodeModelReconciler adapt(TransactionalEditingDomain editingDomain) { + XtextNodeModelReconciler adapter = (XtextNodeModelReconciler) EcoreUtil.getAdapter(editingDomain + .getResourceSet().eAdapters(), XtextNodeModelReconciler.class); + if (adapter == null) { + adapter = new XtextNodeModelReconciler(editingDomain); + } + return adapter; + } + + public void historyNotification(OperationHistoryEvent event) { + int eventType = event.getEventType(); + switch (eventType) { + case OperationHistoryEvent.DONE: + case OperationHistoryEvent.UNDONE: + case OperationHistoryEvent.REDONE: + changeAggregator.endRecording(); + ICommand updateXtextResourceTextCommand = null; + for (EObject modificationRoot : changeAggregator.getModificationRoots()) { + XtextResource xtextResource = (XtextResource) modificationRoot.eResource(); + NodeAdapter nodeAdapter = NodeUtil.getNodeAdapter(modificationRoot); + CompositeNode parserNode = nodeAdapter.getParserNode(); + Serializer serializer = xtextResource.getSerializer(); + String newText = serializer.serialize(modificationRoot); + ICommand newCommand = UpdateXtextResourceTextCommand.createUpdateCommand(xtextResource, parserNode + .getOffset(), parserNode.getLength(), newText); + if (updateXtextResourceTextCommand == null) { + updateXtextResourceTextCommand = newCommand; + } else { + updateXtextResourceTextCommand.compose(newCommand); + } + } + try { + if (updateXtextResourceTextCommand != null) { + updateXtextResourceTextCommand.execute(null, null); + } + } catch (ExecutionException exc) { + Activator.logError(exc); + } + changeAggregator.beginRecording(); + break; + default: + // ignore + } + + } +} |