/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* 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
*
* Contributors:
* * Anya Helene Bagge - anya@ii.uib.no (Univ. Bergen)
*******************************************************************************/
package org.rascalmpl.tasks.facts;
import static org.rascalmpl.tasks.IDependencyListener.Change.AVAILABLE;
import static org.rascalmpl.tasks.IDependencyListener.Change.CHANGED;
import static org.rascalmpl.tasks.IDependencyListener.Change.INVALIDATED;
import static org.rascalmpl.tasks.IDependencyListener.Change.REMOVED;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.rascalmpl.tasks.IDependencyListener;
import org.rascalmpl.tasks.IExpirationListener;
import org.rascalmpl.tasks.IFact;
public abstract class AbstractFact<V> implements IFact<V> {
static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
protected V value = null;
private IRef<V> valueRef = null;
protected int status = IFact.FACT_DEPS_CHANGED;
protected final Set<IDependencyListener> listeners = new HashSet<IDependencyListener>();
protected final Set<IFact<?>> dependencies = new HashSet<IFact<?>>();
protected final Object key;
protected final String keyName;
protected IExpirationListener<V> exp;
protected AbstractFact(Object key, String keyName, IExpirationListener<V> exp) {
this.key = key;
this.keyName = keyName;
this.exp = exp;
}
public boolean isValid() {
return status == IFact.FACT_OK;
}
public void registerListener(IDependencyListener listener) {
listeners.add(listener);
}
public void unregisterListener(IDependencyListener listener) {
listeners.remove(listener);
}
public synchronized void remove() {
for(IDependencyListener l : listeners) {
l.changed(this, REMOVED, null);
}
listeners.clear();
for(IFact<?> f : dependencies) {
f.unregisterListener(this);
}
dependencies.clear();
value = null;
clearRef();
}
protected void notifyInvalidated() {
for(IDependencyListener f : listeners) {
f.changed(this, INVALIDATED, null);
}
}
protected void notifyChanged() {
for(IDependencyListener f : listeners) {
f.changed(this, CHANGED, null);
}
}
protected void notifyAvailable() {
for(IDependencyListener f : listeners) {
f.changed(this, AVAILABLE, null);
}
}
public Collection<IDependencyListener> getListeners() {
return Collections.unmodifiableCollection(listeners);
}
public Object getKey() {
return key;
}
public String toString() {
return keyName;
}
public synchronized void setDepends(Collection<IFact<V>> deps) {
for(IFact<?> foo : dependencies) {
if(!deps.contains(foo))
foo.unregisterListener(this);
}
for(IFact<?> foo : deps) {
if(!dependencies.contains(foo))
foo.registerListener(this);
}
dependencies.clear();
dependencies.addAll(deps);
}
public synchronized Collection<IFact<?>> getDepends() {
return Collections.unmodifiableCollection(dependencies);
}
public int getStatus() {
return status;
}
public synchronized void expire() {
if(status != FACT_OK) {
if(exp != null)
exp.expire(key);
for(IDependencyListener l : listeners) {
l.changed(this, Change.EXPIRED, null);
}
listeners.clear();
for(IFact<?> f : dependencies) {
f.unregisterListener(this);
}
dependencies.clear();
value = null;
clearRef();
}
else
valueRef = null;
}
protected void setRefWeak(V value) {
valueRef = new WeakRef<V>(value, this);
}
protected void setRefSoft(V value) {
valueRef = new SoftRef<V>(value, this);
}
protected void clearRef() {
if(valueRef != null) {
valueRef.clear();
valueRef = null;
}
}
protected V getRef() {
if(valueRef != null)
return valueRef.get();
else
return null;
}
public static void pruneExpired() {
Object o = queue.poll();
while(o != null) {
AbstractFact<?> fact = ((IRef<?>) o).getFact();
fact.expire();
o = queue.poll();
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((keyName == null) ? 0 : keyName.hashCode());
result = prime * result + status;
result = prime * result + ((value == null) ? 0 : value.hashCode());
result = prime * result
+ ((valueRef == null) ? 0 : valueRef.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
AbstractFact<V> other = (AbstractFact<V>) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
if (status != other.status)
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
if (valueRef == null) {
if (other.valueRef != null)
return false;
} else if (!valueRef.equals(other.valueRef))
return false;
return true;
}
}
interface IRef<V> {
public AbstractFact<V> getFact();
public V get();
public void clear();
public int hashCode();
}
class WeakRef<V> extends WeakReference<V> implements IRef<V> {
private final AbstractFact<V> fact;
WeakRef(V v, AbstractFact<V> fact) {
super(v, AbstractFact.queue);
this.fact = fact;
}
public AbstractFact<V> getFact() {
return fact;
}
}
class SoftRef<V> extends SoftReference<V> implements IRef<V> {
private final AbstractFact<V> fact;
SoftRef(V v, AbstractFact<V> fact) {
super(v, AbstractFact.queue);
this.fact = fact;
}
public AbstractFact<V> getFact() {
return fact;
}
}