org.eclipse.xtext.util.OnChangeEvictingCache Maven / Gradle / Ivy
/*******************************************************************************
* 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.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Provider;
import com.google.inject.Singleton;
/**
* A cache implementation that stores its values in the scope of a resource.
* The values will be discarded as soon as the contents of the resource changes semantically.
* Clients may override this behavior for certain transactions by means of {@link #execWithoutCacheClear(Resource, IUnitOfWork)}
* or {@link #execWithTemporaryCaching(Resource, IUnitOfWork)}.
*
* @author Sven Efftinge - Initial contribution and API
* @author Sebastian Zarnekow
*/
@Singleton
public class OnChangeEvictingCache implements IResourceScopeCache {
private static final Logger log = Logger.getLogger(OnChangeEvictingCache.class);
/**
* A simple cache listener. It will be notified if the cache is cleared.
* The notification will only occur once. Listeners that are interested in subsequent notifications
* have to re-add themselves.
*/
public static interface Listener {
void onEvict(CacheAdapter cache);
}
/**
* Clears the cache of the given resource.
*/
public void clear(Resource resource) {
getOrCreate(resource).clearValues();
}
/**
* Try to obtain the value that is cached for the given key in the given resource.
* If no value is cached, the provider is used to compute it and store it afterwards.
* @param resource the resource. If it is null
, the provider will be used to compute the value.
* @param key the cache key. May not be null
.
* @param provider the strategy to compute the value if necessary. May not be null
.
*/
public T get(Object key, Resource resource, Provider provider) {
if(resource == null) {
return provider.get();
}
CacheAdapter adapter = getOrCreate(resource);
T element = adapter.internalGet(key);
if (element==null) {
element = provider.get();
cacheMiss(adapter);
adapter.set(key, element);
} else {
cacheHit(adapter);
}
if (element == CacheAdapter.NULL) {
return null;
}
return element;
}
/**
* Announce a cache miss for the internal statistics of the adapter.
* @since 2.1
*/
protected void cacheMiss(CacheAdapter adapter) {
adapter.cacheMiss();
}
/**
* Announce a cache hit for the internal statistics of the adapter.
* @since 2.1
*/
protected void cacheHit(CacheAdapter adapter) {
adapter.cacheHit();
}
/**
* Returns the cache adapter that is associated with the resource. The lifecycle of the cache
* is strongly connected to the resource and its change notifications. Will not return null
.
* @param resource the resource. May not be null
.
* @return the cache adapter for the given resource. Never null
.
*/
public CacheAdapter getOrCreate(Resource resource) {
CacheAdapter adapter = (CacheAdapter) EcoreUtil.getAdapter(resource.eAdapters(), CacheAdapter.class);
if (adapter == null) {
adapter = new CacheAdapter();
resource.eAdapters().add(adapter);
adapter.setResource(resource);
}
return adapter;
}
/**
* The transaction will be executed. While it is running, any semantic state change
* in the given resource will be ignored and the cache will not be cleared.
*/
public Result execWithoutCacheClear(Param resource, IUnitOfWork transaction) throws WrappedException {
CacheAdapter cacheAdapter = getOrCreate(resource);
try {
cacheAdapter.ignoreNotifications();
return transaction.exec(resource);
} catch (Exception e) {
throw new WrappedException(e);
} finally {
cacheAdapter.listenToNotifications();
}
}
/**
* The transaction will be executed with caching enabled. However, all newly cached values will be discarded as soon
* as the transaction is over.
* @since 2.1
*/
public Result execWithTemporaryCaching(Param resource, IUnitOfWork transaction) throws WrappedException {
CacheAdapter cacheAdapter = getOrCreate(resource);
IgnoreValuesMemento memento = cacheAdapter.ignoreNewValues();
try {
return transaction.exec(resource);
} catch (Exception e) {
throw new WrappedException(e);
} finally {
memento.done();
}
}
private static class IgnoreValuesMemento {
private final List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy