package de.hub.emffrag.fragmentation;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* Caches {@link FObjectImpl} instances for {@link FInternalObjectImpl}
* instances to avoid unnecessary duplication of user values. When an internal
* object gets unloaded the cache entry is replaced with a cache entry based on
* URI. If an internal object is loaded, this is done vice versa.
*/
public class UserObjectsCache {
public final static UserObjectsCache instance = new UserObjectsCache();
private final Cache<FInternalObjectImpl, FObjectImpl> cache;
private final Cache<URI, FObjectImpl> uriCache;
public UserObjectsCache() {
cache = CacheBuilder.newBuilder().weakValues().build();
uriCache = CacheBuilder.newBuilder().weakValues().build();
}
public FObjectImpl getUserObject(final FInternalObjectImpl internalObject) {
try {
FObjectImpl userValue = cache.getIfPresent(internalObject);
if (userValue == null) {
URI uri = EcoreUtil.getURI(internalObject);
userValue = uriCache.getIfPresent(uri);
if (userValue != null) {
uriCache.invalidate(uri);
cache.put(internalObject, userValue);
userValue.fSetInternalObject(internalObject, true);
}
}
if (userValue == null) {
return cache.get(internalObject, new Callable<FObjectImpl>() {
@Override
public FObjectImpl call() throws Exception {
return createUserObject(internalObject);
}
});
} else {
return userValue;
}
} catch (ExecutionException e) {
throw new RuntimeException();
}
}
private FObjectImpl createUserObject(FInternalObjectImpl internalObject) {
EPackage userMetaModel = ReflectiveMetaModelRegistry.instance
.getUserMetaModel(internalObject.eClass().getEPackage());
FObjectImpl userObject = (FObjectImpl) userMetaModel
.getEFactoryInstance().create(
ReflectiveMetaModelRegistry.instance
.getUserClass(internalObject.eClass()));
userObject.fSetInternalObject(internalObject, false);
cache.put(internalObject, userObject);
return userObject;
}
FInternalObjectImpl createInternalObject(FObjectImpl userObject) {
// create an uncontained internal object
// add the new internal object to the map for all uncontained (not
// added) internal objects
// TODO this seems to have low performance
EClass userClass = userObject.eClass();
EPackage internalPackage = ReflectiveMetaModelRegistry.instance
.registerUserMetaModel(userClass.getEPackage());
FInternalObjectImpl internalObject = new FInternalObjectImpl(
(EClass) internalPackage.getEClassifier(userClass.getName()));
cache.put(internalObject, userObject);
userObject.fSetInternalObject(internalObject, false);
return internalObject;
}
public int size() {
cache.cleanUp();
return (int) cache.size();
}
public boolean hasUserObject(FInternalObjectImpl instance) {
FObjectImpl userValue = cache.getIfPresent(instance);
if (userValue == null) {
URI uri = EcoreUtil.getURI(instance);
userValue = uriCache.getIfPresent(uri);
}
return userValue != null;
}
public void unload(FInternalObjectImpl object) {
FObjectImpl userValue = cache.getIfPresent(object);
if (userValue != null) {
cache.invalidate(object);
URI uri = EcoreUtil.getURI(object);
if (uri.segmentCount() > 0) {
uriCache.put(uri, userValue);
}
}
}
}