/* Author: Jeff Dalton * Updated: Sun Dec 2 23:41:32 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 Schema describes a possible action in a process being modelled. * There can also be schemas that describe the top-level of a process. */ public class Schema implements SchemaSymbols, NamedObject, Cloneable { public String name; // e.g. "develop coa quickly" /\/ public Symbol type; // e.g. process or action public LList pattern; // e.g. ("develop COA" ?coa) public Object action; // e.g. "devalop COA" public LList nodes = Lisp.NIL; public LList orderings = Lisp.NIL; public LList properties = Lisp.NIL; public LList effects = Lisp.NIL; public String comments = ""; public boolean isForEachCoa; // true iff pattern contains ?coa /\/ public Schema() { this.type = S_ACTION; } public Schema (String name, LList pattern) { this.name = name; this.type = S_ACTION; this.pattern = pattern; this.action = pattern.elementAt(0); this.isForEachCoa = (pattern.elementAt(1) == Q_COA); Debug.noteln("New blank schema " + Util.quote(name) + " for", pattern); } public Schema(LList def) { // def has the form (name type pattern . slot-alist) this.name = def.elementAt(0).toString(); this.type = (Symbol)def.elementAt(1); this.pattern = (LList)def.elementAt(2); this.action = pattern.elementAt(0); this.isForEachCoa = (pattern.elementAt(1) == Q_COA); Debug.noteln("New schema", Util.quote(name)); // Init schema fields that correspond to slots in the definition. for (LList slots = def.drop(3); slots != Lisp.NIL; slots = slots.cdr()) { Cons slot = (Cons)slots.car(); Symbol name = (Symbol)slot.car(); Object value = slot.cdr(); if (name == S_NODES) this.nodes = (LList)value; else if (name == S_ORDERINGS) this.orderings = (LList)value; else if (name == S_PROPERTIES) this.properties = (LList)value; else if (name == S_EFFECTS) this.effects = (LList)value; else Debug.warn("Illegal schema slot:" + slot); } checkConsistency(); } public String getName() { return name; } public void checkConsistency() { checkNodeNumbers(); } protected void checkNodeNumbers() { // Check that the nodes in the nodes clause are sequentally // numbered and start with 1. LList nodeSpecs; int place; for (nodeSpecs = nodes, place = 1; nodeSpecs != Lisp.NIL; nodeSpecs = nodeSpecs.cdr(), place++) { LList spec = (LList)nodeSpecs.car(); int number = ((Number)spec.car()).intValue(); if (number != place) { Debug.warn("Nodes not sequentially numbered from 1 " + "in schema for " + pattern); break; } } // Check that all orderings refer to node numbers that exist final int nodeMax = nodes.length(); orderings.walkTree(new Function1() { public Object funcall(Object nodeNumber) { int n = ((Number)nodeNumber).intValue(); if (n < 1 || n > nodeMax) Debug.warn("Ordering in schema for " + pattern + "refers to nonexistent node " + nodeNumber); return null; } }); } public Object getPropertyObject (String name) { for (LList ps = properties; ps != Lisp.NIL; ps = ps.cdr()) { Cons prop = (Cons)ps.car(); if (((String)prop.car()).equals(name)) { return prop.cdr().car(); } } return null; } public String getProperty(String name) { return (String)getPropertyObject(name); } public boolean isTrue(String name) { String value = getProperty(name); if (value == null || value.equals("false")) return false; else { Debug.assert(value.equals("true"), "not true, but not false"); return true; } } public Schema instantiate(MatchEnv env) { return instantiate(env, new Function1() { public Object funcall(Object name) { throw new RuntimeException("Unbound variable " + name); } }); } public Schema instantiate(final MatchEnv env, Function1 ifUnbound) { try { Schema s = (Schema)clone(); s.pattern = (LList)env.instantiateTree(s.pattern, ifUnbound); s.nodes = (LList)env.instantiateTree(s.nodes, ifUnbound); s.properties = (LList)env.instantiateTree(s.properties, ifUnbound); // /\/: Still have to supply ifUnbound for effects. s.effects = s.effects.mapcar(new Function1() { public Object funcall(Object effect) { return ((Effect)effect).instantiate(env); } }); return s; } catch (CloneNotSupportedException e) { Debug.noteException(e); return null; } } public Schema forCoa(Long coa) { LList callPattern = isForEachCoa ? Lisp.list(action, coa) : Lisp.list(action); MatchEnv e = SimpleMatcher.match(pattern, callPattern); Debug.assert(e != null, "Can't instantiate schema for coa", pattern); return instantiate(e); } public Object clone() throws CloneNotSupportedException { return super.clone(); } public LList toLList() { // for debugging Object[] form = new Object[] { name, type, pattern, Lisp.list(S_NODES, nodes), Lisp.list(S_ORDERINGS, orderings), Lisp.list(S_PROPERTIES, properties) }; return Seq.toLList(Seq.elements(form)); } public String toString() { return "Schema[" + type + " " + name + " expands " + pattern + "]"; } } // Issues: // * Property names should be either Keyword or Object, not String. // * String property values should not be the default or have any // special status. // * Should all methods that don't refer to the Schema be static? // * Should there be so many public instance variables?