package util.gdl.grammar;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public final class GdlPool
{
private static final ConcurrentMap<String, GdlConstant> constantPool = new ConcurrentHashMap<String, GdlConstant>();
private static final ConcurrentMap<GdlTerm, ConcurrentMap<GdlTerm, GdlDistinct>> distinctPool = new ConcurrentHashMap<GdlTerm, ConcurrentMap<GdlTerm, GdlDistinct>>();
private static final ConcurrentMap<GdlConstant, ConcurrentMap<List<GdlTerm>, GdlFunction>> functionPool = new ConcurrentHashMap<GdlConstant, ConcurrentMap<List<GdlTerm>, GdlFunction>>();
private static final ConcurrentMap<GdlLiteral, GdlNot> notPool = new ConcurrentHashMap<GdlLiteral, GdlNot>();
private static final ConcurrentMap<List<GdlLiteral>, GdlOr> orPool = new ConcurrentHashMap<List<GdlLiteral>, GdlOr>();
private static final ConcurrentMap<GdlConstant, GdlProposition> propositionPool = new ConcurrentHashMap<GdlConstant, GdlProposition>();
private static final ConcurrentMap<GdlConstant, ConcurrentMap<List<GdlTerm>, GdlRelation>> relationPool = new ConcurrentHashMap<GdlConstant, ConcurrentMap<List<GdlTerm>, GdlRelation>>();
private static final ConcurrentMap<GdlSentence, ConcurrentMap<List<GdlLiteral>, GdlRule>> rulePool = new ConcurrentHashMap<GdlSentence, ConcurrentMap<List<GdlLiteral>, GdlRule>>();
private static final ConcurrentMap<String, GdlVariable> variablePool = new ConcurrentHashMap<String, GdlVariable>();
/**
* Drains the contents of the GdlPool. Useful to control memory usage
* once you have finished playing a large game.
*
* WARNING: Should only be called *between games*.
*/
public static void drainPool() {
distinctPool.clear();
functionPool.clear();
notPool.clear();
orPool.clear();
propositionPool.clear();
relationPool.clear();
rulePool.clear();
variablePool.clear();
// NOTE: We do *not* drain the constantPool because, elsewhere,
// parts of the Prover rely on having a handle to the "true" constant
// that does not change over the course of the program.
//constantPool.clear();
}
/**
* If the pool does not have a mapping for the given key, adds a mapping from key to value
* to the pool.
*
* Note that even if you've checked to make sure that the pool doesn't contain the key,
* you still shouldn't assume that this method actually inserts the given value, since
* this class is accessed by multiple threads simultaneously.
*
* @return the value mapped to by key in the pool
*/
private static <K,V> V addToPool(K key, V value, ConcurrentMap<K, V> pool) {
V prevValue = pool.putIfAbsent(key, value);
if(prevValue == null)
return value;
else
return prevValue;
}
public static GdlConstant getConstant(String value)
{
GdlConstant ret = constantPool.get(value);
if(ret == null)
ret = addToPool(value, new GdlConstant(value), constantPool);
return ret;
}
public static GdlDistinct getDistinct(GdlTerm arg1, GdlTerm arg2)
{
ConcurrentMap<GdlTerm, GdlDistinct> bucket = distinctPool.get(arg1);
if(bucket == null)
bucket = addToPool(arg1, new ConcurrentHashMap<GdlTerm, GdlDistinct>(), distinctPool);
GdlDistinct ret = bucket.get(arg2);
if(ret == null)
ret = addToPool(arg2, new GdlDistinct(arg1, arg2), bucket);
return ret;
}
public static GdlFunction getFunction(GdlConstant name)
{
List<GdlTerm> empty = Collections.emptyList();
return getFunction(name, empty);
}
public static GdlFunction getFunction(GdlConstant name, GdlTerm[] body)
{
return getFunction(name, Arrays.asList(body));
}
public static GdlFunction getFunction(GdlConstant name, List<GdlTerm> body)
{
ConcurrentMap<List<GdlTerm>, GdlFunction> bucket = functionPool.get(name);
if(bucket == null)
bucket = addToPool(name, new ConcurrentHashMap<List<GdlTerm>, GdlFunction>(), functionPool);
GdlFunction ret = bucket.get(body);
if(ret == null)
ret = addToPool(body, new GdlFunction(name, body), bucket);
return ret;
}
public static GdlNot getNot(GdlLiteral body)
{
GdlNot ret = notPool.get(body);
if(ret == null)
ret = addToPool(body, new GdlNot(body), notPool);
return ret;
}
public static GdlOr getOr(GdlLiteral[] disjuncts)
{
return getOr(Arrays.asList(disjuncts));
}
public static GdlOr getOr(List<GdlLiteral> disjuncts)
{
GdlOr ret = orPool.get(disjuncts);
if(ret == null)
ret = addToPool(disjuncts, new GdlOr(disjuncts), orPool);
return ret;
}
public static GdlProposition getProposition(GdlConstant name)
{
GdlProposition ret = propositionPool.get(name);
if(ret == null)
ret = addToPool(name, new GdlProposition(name), propositionPool);
return ret;
}
public static GdlRelation getRelation(GdlConstant name)
{
List<GdlTerm> empty = Collections.emptyList();
return getRelation(name, empty);
}
public static GdlRelation getRelation(GdlConstant name, GdlTerm[] body)
{
return getRelation(name, Arrays.asList(body));
}
public static GdlRelation getRelation(GdlConstant name, List<GdlTerm> body)
{
ConcurrentMap<List<GdlTerm>, GdlRelation> bucket = relationPool.get(name);
if(bucket == null)
bucket = addToPool(name, new ConcurrentHashMap<List<GdlTerm>, GdlRelation>(), relationPool);
GdlRelation ret = bucket.get(body);
if(ret == null)
ret = addToPool(body, new GdlRelation(name, body), bucket);
return ret;
}
public static GdlRule getRule(GdlSentence head)
{
List<GdlLiteral> empty = Collections.emptyList();
return getRule(head, empty);
}
public static GdlRule getRule(GdlSentence head, GdlLiteral[] body)
{
return getRule(head, Arrays.asList(body));
}
public static GdlRule getRule(GdlSentence head, List<GdlLiteral> body)
{
ConcurrentMap<List<GdlLiteral>, GdlRule> bucket = rulePool.get(head);
if(bucket == null)
bucket = addToPool(head, new ConcurrentHashMap<List<GdlLiteral>, GdlRule>(), rulePool);
GdlRule ret = bucket.get(body);
if(ret == null)
ret = addToPool(body, new GdlRule(head, body), bucket);
return ret;
}
public static GdlVariable getVariable(String name)
{
GdlVariable ret = variablePool.get(name);
if(ret == null)
ret = addToPool(name, new GdlVariable(name), variablePool);
return ret;
}
/**
* This method should only rarely be used. It takes a foreign GDL object
* (one that wasn't constructed through the GdlPool) and returns a version
* that lives in the GdlPool. Various parts of the prover infrastructure
* expect that all GDL objects live in the GdlPool, and so it's important
* that any foreign GDL objects created outside the GdlPool be immersed
* before being used. Since every GDL object should be created through the
* GdlPool, immerse should only need to be called on GDL that appears from
* outside sources: for example, being deserialized from a file.
*/
public static Gdl immerse(Gdl foreignGdl) {
if(foreignGdl instanceof GdlDistinct) {
return GdlPool.getDistinct((GdlTerm)immerse(((GdlDistinct) foreignGdl).getArg1()), (GdlTerm)immerse(((GdlDistinct) foreignGdl).getArg2()));
} else if(foreignGdl instanceof GdlNot) {
return GdlPool.getNot((GdlLiteral)immerse(((GdlNot) foreignGdl).getBody()));
} else if(foreignGdl instanceof GdlOr) {
GdlOr or = (GdlOr)foreignGdl;
List<GdlLiteral> rval = new ArrayList<GdlLiteral>();
for(int i=0; i<or.arity(); i++)
{
rval.add((GdlLiteral) immerse(or.get(i)));
}
return GdlPool.getOr(rval);
} else if(foreignGdl instanceof GdlProposition) {
return GdlPool.getProposition((GdlConstant)immerse(((GdlProposition) foreignGdl).getName()));
} else if(foreignGdl instanceof GdlRelation) {
GdlRelation rel = (GdlRelation)foreignGdl;
List<GdlTerm> rval = new ArrayList<GdlTerm>();
for(int i=0; i<rel.arity(); i++)
{
rval.add((GdlTerm) immerse(rel.get(i)));
}
return GdlPool.getRelation((GdlConstant)immerse(rel.getName()), rval);
} else if(foreignGdl instanceof GdlRule) {
GdlRule rule = (GdlRule)foreignGdl;
List<GdlLiteral> rval = new ArrayList<GdlLiteral>();
for(int i=0; i<rule.arity(); i++)
{
rval.add((GdlLiteral) immerse(rule.get(i)));
}
return GdlPool.getRule((GdlSentence) immerse(rule.getHead()), rval);
} else if(foreignGdl instanceof GdlConstant) {
return GdlPool.getConstant(((GdlConstant) foreignGdl).getValue());
} else if(foreignGdl instanceof GdlFunction) {
GdlFunction func = (GdlFunction)foreignGdl;
List<GdlTerm> rval = new ArrayList<GdlTerm>();
for(int i=0; i<func.arity(); i++)
{
rval.add((GdlTerm) immerse(func.get(i)));
}
return GdlPool.getFunction((GdlConstant) immerse(func.getName()), rval);
} else if(foreignGdl instanceof GdlVariable) {
return GdlPool.getVariable(((GdlVariable) foreignGdl).getName());
} else
throw new RuntimeException("Uh oh, gdl hierarchy must have been extended without updating this code.");
}
}