/* $Id: Act.java,v 1.147 2006/01/04 23:17:37 rich Exp $
Copyright (c) 1998-2002 Mitsubishi Electric Research Laboratories, Inc.
Use of this file and the system it is part of is constrained by
the file COPYRIGHT in the root directory of this system.
*/
package collagen.plan;
import java.util.*;
import java.lang.reflect.*;
import collagen.*;
import collagen.infer.*;
import collagen.infer.equal.*;
import collagen.infer.tms.*;
import collagen.discourse.*;
import collagen.beans.*;
import collagen.beans.ApplicationAdapter.NoAdapterException;
import collagen.lang.lib.*;
import collagen.lang.*;
import com.merl.util.*;
import collagen.infer.gloss.*;
import collagen.infer.gloss.Slot.*;
public interface Act extends Term, Cloneable {
boolean isMentionable ();
boolean isRemote ();
boolean isUnknown ();
boolean isTop ();
/**
* If requiresExplanation
return false, then do not start an
* interruption or new toplevel goal to explain this act, but simply attach
* it to current goal.
**/
boolean requiresExplanation ();
/**
* Tests whether this toplevel goal can be introduced by recognition.
* Default true.
*/
boolean isRecognizableTop();
boolean isShowable ();
boolean isTeachable (Plan plan);
boolean isRetriable (Plan plan);
boolean isOptionalStep (Plan plan);
boolean isRepeatableStep (Plan plan);
boolean isUnknownRetryStep (Plan plan);
/**
* If true, then this is goal is stopped whenever it is popped
* off the focus stack.
*
* @see #isInterruptible(Plan, Act)
*/
boolean isAbandonable (Plan plan);
/**
* If false, then instead of interrupting this goal with a segment with given
* purpose, pop this goal off the stack. Furthermore, note that all the
* segments on the stack are potentially interrupted by a given
* interruption. Default true.
*
* @see #isAbandonable(Plan)
*/
boolean isInterruptible (Plan plan, Act interruption);
boolean isStarter ();
Boolean isApplicable ();
Boolean isAchieved ();
Boolean isApplicable (Situation.Literal when);
Boolean isAchieved (Situation.Literal when);
/**
* If this method returns true, then the given plan is not live.
*
* @param plan is a plan whose purpose is this act
*/
boolean isBlocked (Plan plan, boolean nearMiss);
/**
* Tests whether this non-primitive act can be started as a goal, or this
* primitive act performed by user or agent, without asking permission. Note
* that if this method returns true, it does not mean that this
* act should be undertaken or executed now (or ever); it just means that the
* given actor does not need to ask permission first. Also, if the plan for
* this action is optional, then it is still unknown whether it should be executed.
*
* @param who
* Actor.USER
or Actor.AGENT
only
* @param plan
* plan whose purpose is this act, or null
*
* @see Act.Base#isAuthorized(Actor.Literal,Plan) for defaults
*/
boolean isAuthorized (Actor.Literal who, Plan plan);
/**
* Tests whether this action has been accepted for execution or undertaking
* as a goal by given actor or any actor (if who argument is null).
*
* @see Act.Base#isAccepted(Actor.Literal) for semantics
*/
boolean isAccepted (Actor.Literal who);
boolean isAccepted ();
/**
* Tests whether this action has been rejected for execution or undertaking
* as a goal by given actor or any actor (if who argument is null).
*
* @see Act.Base#isAccepted(Actor.Literal) for semantics
*/
boolean isRejected (Actor.Literal who);
boolean isRejected ();
/** used for maintaining user model **/
java.io.Serializable getTrackingKey ();
UserModel.Index getTrackingIndex (Plan plan, boolean isRepeat);
/**
* Returns an adjustment to be added to priority of
* agenda item generated for instance of this act using given
* plan. (Default 0)
*
* @param plan may be null
*/
float getPriority (Plan plan);
Proposition getPreconditions ();
Proposition getPostconditions ();
Proposition[] getPreconditionsArray ();
Proposition[] getPostconditionsArray ();
/**
* Returns the proposition associated with this act, or null. The semantics
* of this proposition depends on the type of act (e.g., see {@link
* Propose}).
*
* @see #setProposition
* @see Propositional
**/
Proposition getProposition ();
/**
* Set the proposition associated with this act, or null. The semantics of
* this proposition depends on the type of act (e.g., see {@link Propose}).
*
* @see #getProposition
* @see Propositional
**/
void setProposition (Proposition proposition);
Actor.Who getWho ();
Actor.Literal getWhoValue ();
Recipe.Name getHow ();
Recipe.Name.Literal getHowValue ();
boolean matchesIgnoringResults (Act act);
boolean isSubsumedBy (Act act);
Proposition[] /* or null */ contributes (Plan plan, boolean nearMiss);
Proposition[] /* or null */ matches (Plan plan, boolean nearMiss);
boolean contributes (Class actType, boolean nearMiss);
Class[] getContributes ();
/**
* This method is called whenever given parent plan acquires given child plan
* (whose purpose is this act) as part of discourse interpretation or recipe
* instantiation. It is the responsibility of this method to call
* addSubplan
.
*/
Elaboration attach (Plan child, Plan parent, Proposition[] bindings);
boolean contributesValues (Plan plan, boolean nearMiss);
boolean contributesValues (Plan plan, boolean nearMiss, boolean includeWho);
/**
* Tests whether this act contributes only to live plans in non-near-miss
* recognition. Note that even if this method returns false, non-near-miss
* plan recognition (see expand()
in Library
) does
* not interpolate from nonLive plans, which means only direct
* contribution to nonLive plans is considered.,
**/
boolean contributesOnlyLive ();
/**
* This can be overridden for acts without recipes for which there is special
* knowledge (e.g., Propose.What for structures). This is only called if
* plan.isPlanned() returns false.
**/
boolean isComplete (Plan plan, boolean nearMiss);
boolean isPathFrom (Library.Path library, Class c, boolean nearMiss);
boolean isLivePathFrom (Library.Path library, Class c, boolean nearMiss);
int getParameterCount ();
Term getParameter (int i);
Term[] getParameters ();
Term[] getParameterValues ();
boolean hasAllParameterValues ();
boolean hasAssumedParameterValue ();
Term.Slot[] getParameterOps ();
void setParameters (Term[] parameters);
Term.Slot getParameterSlot (int i);
int getResultCount ();
Term getResult (int i);
Term[] getResults ();
Term[] getResultValues ();
Term.Slot[] getResultOps ();
void setResults (Term[] parameters);
Term.Slot getResultSlot (int i);
boolean isJoint ();
boolean isAgent ();
boolean isUser ();
boolean isExternal ();
boolean isSelf (Actor self);
boolean isOther (Actor self);
boolean isBargeIn (Actor actor);
Act newAct (Term[] parameters, Term[] results, Term[] properties);
/**
* Returns this act, a copy, or null, depending on the relationship
* between the who value of this act and the given who:
*
Actor.USER
or Actor.AGENT
only
* @param plan
* purpose of this plan may be this act, the plan this act
* contributes to, or null
* @see Act.Base#isFireable(Actor.Literal,Plan) for defaults
*/
boolean isFireable (Actor.Literal who, Plan plan);
Situation getWhen ();
Situation.Literal getWhenValue ();
boolean occurred ();
/* DESIGN NOTE: For convenience, application state momentos for both
before and after each primitive act is stored. See ApplicationAdapter
(performance) and Discourse (communication) for how these are computed.
Note: These are defined here instead of in Occurrence, because occurrences
are not created until discourse queue, which is too late for momentos
TODO avoid allocating these Term.Slots for applications without
momentos ?
*/
Momento getBefore ();
Momento getAfter ();
void setBefore (Momento momento);
void setAfter (Momento momento);
}
public interface Occurrence extends Primitive, Term.Literal { // Act.Occurrence
long getTimestamp ();
/** This method is called to determine if a RecoverFrom subplan needs to be
added to Plan
when this act is a near-miss or a
deviation.
@return true if a RecoverFrom must be added; false if unsure
**/
boolean requiresRecovery (Plan plan);
}
// following handy for in acts with nested purposes
// see ContributesPurpose and Propose.Purpose.
// (Note: didn't make this a subclass because don't want intermediate act
// type (even if abstract)
public interface Purpose extends Act { // Act.Purpose
Act.Quoted getPurpose ();
Act getPurposeValue ();
Purpose newPurposeValue (Act purpose);
}
// Act.Quoted
public static class Quoted extends Wrapper implements Term.Quoted {
public Class getVariableType () { return Act.Quoted.class; }
public Quoted () {} // for acts
// Default implementation of getLocation for Quoted acts refers to the
// wrapped object.
public Object getLocation () {
return Act.Base.getLocation((Act) getUnquotedValue());
}
public static Act.Quoted.Literal make (Act act) {
return Literal.makeLiteral(act);
}
// Act.Quoted.Literal
public final static class Literal extends Act.Quoted
implements Term.Quoted.Literal {
private static KeyHashtable table =
new KeyHashtable(Infer.QUOTEDACTLITERAL_LENGTH,
Infer.QUOTEDACTLITERAL_LOAD);
private static int hash (Act act) {
return act.hashCode() & 0x7FFFFFFF;
}
private final Act act;
private KeyHashtable.Entry nextEntry;
public int keyHashCode () { return hash(act); }
public KeyHashtable.Entry getNextEntry() { return nextEntry; }
public void setNextEntry (KeyHashtable.Entry next) { nextEntry = next; }
private static Act.Quoted.Literal makeLiteral (Act act) {
if ( act == null )
throw new IllegalArgumentException("Cannot quote null act");
int index = hash(act) % table.getLength();
for (Act.Quoted.Literal entry = (Act.Quoted.Literal) table.get(index);
entry != null;
entry = (Act.Quoted.Literal) entry.getNextEntry() )
// always use reference equality for objects
if ( entry.act == act ) return entry;
return new Act.Quoted.Literal(act, index);
}
private Literal (Act act, int index) {
this.act = act;
table.add(this, index);
Infer.update(this);
}
// public methods
public String toJavaString () {
StringBuffer buffer = new StringBuffer("Act.Quoted.make(");
buffer.append(act.toJavaString());
return buffer.append(')').toString();
}
public Object toObject () { return unquote(); }
public Term unquote () { return act; }
public Act unwrap () { return act; }
}
}
// tags inserted by preprocessor
// used for recipe steps
public interface Step extends Act, collagen.plan.Step, Term.Slot {}
public interface Parameter extends Term.Slot {}
public interface Result extends Term.Slot {}
public class Base extends Act_Builtin { // Act.Base
public Base () { this(null, null, null); }
public Base (Actor who) { this(new Term[] {who}); }
public Base (Term[] properties) { this(null, null, properties); }
protected Base (Term[] parameters, Term[] results, Term[] properties) {
super(properties);
setParameters(parameters); // must be before conditions
setResults(results); // ditto
// must be after slots initialized
setConstraintsArray(getConstraintsArray(), "constraints defn");
try {
setPreconditionsArray(getPreconditionsArray(), "preconditions defn");
setPostconditionsArray(getPostconditionsArray(), "postconditions defn");
} catch (NoAdapterException e) {}
// note using getCanonical instead of getValue below so as not to
// invokes executeQueue before rest of initialization done
if ( properties != null && ( this instanceof Act.Primitive )
&& Actor.isJoint((Actor.Who) properties[WHO]) )
Logging.LOG.warning("Primitive act may not be joint: {0}", this);
Infer.update(this);
if ( this instanceof Performance || this instanceof Utterance ) {
Term who = getWho();
if ( !who.hasValue() )
new Axiom(new Proposition[] { Equality.make(who, Actor.AGENT),
Equality.make(who, Actor.USER) },
"Act.Primitive who");
}
}
private static int count;
{ count++; }
/**
* Return total number of acts created (for performance monitoring).
*/
public static int getCount () { return count; }
public void simulate () {} // default for simulation without application
/* DESIGN NOTE: Actor for non-primitive acts
There are four possible values for the actor(getWhoValue) of a
non-primitive act:
(1) Actor.JOINT: e.g,. "Let's do foo (together)."
(2) self: e.g., "I'm going to do foo."
(3) other: e.g., "Please do foo."
(4) null: e.g., "Foo should be done."
In case (1), both collaborators have mutual beliefs about all aspects
of the goal (e.g., the recipe used), and are both committed to its
successful completion.
In cases (2) and (3), one actor is responsible for the task, but that
does _not_ mean that every subplan for the goal will be done by that
actor. The actor can delegate parts of the task and get information
(knowledge preconditions) about how to do parts of the task from the
other actor.
Case (4) is neutral between the other cases, i.e., the choice may later
be made for one of the other cases.
TODO: Some current code uses null for actor where it should
more properly use Actor.JOINT.
*/
// for convenience and optimization (see design note above)
public Actor.Literal getWhoValue () {
Term[] properties = getPropertyArray();
if ( properties == null || properties[WHO] == null )
return null;
return (Actor.Literal) getWho().getValue();
}
public Recipe.Name getHow () { // convenience
return How.make(this);
}
public Recipe.Name.Literal getHowValue () { // convenience and optimization
return How.getValue(this);
}
// convenience method
public final static Act.Quoted quote (Act act) {
return act==null ? new Act.Quoted() : Act.Quoted.make(act);
}
public final static Predication.Quoted quote (Predication predication) {
return predication==null ? new Predication.Quoted()
: Predication.Quoted.make(predication);
}
// following for use with instance of Act.Purpose
public Act /* or null */ getPurposeValue () {
return this instanceof Purpose ?
(Act) getUnquotedValue(((Purpose) this).getPurpose())
: null;
}
public Purpose newPurposeValue (Act purpose) {
if ( this instanceof Purpose )
return (Purpose)
((Purpose) this).newParameterValue(
0,
(Term.Literal) quote(purpose));
else throw new UnsupportedOperationException(
purpose+" is not an Act.Purpose");
}
protected static Proposition[] purposeMatches (Proposition[] bindings,
Purpose act1, Term act2,
boolean hypothetical) {
return bindings != null ? bindings :
( act2 instanceof Purpose && act1.canBind(act2) ) ?
purposeMatches(act1, (Purpose) act2, hypothetical) : null;
}
protected static Proposition[] purposeMatches (Purpose act1, Purpose act2,
boolean hypothetical) {
Proposition[] whoWhenbindings =
((Act.Base) act1).whoWhenMatches(act2, hypothetical);
if ( whoWhenbindings == null ) return null;
Term purpose1 = act1.getPurposeValue();
Term purpose2 = act2.getPurposeValue();
if ( purpose1 == null || purpose2 == null ) return whoWhenbindings;
Proposition[] purposeBindings = purpose1.matches(purpose2, hypothetical);
return purposeBindings == null ? null
: Statement.add(purposeBindings, whoWhenbindings);
}
public static boolean purposeContributes (Purpose act,
Class actType, boolean nearMiss) {
Act purpose = act.getPurposeValue();
// unbound purpose binds to any act
return purpose == null ||
( purpose.getContributes() == null ?
purpose.contributes(actType, nearMiss)
: purpose.getType() == actType ); // avoid ambiguity
}
public static boolean purposeIsPathFrom (Purpose act, Library.Path library,
Class c, boolean nearMiss) {
Act purpose = act.getPurposeValue();
// unbound purpose binds to any act
return purpose == null ? true : purpose.isPathFrom(library, c, nearMiss);
}
public static boolean purposeIsLivePathFrom (Purpose act, Library.Path library,
Class c, boolean nearMiss) {
Act purpose = act.getPurposeValue();
// unbound purpose binds to any act
return purpose == null ? true : purpose.isLivePathFrom(library, c, nearMiss);
}
public static Proposition[] purposeContributes (Proposition[] bindings, Purpose act,
Plan plan, boolean nearMiss) {
return bindings != null ? bindings :
purposeContributes(act, plan, false, nearMiss);
}
protected static Proposition[] /* or null */ purposeContributes (Purpose act,
Plan plan, boolean clone, boolean nearMiss) {
return actContributes(act, act.getPurpose(), plan, clone, nearMiss);
}
public static Gloss purposeGloss (English l, Purpose act, String prefix,
String postfix, boolean gerundize) {
Act purpose = act.getPurposeValue();
Gloss g = l.renderDispatch(act, purpose);
if ( g != null ) return g;
g = l.assembleParseStringList(
act,
l.tokenizeParseString(l.translate(prefix)));
if ( purpose == null ) l.spTranslate(g, "collagen.noun.something");
else g.append(' ').append(l.renderContext(purpose, false, gerundize, false));
g.append(postfix);
return g;
}
protected static Proposition[] /* or null */ actContributes (
Purpose act, Act.Quoted quoted,
Plan plan, boolean clone, boolean nearMiss) {
Act purpose = (Act)getUnquotedValue(quoted);
if ( purpose == null )
// unbound purpose binds to any act
return Equality.newBinding(
quoted,
Act.Quoted.make(plan.getPurpose()));
if ( clone )
purpose = (Act) purpose.clone();
Class[] contributes = act.getContributes();
if (contributes!=null)
for (int i=contributes.length ; i-- > 0 ; )
if (plan.getType() == contributes[i]) { // use isType?
return purpose.matches(plan, nearMiss);
}
return purpose.contributes(plan, nearMiss);
}
/** @param verb - will be translated **/
public static Gloss glossPurpose (English l, Act act, String verb,
boolean gerundize) {
return glossPurpose(l, act, verb, gerundize, true);
}
/** @param verb - will be translated **/
public static Gloss glossPurpose (English l, Act act, String verb,
boolean gerundize, boolean prefix) {
return glossPurpose(
l, act,
act instanceof Purpose ? ((Purpose) act).getPurpose() : null,
verb, gerundize, prefix);
}
/** @param verb - will be translated **/
public static Gloss glossPurpose (English l, Act act,
Act.Quoted quotedPurpose, String verb,
boolean gerundize, boolean prefix) {
Act purpose = (Act) getUnquotedValue(quotedPurpose);
Actor who = purpose == null ? Actor.JOINT : purpose.getWhoValue();
if ( who == null )
who = purpose instanceof Act.Primitive ?
(Actor) purpose.getWho() : Actor.JOINT;
return glossPurpose(l, act, who, quotedPurpose, verb, gerundize, prefix);
}
// used by tutoring code
/** @param verb - will be translated **/
public static Gloss glossPurpose (English l, Act act,
Actor who, Act.Quoted quotedPurpose,
String verb, boolean gerundize, boolean prefix) {
Act purpose = (Act) getUnquotedValue(quotedPurpose);
Gloss g = l.renderDispatch(act, purpose);
if ( g != null ) return g;
g = new Gloss();
g.append(l.renderSubject(who, true, act.getWhoValue()));
if ( prefix ) g.append(' ').append(l.translate(verb));
g.append(' ');
if ( purpose == null ) {
String something = l.translate("collagen.verb.something");
if ( gerundize ) something = l.gerundize(something);
g.append(something);
} else if ( !l.isInCreation() && purpose.isUnknown() ) {
String interrupt = l.translate("collagen.verb.interruption");
if ( gerundize ) interrupt = l.gerundize(interrupt);
g.append(interrupt);
} else g.append(l.renderContext(quotedPurpose, false, gerundize, false));
if ( !prefix ) g.append(' ').append(l.translate(verb));
return l.prependSubjectAndVerb(l.quotationizeIf(g, act),
l.translate("collagen.verb.utterance"),
act);
}
/**
* Temporary placeholder until adapter, i.e., world object, is explicit
* argument to pre- and postconditions.
* * TODO isObserved() should also take world object argument */ protected static ApplicationAdapter getAdapter () { return Collagen.getCollagen().getAdapter(); } protected static boolean hasAdapter () { return Collagen.getCollagen().hasAdapter(); } // for use in recursive definitions of getLocation() protected static Object getLocation (Term term) { return term == null ? null : hasAdapter() ? getAdapter().getLocation(term) : term.getLocation(); } // boolean properties of acts public boolean hasAllParameterValues () { for (int i = getParameterCount(); i-- > 0;) if ( !getParameter(i).hasValue() ) return false; return true; } /** *
* | should(a) | *!should(a) | *unknown | *
---|---|---|---|
a.who=user | *agent rejected user accepted |
* agent rejected user rejected |
* agent rejected   |
*
a.who!=user | *agent accepted user rejected |
* agent rejected user rejected |
*   user rejected |
*
unknown | *agent accepted user accepted |
* agent rejected user rejected |
*   | *
Only defined for {@link Act.Occurrence}. **/ public boolean requiresRecovery (Plan plan) { return false; } /** This is a sample of a requiresRecovery method that sometimes returns true. It is used by collagen.plan.Testing and some test.tutor acts.
The important thing to note is that it only returns true if the act type of the error and the act type of the plan argument come from the same package. This prevents adding recover froms to core collagen acts like SessionRoot, RecoverFroms, TutorSessionRecipe, DoExercise, etc.
**/ protected boolean samePackageRecovery (Plan plan) { Act purpose = plan.getPurpose(); if (purpose.isType(Show.How.class)) purpose = ((Purpose) purpose).getPurposeValue(); return purpose.getType().getPackage() == getType().getPackage(); } // builtin properties // public static final variables public static final int WHO = 0; // **WARNING** if PROPERTY is changed, make sure you change corresponding // value in Coll.Act.printLiteral() and change number of nulls in Base // constructor and in act constructors in Lang.coll and Builtin.coll // must be defined here to override Variable.PROPERTY public static final int PROPERTY = 1; // first useable property // parameters (optional) public static final Term.Slot[] PARAMETERS = {}; public Term.Slot[] getParameterOps () { return PARAMETERS; } public int getParameterCount () { return getParameterOps().length; } // following two methods are overridden in classes with parameters // (to save instance variable when not needed) protected Term[] getParameterArray () { return null; } protected void setParameterArray (Term[] parameters) { Logging.LOG.warning("{0} does not have any parameters to set", this); } public void setParameters (Term[] parameters) { if ( parameters != null ) { if ( getParameterArray() != null ) Logging.LOG.warning("Attempting to reset parameters of existing act"); if ( parameters.length != getParameterCount() ) Logging.LOG.warning("Wrong length parameter array for {0}: {1}", this, parameters); // should do some type checking also for (int i = parameters.length; i-- > 0;) { Term value = parameters[i]; parameters[i] = getParameterOps()[i].newSlot(this); if ( value != null ) Equality.newAxiom(parameters[i], value, "parameter construction"); } setParameterArray(parameters); } Infer.update(this); } public Term getParameter (int i) { Term[] parameters = getParameterArray(); if ( parameters == null ) setParameterArray(parameters = new Term[getParameterCount()]); if ( parameters[i] == null ) parameters[i] = getParameterOps()[i].newSlot(this); return parameters[i]; } public Term.Slot getParameterSlot (int i) { // see Probe Term parameter = getParameter(i); return parameter instanceof Term.Slot ? (Term.Slot) parameter : getParameterOps()[i].newSlot(this); } public Term[] getParameters () { for (int i = getParameterCount(); i-- > 0;) getParameter(i); return getParameterArray(); } public Term[] getParameterValues () { int arity = getParameterCount(); if ( arity == 0 ) return null; // array more useful as Term than Term.Literal Term[] values = new Term[arity]; for (int i = arity; i-- > 0;) values[i] = getParameter(i).getValue(); return values; } // results (optional) public static final Term.Slot[] RESULTS = {}; public Term.Slot[] getResultOps () { return RESULTS; } public int getResultCount () { return getResultOps().length; } // following two methods are overridden in classes with results // (to save instance variable when not needed) protected Term[] getResultArray () { return null; } protected void setResultArray (Term[] results) { Logging.LOG.warning("{0} does not have any results to set", this); } public void setResults (Term[] results) { if ( results != null ) { if ( getResultArray() != null ) Logging.LOG.warning("Attempting to reset results of existing act"); if ( results.length != getResultCount() ) Logging.LOG.warning("Wrong length parameter array"); // should do some type checking also for (int i = results.length; i-- > 0;) { Term value = results[i]; results[i] = getResultOps()[i].newSlot(this); if ( value != null ) Equality.newAxiom(results[i], value, "result construction"); } setResultArray(results); } Infer.update(this); } public Term getResult (int i) { Term[] results = getResultArray(); if ( results == null ) setResultArray(results = new Term[getResultCount()]); if ( results[i] == null ) results[i] = getResultOps()[i].newSlot(this); return results[i]; } public Term.Slot getResultSlot (int i) { // see Probe Term parameter = getResult(i); return parameter instanceof Term.Slot ? (Term.Slot) parameter : getResultOps()[i].newSlot(this); } public Term[] getResults () { for (int i = getResultCount(); i-- > 0;) getResult(i); return getResultArray(); } public Term[] getResultValues () { int arity = getResultCount(); if ( arity == 0 ) return null; // array more useful as Term than Term.Literal Term[] values = new Term[arity]; for (int i = arity; i-- > 0;) values[i] = getResult(i).getValue(); return values; } // Slot caching for glossing (see collagen.infer.gloss.Slot) public static void initSlotCache (Class cls, Cache cache) { Mapping[] properties = Cache.getSlotArray( (Term.Slot[]) Utils.getStaticField(cls, Cache.PROPERTY), propertySlots); Mapping[] parameters = Cache.getSlotArray( (Term.Slot[]) Utils.getStaticField(cls, Cache.PARAMETER), parameterSlots); Mapping[] results = Cache.getSlotArray( (Term.Slot[]) Utils.getStaticField(cls, Cache.RESULT), resultSlots); cache.initArrays(properties.length+parameters.length +results.length-Act.Base.PROPERTY+1); cache.slots[0] = properties[WHO]; cache.types[0] = cache.slots[0].getPrototype().getType(); int i=1; i=cache.fillArrays(i, 0, parameters); i=cache.fillArrays(i, 0, results); cache.fillArrays(i, Act.Base.PROPERTY, properties); } // must be public public static class ParameterMapping extends Cache.MappingDirect { public Term map (Object o) { return (o instanceof Act) ? ((Act) o).getParameter(idx) : null; } public String getKey () { return Cache.PARAMETER; } } // must be public public static class ResultMapping extends Cache.MappingDirect { public Term map (Object o) { return (o instanceof Act) ? ((Act) o).getResult(idx) : null; } public String getKey () { return Cache.RESULT; } } private final static Cache.SlotMappingMaker parameterSlots = new Cache.SlotMappingMaker(ParameterMapping.class); private final static Cache.SlotMappingMaker resultSlots = new Cache.SlotMappingMaker(ResultMapping.class); // other public methods public boolean hasSlot (Term.Slot slot) { if ( super.hasSlot(slot) ) return true; Term[] parameters = getParameterArray(); if ( parameters == null ) return false; for (int i = parameters.length; i-- > 0;) { Term parameter = parameters[i]; if ( parameter == null ) continue; if ( slot == parameter || parameter.hasSlot(slot) ) return true; } Term[] results = getResultArray(); if ( results == null ) return false; for (int i = results.length; i-- > 0;) { Term result = results[i]; if ( result == null ) continue; if ( slot == result || result.hasSlot(slot) ) return true; } return false; } // BUG: this only finds first slot that matches // (and there is no way of getting rest!) public Term.Slot getMatchingSlot (Term term) { Term.Slot slot = super.getMatchingSlot(term); if ( slot != null ) return slot; slot = getMatchingParameter(term); if ( slot != null ) return slot; slot = getMatchingResult(term); if ( slot != null ) return slot; return null; } protected Term.Slot getMatchingParameter (Term term) { int length = getParameterCount(); for (int i = 0; i < length; i++ ) { // preserve order Term.Slot slot = getParameterSlot(i); // note recursion if ( slot.matches(term, true) != null || (slot = slot.getMatchingSlot(term)) != null ) return slot; } return null; } protected Term.Slot getMatchingResult (Term term) { int length = getResultCount(); for (int i = 0; i < length; i++ ) { // preserve order Term.Slot slot = getResultSlot(i); // note recursion if ( slot.matches(term, true) != null || (slot = slot.getMatchingSlot(term)) != null ) return slot; } return null; } public boolean matchesIgnoringResults (Act act) { if ( act == null ) return true; // so don't have to redefine for Propose.Should, etc. if ( getResultCount() == 0 ) return matches(act, true) != null; // not checking properties if ( !( this.canBind(act) && this.getWho().matches(act.getWho(), true) != null )) return false; return slotsMatch(getParameters(), act.getParameters(), null, true); } public boolean isSubsumedBy (Act act) { // only for acts of same type if (act == null || getType() != act.getType() ) return false; Proposition equality = Equality.make(this, act); if ( !equality.isConsistent(Boolean.TRUE, true) ) return false; // must match int count; try { equality.setTrue("isSubsumedBy"); count = countValues(); } finally { equality.retract(); } return countValues() == count; // sic } // following two methods used in Lang.coll protected Proposition[] whoWhenMatches (Act act, boolean hypothetical) { Proposition[] whoBindings = this.getWho().matches(act.getWho(), hypothetical); if ( whoBindings == null || !(this instanceof Act.Primitive) || !(act instanceof Act.Primitive) ) return whoBindings; Proposition[] whenBindings = ((Act.Primitive) this).getWhen() .matches(((Act.Primitive) act).getWhen(), hypothetical); return whenBindings == null ? null : Statement.add(whoBindings, whenBindings); } /** * Note that clone uses {@link #copySlotValues}. */ public Object clone () { Act target = newAct(null, null , null); copySlotValues((Base) target); return target; } /** * Note that values of target depend on values of this act (unlike * {@link #newAct(Term[],Term[],Term[])} which makes values axioms). * Values copied recursively. */ public void copySlotValues (Base target) { List visited = new ArrayList(); copySlotValues((Variable) target, visited); for (int i = getParameterCount(); i-- > 0;) { Term.Slot op = getParameterOps()[i]; // avoid circularities if ( visited.contains(op) ) continue; visited.add(op); Term parameter = getParameter(i); Term targetParameter = target.getParameter(i); Term.Literal value = parameter.getValue(); if ( value == null ) ((Variable) parameter).copySlotValues(targetParameter, visited); else Axiom.implies(Equality.make(parameter, value), Equality.make(targetParameter, value), "copySlotValues"); } for (int i = getResultCount(); i-- > 0;) { Term.Slot op = getResultOps()[i]; // avoid circularities if ( visited.contains(op) ) continue; visited.add(op); Term result = getResult(i); Term targetResult = target.getResult(i); Term.Literal value = result.getValue(); if ( value == null ) ((Variable) result).copySlotValues(targetResult, visited); else Axiom.implies(Equality.make(result, value), Equality.make(targetResult, value), "copySlotValues"); } } private final Class[] constructor = new Class[] { Term[].class, Term[].class, Term[].class }; public Act newAct (Term[] parameters, Term[] results, Term[] properties) { Act act = (Act) Utils.newInstance( getClass(), constructor, new Object[] { parameters, results, properties }); newAct(act); return act; } // for copying extra information to new act (see Recipe steps) protected void newAct (Act act) { Language language = Glosser.getDefaultLanguage(); if ( language != null ) { Object object = language.map(this); if ( object != null ) language.setGloss( act, object instanceof Gloss ? new Gloss((Gloss) object, this, act) : object); } ((Act.Base) act).optionalStep = optionalStep; ((Act.Base) act).repeatableStep = repeatableStep; ((Act.Base) act).unknownRetryStep = unknownRetryStep; // special case for Propose.When if ( act instanceof Occurrence ) { Term[] parameters = getParameters(); if ( parameters != null ) { Term when = ((Act.Primitive) this).getWhen(); for (int i = parameters.length; i-- > 0;) if ( parameters[i].isEqual(when) ) act.getParameters()[i].bind(((Occurrence) act).getWhen(), "construction"); } } } public Act newWhoValue (Actor.Literal who) { return newWhoValue(who, false); } public Act newWhoValue (Actor.Literal who, boolean assumption) { // note coding to support multiple users (see Actor) if ( getWhoValue() == who ) return this; if ( Actor.isJoint(who) && this instanceof Act.Primitive ) Logging.LOG.warning("Setting actor of primitive act to JOINT: {0}", this); Term[] properties = this.getPropertyValues(); // make unbound first so can check for contradictions properties[WHO] = null; Act act = newAct(getParameterValues(), getResultValues(), properties); if ( who == null ) return act; Proposition binding = Equality.make(who, act.getWho()); if ( binding.isConsistent(Boolean.TRUE, true) ) { binding.setTrue("newWhoValue"); if ( assumption ) binding.setAssumption(true); return act; } else return null; } public Act newParameterValue (int i, Term.Literal value) { Term.Literal old = (Term.Literal) getParameter(i).getValue(); if ( old == value ) return this; Term[] parameters = getParameterValues(); // make unbound first so can check for contradictions parameters[i] = null; Act act = newAct(parameters, getResultValues(), this.getPropertyValues()); if ( value == null ) return act; Proposition binding = Equality.make(value, act.getParameter(i)); if ( binding.isConsistent(Boolean.TRUE, true) ) { binding.setTrue("newParameterValue"); return act; } else return null; } public static int COUNT; // global act class counter public static int INDEX = COUNT++; public int getIndex () { return INDEX; } // overridden in subclasses // following five methods only used for Utterance public Utterance setGloss (String string) { return setGloss(new Gloss(string)); } public Utterance setGloss (Gloss gloss) { Glosser.getLanguage() .setGloss(this, new SetGloss(this.getWho(), gloss)); return (Utterance) this; } public Gloss /* or null */ getGloss () { Object g = Glosser.getLanguage().getResources().map(this); return g instanceof Gloss ? (Gloss) g : g instanceof SetGloss ? ((SetGloss) g).getCachedGloss() : null; } public boolean isYesNo () { return this instanceof YesNo || ( isType(Propose.Should.class) && ((Propose) this).getPurposeValue() instanceof YesNo ); } /** * Creates discourse entities for this utterance by looking for * appropriate slots that are already associated with given gloss. The * basic idea to make a representation that is consistent with what a * parser might have produced for this utterance. * * @param gloss the gloss for this utterance * @return null or a non-empty list of discourse entities **/ public List createEntities (Gloss gloss) { if ( this instanceof Message ) return null; List slots = new ArrayList(); List quoted = new ArrayList(); // do this as separate loop to avoid ConcurrentModificationException for (Iterator i = gloss.getFragments().keySet().iterator(); i.hasNext();) { Object key = i.next(); if ( key instanceof Term.Slot && // not including slots which refer to actions for now // (see design note re resolution of act references in Discourse) !(key instanceof Actor || key instanceof Act || key instanceof Act.Quoted) ) { if ( key instanceof Term.Quoted && ((Term) key).hasValue() ) quoted.add(((Term) key).getUnquotedValue()); else slots.add(key); } } if ( slots.isEmpty() && quoted.isEmpty() ) return null; List entities = new ArrayList(); Map fragments = gloss.getFragments(); for (Iterator i = slots.iterator(); i.hasNext();) { Term.Slot slot = (Term.Slot) i.next(); Gloss.Fragment fragment = (Gloss.Fragment) fragments.get(slot); Entity entity = new Entity((Utterance) this, slot, gloss, fragment.start, fragment.end); if ( quoted.contains(slot) ) entity.setDefinite(true); else if ( !slot.hasValue() ) entity.setIndefinite(true); entities.add(entity); } return entities; } /* DESIGN NOTE: Optional, Repeatable and Unknown Retry State for these flags is stored here rather than in plan, so that these methods can be overridden in step acts. Note that CollToJavaFiles initializes these optionalStep and repeatableStep variables from .coll file. */ protected boolean optionalStep, repeatableStep, unknownRetryStep; public boolean isOptionalStep (Plan plan) { return optionalStep; } public boolean isRepeatableStep (Plan plan) { return repeatableStep; } public boolean isUnknownRetryStep (Plan plan) { return unknownRetryStep; } public boolean isAbandonable (Plan plan) { return false; } public boolean isInterruptible (Plan plan, Act interruption) { return true; } public String toJavaString () { return toJavaString(isExternal()); } protected String toJavaString (boolean suppressWho) { return toJavaString(Java.getName(this),suppressWho); } protected String toJavaString (String name) { // for use in libraries return toJavaString(name,isExternal()); } // for use in libraries protected String toJavaString (String name, boolean suppressWho) { StringBuffer buffer = new StringBuffer("new ").append(name).append('('); if ( !suppressWho ) toJavaStringWho(buffer); int parameters = getParameterCount(); for (int i = 0; i < parameters; i++) { if ( !(i == 0 && suppressWho) ) buffer.append(", "); buffer.append(toJavaStringParameter(i)); } int results = getResultCount(); for (int i = 0; i < results; i++) { if ( !( i == 0 && suppressWho && parameters == 0) ) buffer.append(", "); buffer.append(toJavaStringResult(i)); } addPropertyJavaStrings(buffer, ", ", PROPERTY); return buffer.append(')').toString(); } protected String toJavaStringParameter (int i) { return Variable.toJavaString(getParameter(i).getValue()); } protected String toJavaStringResult (int i) { return Variable.toJavaString(getResult(i).getValue()); } protected void toJavaStringWho (StringBuffer buffer) { // for libraries Actor.Literal who = this.getWhoValue(); if ( who == null ) buffer.append("(Actor) "); // to disambiguate buffer.append(Variable.toJavaString(who)); } // utilities for occurrence subclasses protected void checkProperties (Term[] properties) { Equality.newAxiom(When.make((Act.Occurrence) this), new Situation.Literal(), "construction"); // see note re getCanonical on Base constructor above Actor.Literal who = (Actor.Literal) properties[WHO].getValue(); if ( !(Actor.isUser(who) || Actor.isAgent(who) || Actor.isExternal(who)) ) Logging.LOG.warning( "Who of occurrence is not user, agent, or external: {0}", who); } protected static Term[] checkValues (Term[] values) { if ( values == null ) return null; if ( Utils.getCount(values) != values.length) Logging.LOG.warning( "Attempting to create occurrence with missing values:\n{0}", Arrays.asList(values)); return values; } // defaults // default for manipulations public void notifyApplication (Object application) {} // overridden in literal subclasses public collagen.plan.Act.Occurrence newOccurrence (long timestamp) { Logging.LOG.warning("Cannot make new occurrence of Act variable"); return null; } public Act getAct () { return this; } } } // end of Act