Skip to content

Commit

Permalink
EMF driver: use adapters to keep cached allContents/allInstances up t…
Browse files Browse the repository at this point in the history
…o date (fixes #508075)
  • Loading branch information
agarciadom committed Nov 23, 2016
1 parent 9bd80dd commit 699896d
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
******************************************************************************/
package org.eclipse.epsilon.examples.testlang.dt.launching;

import org.eclipse.epsilon.eol.IEolExecutableModule;
import org.eclipse.epsilon.eol.IEolModule;
import org.eclipse.epsilon.eol.dt.launching.EolLaunchConfigurationDelegate;
import org.eclipse.epsilon.examples.testlang.engine.TestLangModule;

public class TestLangLaunchConfigurationDelegate extends EolLaunchConfigurationDelegate {

@Override
public IEolExecutableModule createModule() {
public IEolModule createModule() {
return new TestLangModule();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;

import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.common.util.URI;
Expand All @@ -31,6 +32,7 @@
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.epsilon.common.util.StringProperties;
Expand Down Expand Up @@ -175,6 +177,40 @@ protected void loadModel() throws EolModelLoadingException {
setupContainmentChangeListeners();
}

/**
* This listener is the one that keeps the cached .allInstances
* and model .allContents() lists up to date, instead of the usual
* createInstance/deleteInstance methods.
*
* This prevents the cache from becoming inconsistent if EcoreUtil
* or any other code outside Epsilon is used to create instances
* within the model.
*/
protected class CachedContentsAdapter extends EContentAdapter {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);

try {
switch (notification.getEventType()) {
case Notification.ADD:
EObject added = (EObject) notification.getNewValue();
forceAddToCache(added.eClass().getName(), added);
break;
case Notification.REMOVE:
EObject removed = (EObject) notification.getOldValue();
forceRemoveFromCache(removed);
break;
default:
// do nothing
break;
}
} catch (EolModelElementTypeNotFoundException ex) {
ex.printStackTrace();
}
}
}

/**
* Used for backwards-compatibility with existing Eclipse launch configurations.
*
Expand Down Expand Up @@ -210,7 +246,32 @@ private static void migrateUriValue(StringProperties properties, String oldPrope
}
}
}


@Override
protected void addToCache(String type, EObject instance) throws EolModelElementTypeNotFoundException {
// do nothing (we want to trigger changes through EMF adapters instead)
}

@Override
protected void removeFromCache(EObject instance) throws EolModelElementTypeNotFoundException {
// do nothing (we want to trigger changes through EMF adapters instead)
}

/**
* We want to use the overridden method, but not from
* {@link #createInstance(String)}, but rather from the adapter we set up to
* track additions and removals from the contents of a model. For this reason,
* we leave the overridden method empty and define this one that can be safely
* called from the adapter.
*/
protected void forceAddToCache(String type, EObject instance) throws EolModelElementTypeNotFoundException {
super.addToCache(type, instance);
}

/** @see #forceAddToCache(String, EObject) */
protected void forceRemoveFromCache(EObject instance) throws EolModelElementTypeNotFoundException {
super.removeFromCache(instance);
}

public void setupContainmentChangeListeners() {
// Add a notification adapter to all objects in the model
Expand Down Expand Up @@ -269,6 +330,7 @@ public void loadModelFromUri() throws EolModelLoadingException {
}
}
modelImpl = model;
modelImpl.eAdapters().add(new CachedContentsAdapter());
}

public List<String> getMetamodelFiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Collections;
import java.util.List;

import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;

Expand Down Expand Up @@ -93,6 +94,17 @@ protected void init(String name, Resource modelImpl, Collection<EPackage> ePacka
}
}

boolean bHasCachedContentsAdapter = false;
for (Adapter adapter : modelImpl.eAdapters()) {
if (adapter instanceof CachedContentsAdapter) {
bHasCachedContentsAdapter = true;
break;
}
}
if (!bHasCachedContentsAdapter) {
modelImpl.eAdapters().add(new CachedContentsAdapter());
}

if (isContainerListenerEnabled) {
this.setupContainmentChangeListeners();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ public abstract class CachedModel<ModelElementType> extends Model {

protected boolean cachingEnabled = true;

protected void addToCache(String type, ModelElementType instance) throws EolModelElementTypeNotFoundException {
allContentsCache.add(instance);
typeCache.put(getCacheKeyForType(type), instance);

for (String kind : getAllTypeNamesOf(instance)) {
kindCache.put(getCacheKeyForType(kind), instance);
}
}

protected void removeFromCache(ModelElementType instance) throws EolModelElementTypeNotFoundException {
allContentsCache.remove(instance);

typeCache.remove(getCacheKeyForType(getTypeNameOf(instance)), instance);

for (String kind : getAllTypeNamesOf(instance)) {
kindCache.remove(getCacheKeyForType(kind), instance);
}
}

public void setCachingEnabled(boolean cachingEnabled) {
this.cachingEnabled = cachingEnabled;
}
Expand Down Expand Up @@ -131,38 +150,22 @@ public ModelElementType createInstance(String type) throws EolModelElementTypeNo
ModelElementType instance = createInstanceInModel(type);

if (isCachingEnabled()) {
allContentsCache.add(instance);

typeCache.put(getCacheKeyForType(type), instance);

for (String kind : getAllTypeNamesOf(instance)) {
kindCache.put(getCacheKeyForType(kind), instance);
}
addToCache(type, instance);
}

return instance;
}

public void deleteElement(Object o) throws EolRuntimeException {
if (deleteElementInModel(o)) {

if (cachingEnabled) {

if (isCachingEnabled()) {
@SuppressWarnings("unchecked")
ModelElementType instance = (ModelElementType) o;

allContentsCache.remove(instance);

typeCache.remove(getCacheKeyForType(getTypeNameOf(instance)), instance);

for (String kind : getAllTypeNamesOf(instance)) {
kindCache.remove(getCacheKeyForType(kind), instance);
}

removeFromCache(instance);
}
}
}

public void load() throws EolModelLoadingException {
clearCache();
loadModel();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.eclipse.epsilon.emc.emf;

import static org.junit.Assert.assertEquals;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.junit.Test;

public class EmfModelCachedContentsTests {

protected EmfModel createEmptyModel() throws EolModelLoadingException {
final EmfModel model = new EmfModel();
model.setModelFile("dummy.xmi");
model.setMetamodelUri(EcorePackage.eINSTANCE.getNsURI());
model.setReadOnLoad(false);
model.setStoredOnDisposal(false);
model.load();
return model;
}

@Test
public void emptyModel() throws Exception {
final EmfModel model = createEmptyModel();
assertEquals(0, model.allContents().size());
}

@Test
public void manageThroughModel() throws Exception {
final EmfModel model = createEmptyModel();
assertEquals(0, model.allContents().size());
final EObject eClass = model.createInstance("EClass");
assertEquals(1, model.allContents().size());
model.deleteElement(eClass);
assertEquals(0, model.allContents().size());
}

@Test
public void manageExternally() throws Exception {
final EmfModel model = createEmptyModel();
assertEquals(0, model.allContents().size());

final EClass eClass = EcorePackage.eINSTANCE.getEcoreFactory().createEClass();
model.getResource().getContents().add(eClass);
assertEquals(1, model.allContents().size());

model.getResource().getContents().remove(eClass);
assertEquals(0, model.allContents().size());
}

}

0 comments on commit 699896d

Please sign in to comment.