All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ggp.base.util.gdl.grammar.GdlPool Maven / Gradle / Ivy

The newest version!
package org.ggp.base.util.gdl.grammar;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.MapMaker;

/**
 * The GdlPool manages the creation of {@link Gdl} objects. It is the only way Gdl
 * objects may be created. It ensures that for each possible sentence, rule, or
 * fragment of GDL, only one corresponding Gdl object is created. This means Gdl
 * objects may be checked for equality with an instance-equality check (==) rather
 * than a more expensive recursive equality check.
 * 

* A long-lived game player may accumulate lots of objects in its pool. To remove * them, it may call {@link #drainPool()} in between games. Note that if this * method is called while references to Gdl objects other than keyword constants * are held elsewhere, bad things will happen. */ public final class GdlPool { private static final ConcurrentMap> distinctPool = new ConcurrentHashMap>(); private static final ConcurrentMap, GdlFunction>> functionPool = new ConcurrentHashMap, GdlFunction>>(); private static final ConcurrentMap notPool = new ConcurrentHashMap(); private static final ConcurrentMap, GdlOr> orPool = new ConcurrentHashMap, GdlOr>(); private static final ConcurrentMap propositionPool = new ConcurrentHashMap(); private static final ConcurrentMap, GdlRelation>> relationPool = new ConcurrentHashMap, GdlRelation>>(); private static final ConcurrentMap, GdlRule>> rulePool = new ConcurrentHashMap, GdlRule>>(); private static final ConcurrentMap variablePool = new ConcurrentHashMap(); private static final ConcurrentMap constantPool = new ConcurrentHashMap(); //Access to constantCases and variableCases should be synchronized using their monitor locks. private static final Map constantCases = new TreeMap(String.CASE_INSENSITIVE_ORDER); private static final Map variableCases = new TreeMap(String.CASE_INSENSITIVE_ORDER); // Controls whether we normalize the case of incoming constants and variables. public static volatile boolean caseSensitive = true; // Special keyword constants. These are never drained between games and are always // represented as lower-case so that they can easily be referred to internally and // so that all of the "true" objects are equal to each other in the Java == sense. // For example, attempting to create a GdlConstant "TRUE" will return the same constant // as if one had attempted to create the GdlConstant "true", regardless of whether the // game-specific constants are case-sensitive or not. These special keywords are never // sent over the network in PLAY requests and responses, so this should be safe. public static final ImmutableSet KEYWORDS = ImmutableSet.of( "init","true","next","role","does","goal","legal","terminal","base","input","_"); public static final GdlConstant BASE = getConstant("base"); public static final GdlConstant DOES = getConstant("does"); public static final GdlConstant GOAL = getConstant("goal"); public static final GdlConstant INIT = getConstant("init"); public static final GdlConstant INPUT = getConstant("input"); public static final GdlConstant LEGAL = getConstant("legal"); public static final GdlConstant NEXT = getConstant("next"); public static final GdlConstant ROLE = getConstant("role"); public static final GdlConstant TERMINAL = getConstant("terminal"); public static final GdlConstant TRUE = getConstant("true"); /** * Represents a single underscore ("_"). The underscore is not a GDL keyword, but * it's used by SentenceForms and is generally convenient for utility methods. */ public static final GdlConstant UNDERSCORE = getConstant("_"); private GdlPool() { // Not instantiable } /** * 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*, when there are no * references to Gdl objects (other than keyword constants) outside the * pool. */ public static void drainPool() { distinctPool.clear(); functionPool.clear(); notPool.clear(); orPool.clear(); propositionPool.clear(); relationPool.clear(); rulePool.clear(); variablePool.clear(); synchronized (variableCases) { variableCases.clear(); } // // When draining the pool between matches, we still need to preserve the keywords // // since there are global references to them. For example, the Prover state machine // // has a reference to the GdlConstant "true", and that reference must still point // // to the authoritative GdlConstant "true" after the pool is drained and another // // game has begun. As such, when draining the constant pool, these special keywords // // are set aside and returned to the pool after all of the other constants (which // // were game-specific) have been drained. // Map keywordConstants = new HashMap(); // for (String keyword : KEYWORDS) { // keywordConstants.put(keyword, GdlPool.getConstant(keyword)); // } // synchronized (constantCases) { // constantPool.clear(); // constantCases.clear(); // for (Map.Entry keywordEntry : keywordConstants.entrySet()) { // constantCases.put(keywordEntry.getKey(), keywordEntry.getKey()); // constantPool.put(keywordEntry.getKey(), keywordEntry.getValue()); // } // } } /** * 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 V addToPool(K key, V value, ConcurrentMap pool) { V prevValue = pool.putIfAbsent(key, value); if (prevValue == null) { return value; } else { return prevValue; } } public static GdlConstant getConstant(String value) { if (KEYWORDS.contains(value.toLowerCase())) { value = value.toLowerCase(); } if (!caseSensitive) { synchronized (constantCases) { if (constantCases.containsKey(value)) { value = constantCases.get(value); } else { constantCases.put(value, value); } } } GdlConstant ret = constantPool.get(value); if(ret == null) ret = addToPool(value, new GdlConstant(value), constantPool); return ret; } public static GdlVariable getVariable(String name) { if (!caseSensitive) { synchronized (variableCases) { if (variableCases.containsKey(name)) { name = variableCases.get(name); } else { variableCases.put(name, name); } } } GdlVariable ret = variablePool.get(name); if(ret == null) ret = addToPool(name, new GdlVariable(name), variablePool); return ret; } public static GdlDistinct getDistinct(GdlTerm arg1, GdlTerm arg2) { ConcurrentMap bucket = distinctPool.get(arg1); if(bucket == null) bucket = addToPool(arg1, new ConcurrentHashMap(), 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 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 body) { ConcurrentMap, GdlFunction> bucket = functionPool.get(name); if(bucket == null) { ConcurrentMap, GdlFunction> newMap = new MapMaker().weakValues().makeMap(); bucket = addToPool(name, newMap, functionPool); } GdlFunction ret = bucket.get(body); if(ret == null) { ImmutableList immutableBody = ImmutableList.copyOf(body); ret = addToPool(immutableBody, new GdlFunction(name, immutableBody), 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 disjuncts) { GdlOr ret = orPool.get(disjuncts); if(ret == null) { ImmutableList immutableDisjuncts = ImmutableList.copyOf(disjuncts); ret = addToPool(immutableDisjuncts, new GdlOr(immutableDisjuncts), 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 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 body) { ConcurrentMap, GdlRelation> bucket = relationPool.get(name); if(bucket == null) { ConcurrentMap, GdlRelation> newMap = new MapMaker().weakValues().makeMap(); bucket = addToPool(name, newMap, relationPool); } GdlRelation ret = bucket.get(body); if(ret == null) { ImmutableList immutableBody = ImmutableList.copyOf(body); ret = addToPool(immutableBody, new GdlRelation(name, immutableBody), bucket); } return ret; } public static GdlRule getRule(GdlSentence head) { List 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 body) { ConcurrentMap, GdlRule> bucket = rulePool.get(head); if (bucket == null) { ConcurrentMap, GdlRule> newMap = new MapMaker().weakValues().makeMap(); bucket = addToPool(head, newMap, rulePool); } GdlRule ret = bucket.get(body); if(ret == null) { ImmutableList immutableBody = ImmutableList.copyOf(body); ret = addToPool(immutableBody, new GdlRule(head, immutableBody), bucket); } 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. */ @SuppressWarnings("unchecked") //Cast to T for convenience; it works here public static T immerse(T foreignGdl) { if(foreignGdl instanceof GdlDistinct) { return (T) GdlPool.getDistinct(immerse(((GdlDistinct) foreignGdl).getArg1()), immerse(((GdlDistinct) foreignGdl).getArg2())); } else if(foreignGdl instanceof GdlNot) { return (T) GdlPool.getNot(immerse(((GdlNot) foreignGdl).getBody())); } else if(foreignGdl instanceof GdlOr) { GdlOr or = (GdlOr)foreignGdl; List rval = new ArrayList(); for(int i=0; i rval = new ArrayList(); for(int i=0; i rval = new ArrayList(); for(int i=0; i rval = new ArrayList(); for(int i=0; i





© 2015 - 2024 Weber Informatics LLC | Privacy Policy