/* Author: Jeff Dalton * Updated: Sat Jun 30 14:35:45 2001 by Jeff Dalton * Copyright: (c) 2000, AIAI, University of Edinburgh */ package ix.icore.domain; import java.util.*; import ix.util.*; import ix.util.lisp.*; import ix.util.match.*; /** * A SchemaTable contains Schemas that describe the possible actions * in a process. */ public class SchemaTable implements SchemaSymbols { protected LListCollector processSchemas = new LListCollector(); protected LListCollector allSchemas = new LListCollector(); protected Hashtable actionTable = new Hashtable(); protected Hashtable nameTable = new Hashtable(); public SchemaTable() { } public boolean isEmpty() { return allSchemas.isEmpty(); } public void clear() { processSchemas.clear(); allSchemas.clear(); actionTable.clear(); nameTable.clear(); } public void addSchema(Schema s) { nameTable.put(s.name, s); allSchemas.addElement(s); if (s.type == S_PROCESS) { processSchemas.addElement(s); } else { // Add the Schema to the table with the action string as the key LListCollector bucket = (LListCollector)actionTable.get(s.action); if (bucket == null) { bucket = new LListCollector(); actionTable.put(s.action, bucket); } bucket.addElement(s); } } public void deleteNamedSchema(String name) { deleteSchema(getNamedSchema(name)); } public void deleteSchema(Schema s) { nameTable.remove(s.name); allSchemas.deleteElement(s); if (s.type == S_PROCESS) { processSchemas.deleteElement(s); } else { LListCollector bucket = (LListCollector)actionTable.get(s.action); Debug.assert(bucket != null); Debug.assert(bucket.contents().find(s)); bucket.deleteElement(s); } } public LList getAllProcessSchemas() { return processSchemas.contents(); } public LList getMatchingProcessSchemas(LList pattern) { return getMatchingSchemas(pattern, processSchemas.contents()); } public LList getAllSchemas() { return allSchemas.contents(); } public Schema getNamedSchema(String name) { return (Schema)nameTable.get(name); } public LList getMatchingSchemas(LList pattern) { if (pattern instanceof Cons) { Object action = pattern.car(); return getMatchingSchemas(pattern, getSchemasWithAction(action)); } else { Debug.warn("Improper pattern " + pattern); return Lisp.NIL; } } public LList getMatchingSchemas(LList pattern, LList shortlist) { LListCollector result = new LListCollector(); for (Enumeration se = shortlist.elements(); se.hasMoreElements();) { Schema s = (Schema)se.nextElement(); MatchEnv e = SimpleMatcher.match(s.pattern, pattern); if (e != null) { result.addElement(s.instantiate(e)); } } return result.contents(); } public LList getSchemasWithAction(Object action) { LListCollector bucket = (LListCollector)actionTable.get(action); return bucket == null ? Lisp.NIL : bucket.contents(); } public Schema getActionSchema(Object action) { LListCollector bucket = (LListCollector)actionTable.get(action); Debug.assert(bucket != null, "No schemas for action", action); Debug.assert(bucket.length() == 1, "Not exactly one Schema for action", action); return (Schema)bucket.contents().car(); } public void checkSchemaReferences() { // Looks only at the first word of any pattern, to avoid having // to handle variables on both sides of the match. /\/ for (Enumeration se = allSchemas.elements(); se.hasMoreElements();) { Schema s = (Schema)se.nextElement(); for (Enumeration ne = s.nodes.elements(); ne.hasMoreElements();) { LList childSpec = (LList)ne.nextElement(); LList childPattern = (LList)childSpec.elementAt(1); Object childAction = childPattern.elementAt(0); if (actionTable.get(childAction) == null) { Debug.warn("Schema " + s.pattern + " refers to " + childPattern + " which does not exist."); } } } } public Schema getProcessSchema() { Debug.assert(processSchemas.length() == 1, "Must be exactly one process"); return (Schema)processSchemas.contents().car(); } public LList staticExpansionTree() { return staticExpansionTree(getProcessSchema()); } public LList staticExpansionTree(Schema s) { // Returns tree below s, in the form (action subtree) // with each action represented by the first word in its // expansion pattern. return s.nodes.mapcar(new Function1() { public Object funcall(Object nodeSpec) { LList spec = (LList)nodeSpec; LList childPattern = (LList)spec.elementAt(1); String childAction = (String)childPattern.elementAt(0); LList childSchemas = getSchemasWithAction(childAction); Debug.assert(childSchemas != Lisp.NIL, "Can't find a child schema for action", childAction); Debug.assert(childSchemas.length() == 1, "More than one child schema for action", childAction); Schema child = (Schema)childSchemas.car(); return Lisp.list(childAction, staticExpansionTree(child)); } }); } } // Issues: // * Process (task) schemas are stored separately. Is that right? // * And is LListCollector the right way to store them? // * Should getMatchingSchemas() return instantiated Schemas // or Schemas paired with MatchEnvs? // * Should patterns be LLists or Objects? // * staticExpansionTree() should throw something more specific // when there's more than 1 child schema. // * Should we support the "exactly one process" case or should // that have to be done in a subclass?