/*
* $Id$
*
* Copyright (c) 2007-2008 by Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools.opcache;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* An abstract representation of an operation. <code>AbstractOpImpl</code>
* is the base class for all such operations. The results of all operations
* are memoized (using a memory-sensitive cache), so retrieving results is
* both fast and memory-efficient.
*
* <p><b>Warning:</b> For efficiency reasons, the methods {@link #get()} and
* {@link #get(OpObserver)} do <em>not</em> return defensively, nor do the
* {@code Future}s returned by {@link #getFuture(OpObserver)}. That is, the
* object returned is possibly the one retained internally by the
* <code>AbstractOpImpl</code>. Therefore, objects obtained from an
* <code>AbstractOpImpl</code> <em>must not</em> be altered, as this might
* interfere with caching. If an object obtained this way needs to be
* modified, copy the object first and alter the copy.</p>
*
* @author Joel Uckelman
* @since 3.1.0
*/
public abstract class AbstractOpImpl<V> implements Op<V> {
/** The cache which contains calculated results. */
protected final OpCache cache;
/**
* @param cache the cache for storing our result
*/
public AbstractOpImpl(OpCache cache) {
this.cache = cache;
}
/** {@inheritDoc} */
public abstract List<Op<?>> getSources();
/** {@inheritDoc} */
public abstract V eval() throws Exception;
/** {@inheritDoc} */
public V get() {
return cache.get(newKey());
}
/**
* {@inheritDoc}
*
* @throws CancellationException if the operation was cancelled
* @throws InterruptedException if the operation was interrupted
* @throws ExecutionException if the operation failed
*/
public V get(OpObserver<V> obs) throws CancellationException,
InterruptedException,
ExecutionException {
return cache.get(newKey(), obs);
}
/**
* {@inheritDoc}
*
* @throws CancellationException if the operation was cancelled
* @throws InterruptedException if the operation was interrupted
* @throws ExecutionException if the operation failed
*/
public Future<V> getFuture(OpObserver<V> obs) throws ExecutionException {
return cache.getFuture(newKey(), obs);
}
private static final ConcurrentMap<Op<?>,OpCache.Key<?>> kcache =
new ConcurrentHashMap<Op<?>,OpCache.Key<?>>();
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
public OpCache.Key<V> newKey() {
OpCache.Key<V> key = (OpCache.Key<V>) kcache.get(this);
if (key == null) {
final OpCache.Key<V> nkey = new OpCache.Key<V>(this, 0);
key = (OpCache.Key<V>) kcache.putIfAbsent(this, nkey);
if (key == null) key = nkey;
}
return key;
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
public void update() {
final OpCache.Key<V> key = (OpCache.Key<V>) kcache.get(this);
if (key != null) {
final OpCache.Key<V> nkey = new OpCache.Key<V>(this, key.version+1);
kcache.replace(this, key, nkey);
}
}
}