/*******************************************************************************
* 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.MOVED_TO;
import static org.rascalmpl.tasks.IDependencyListener.Change.REMOVED;
import java.util.Collection;
import java.util.Iterator;
import org.rascalmpl.tasks.IDependencyListener;
import org.rascalmpl.tasks.IExpirationListener;
import org.rascalmpl.tasks.IFact;
import org.rascalmpl.value.IValue;
/**
* This class implements fact storage for strongly referenced facts (i.e., a fact will never be removed
* by the garbarge collector unless it is explicitly removed from the database) with fine-grained
* tracking of dependencies.
*
* @author anya
*
*/
public class FineGrainedStrongFact<V> extends AbstractFact<V> {
public FineGrainedStrongFact(Object key, String keyName, IExpirationListener<V> exp) {
super(key, keyName, exp);
}
/* (non-Javadoc)
* @see org.rascalmpl.eclipse.db.IFact#setValue(org.rascalmpl.value.T)
*/
public synchronized boolean setValue(V val) {
V oldValue = value;
if(oldValue == null)
oldValue = getRef();
clearRef();
value = val;
status = IFact.FACT_OK;
if(oldValue != null &&
!(value instanceof IValue ? ((IValue)oldValue).isEqual((IValue)val) : oldValue.equals(value))) {
notifyChanged();
return true;
}
else
return false;
}
@SuppressWarnings("incomplete-switch")
public synchronized void changed(IFact<?> fact, Change change, Object moreInfo) {
switch(change) {
case CHANGED:
if(status < IFact.FACT_DEPS_CHANGED) {
System.out.println("CHANGED: " + fact + " recv by " + this);
setRefWeak(value);
value = null;
status = IFact.FACT_DEPS_CHANGED;
notifyInvalidated();
}
break;
case INVALIDATED:
if(status < IFact.FACT_DEPS_INVALID) {
System.out.println("INVALID: " + fact + " recv by " + this);
status = IFact.FACT_DEPS_INVALID;
notifyInvalidated();
}
break;
case REMOVED:
if(status < IFact.FACT_DEPS_CHANGED) {
dependencies.remove(fact);
setRefWeak(value);
value = null;
status = IFact.FACT_DEPS_CHANGED;
notifyInvalidated();
}
break;
case MOVED_TO:
if(dependencies.remove(fact)) {
dependencies.add((IFact<?>) moreInfo);
}
break;
case EXPIRED:
if(dependencies.remove(fact)) {
Collection<IFact<?>> deps = fact.getDepends();
dependencies.addAll(deps);
for(IFact<?> dep : deps)
dep.registerListener(this);
}
break;
}
}
/**
* Disposing of a fact means telling all our dependencies to notify our
* dependents instead about any changes. This is done when the fact itself
* is still useful, but we don't want to keep it in memory anymore. (We
* basically link ourselves out of the dependency chain)
*/
public void dispose() {
for(IFact<?> f : dependencies) {
f.unregisterListener(this);
}
for(IDependencyListener l : listeners) {
l.changed(this, REMOVED, null);
}
dependencies.clear();
listeners.clear();
value = null;
clearRef();
}
public synchronized V getValue() {
if(status == IFact.FACT_OK)
return value;
else
return null;
}
@SuppressWarnings("unchecked")
public synchronized boolean updateFrom(IFact<V> fact) {
boolean result = false;
synchronized(fact) {
if(fact instanceof AbstractFact<?>) {
AbstractFact<?> f = (AbstractFact<?>)fact;
int oldStatus = status;
status = f.status;
V oldValue = value;
if(oldValue == null)
oldValue = getRef();
if (status == FACT_OK) {
value = (V) f.value;
if (oldValue != null
&& !(oldValue instanceof IValue ? ((IValue) oldValue)
.isEqual((IValue) value) : oldValue
.equals(value))) {
notifyChanged();
result = true;
}
}
else if (oldStatus == FACT_OK) {
notifyInvalidated();
value = null;
clearRef();
}
//else
// throw new ImplementationError("Trying to update from fact with incompatible value types");
Iterator<IFact<?>> iterator = dependencies.iterator();
while(iterator.hasNext()) {
IFact<?> df = iterator.next();
if(!f.dependencies.contains(df)) {
iterator.remove();
df.unregisterListener(this);
}
}
for(IFact<?> df : f.dependencies) {
if(!dependencies.contains(df)) {
dependencies.add(df);
df.registerListener(this);
}
}
for(IDependencyListener dl : f.listeners)
dl.changed(f, MOVED_TO, this);
listeners.addAll(f.listeners);
}
}
return result;
}
}