org.eclipse.xtext.util.OnChangeEvictingCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.xtext.util Show documentation
Show all versions of org.eclipse.xtext.util Show documentation
Utility classes used throughout Xtext.
The newest version!
/*******************************************************************************
* Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
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.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.
*/
@Override
public void clear(Resource resource) {
CacheAdapter cacheAdapter = findCacheAdapter(resource);
if (cacheAdapter != null) {
cacheAdapter.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
.
*/
@Override
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 life cycle 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 = findCacheAdapter(resource);
if (adapter == null) {
adapter = createCacheAdapter();
resource.eAdapters().add(adapter);
adapter.setResource(resource);
}
return adapter;
}
/**
* @since 2.26
*/
protected CacheAdapter findCacheAdapter(Resource resource) {
return (CacheAdapter) EcoreUtil.getAdapter(resource.eAdapters(), CacheAdapter.class);
}
/**
* @since 2.23
*/
protected CacheAdapter createCacheAdapter() {
return new CacheAdapter();
}
/**
* 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 (RuntimeException e) {
throw e;
} 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