capture master
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Mon, 7 Dec 2020 13:30:16 +0000 (00:30 +1100)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Mon, 7 Dec 2020 13:30:16 +0000 (00:30 +1100)
73 files changed:
Makefile [new file with mode: 0644]
classpath [new file with mode: 0755]
com/intendico/data/.cvsignore [new file with mode: 0644]
com/intendico/data/And.java [new file with mode: 0644]
com/intendico/data/Condition.java [new file with mode: 0644]
com/intendico/data/Distinct.java [new file with mode: 0644]
com/intendico/data/Equals.java [new file with mode: 0644]
com/intendico/data/Inquirable.java [new file with mode: 0644]
com/intendico/data/Lost.java [new file with mode: 0644]
com/intendico/data/Not.java [new file with mode: 0644]
com/intendico/data/Or.java [new file with mode: 0644]
com/intendico/data/Query.java [new file with mode: 0644]
com/intendico/data/QueryBase.java [new file with mode: 0644]
com/intendico/data/Ref.java [new file with mode: 0644]
com/intendico/data/Relation.java [new file with mode: 0644]
com/intendico/data/Rule.java [new file with mode: 0644]
com/intendico/data/RuleSet.java [new file with mode: 0644]
com/intendico/data/Snapshot.java [new file with mode: 0644]
com/intendico/data/Store.java [new file with mode: 0644]
com/intendico/data/addon/Language.java [new file with mode: 0644]
com/intendico/data/addon/TextQueryCapability.java [new file with mode: 0644]
com/intendico/gorite/.cvsignore [new file with mode: 0644]
com/intendico/gorite/Action.java [new file with mode: 0644]
com/intendico/gorite/BDIGoal.java [new file with mode: 0644]
com/intendico/gorite/BranchingGoal.java [new file with mode: 0644]
com/intendico/gorite/Capability.java [new file with mode: 0644]
com/intendico/gorite/ConditionGoal.java [new file with mode: 0644]
com/intendico/gorite/Context.java [new file with mode: 0644]
com/intendico/gorite/ContextualGoal.java [new file with mode: 0644]
com/intendico/gorite/ControlGoal.java [new file with mode: 0644]
com/intendico/gorite/Data.java [new file with mode: 0644]
com/intendico/gorite/DataGoal.java [new file with mode: 0644]
com/intendico/gorite/EndGoal.java [new file with mode: 0644]
com/intendico/gorite/Executor.java [new file with mode: 0644]
com/intendico/gorite/FailGoal.java [new file with mode: 0644]
com/intendico/gorite/Goal.java [new file with mode: 0644]
com/intendico/gorite/GoriteError.java [new file with mode: 0644]
com/intendico/gorite/LoopEndException.java [new file with mode: 0644]
com/intendico/gorite/LoopGoal.java [new file with mode: 0644]
com/intendico/gorite/ParallelEndException.java [new file with mode: 0644]
com/intendico/gorite/ParallelGoal.java [new file with mode: 0644]
com/intendico/gorite/Performer.java [new file with mode: 0644]
com/intendico/gorite/Plan.java [new file with mode: 0644]
com/intendico/gorite/PlanChoiceGoal.java [new file with mode: 0644]
com/intendico/gorite/PoolExecutor.java [new file with mode: 0644]
com/intendico/gorite/Precedence.java [new file with mode: 0644]
com/intendico/gorite/RepeatGoal.java [new file with mode: 0644]
com/intendico/gorite/SequenceGoal.java [new file with mode: 0644]
com/intendico/gorite/Team.java [new file with mode: 0644]
com/intendico/gorite/TeamGoal.java [new file with mode: 0644]
com/intendico/gorite/TransferGoal.java [new file with mode: 0644]
com/intendico/gorite/addon/.cvsignore [new file with mode: 0644]
com/intendico/gorite/addon/BellBoy.java [new file with mode: 0644]
com/intendico/gorite/addon/Handshake.java [new file with mode: 0644]
com/intendico/gorite/addon/Perceptor.java [new file with mode: 0644]
com/intendico/gorite/addon/Reflector.java [new file with mode: 0644]
com/intendico/gorite/addon/RemotePerforming.java [new file with mode: 0644]
com/intendico/gorite/addon/SubGoal.java [new file with mode: 0644]
com/intendico/gorite/addon/TimeTrigger.java [new file with mode: 0644]
com/intendico/gorite/addon/TodoGroupParallel.java [new file with mode: 0644]
com/intendico/gorite/addon/TodoGroupRoundRobin.java [new file with mode: 0644]
com/intendico/gorite/addon/TodoGroupSkipBlocked.java [new file with mode: 0644]
com/intendico/gorite/addon/remote/Connection.java [new file with mode: 0644]
com/intendico/gorite/addon/remote/Connector.java [new file with mode: 0644]
com/intendico/gorite/addon/remote/RemotePerforming.java [new file with mode: 0644]
com/intendico/sdp/BNFGrammar.java [new file with mode: 0644]
com/intendico/sdp/Grammar.java [new file with mode: 0644]
com/intendico/sdp/LLBNF.java [new file with mode: 0644]
examples/planchoice/Main.java [new file with mode: 0644]
examples/planchoice/filter [new file with mode: 0755]
examples/planchoice/output [new file with mode: 0644]
examples/ruleset/Main.java [new file with mode: 0644]
overview.html [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b015763
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,134 @@
+# 
+# Copyright 2012, Ralph Ronnquist.
+#
+# Makefile for GORITE
+#
+
+CP := $(shell ./classpath)
+
+DIRS  = com/intendico/gorite
+DIRS += com/intendico/data
+DIRS += com/intendico/gorite/addon
+DIRS += com/intendico/data/addon
+DIRS += com/intendico/sdp
+
+EXGORITE = tests/com/intendico/gorite/examples
+EXDATA = tests/com/intendico/data/examples
+
+SRC     := $(foreach D,$(DIRS),$(wildcard $(D)/*.java))
+SOURCES  := $(filter-out %/package-info.java,$(SRC))
+CLASSES         = $(SOURCES:%.java=%.class)
+CURRENT  = $(foreach C,$(CLASSES),$(wildcard $(C)))
+MISSING  = $(filter-out $(CURRENT),$(CLASSES))
+OUTDATED = $(shell find $(DIRS) -name "*.java" \
+              $(if $(CURRENT),-newer $(word 1,$(shell ls -1t $(CURRENT)))))
+
+jar: gorite.jar
+
+.PHONY: clean compile javadoc
+
+#JAVASRCVERSION = -source 1.4 -Xlint:unchecked
+JAVASRCVERSION = -Xlint:-unchecked
+
+compile: $(OUTDATED) $(MISSING:%.class=%.java)
+       CLASSPATH=$(CP) javac $(JAVASRCVERSION) $?
+
+gorite.jar: $(if $(OUTDATED)$(MISSING),compile) $(SOURCES)
+       touch gorite.jar
+       jar uf gorite.jar $(addsuffix /*.class,$(DIRS))
+
+install: gorite.jar
+       install gorite.jar ../../lib/
+
+clean:
+       rm -f $(CLASSES:%.class=%*.class) gorite.jar
+       rm -rf html
+
+javadoc: VERSION := v11RC05
+JAVAAPI = http://download.oracle.com/javase/6/docs/api
+YEAR = $(shell date +%Y)
+DOCARGS  = -overview overview.html -private -d html
+DOCARGS += -footer "&copy; Copyright $(YEAR), Ralph Ronnquist."
+DOCARGS += -header "GORITE Version $(VERSION)"
+DOCARGS += -linksource
+DOCARGS += -linkoffline "$(JAVAAPI)" pkg/java
+DOCARGS += -classpath $(CP)
+
+
+
+javadoc: gorite.jar
+       javadoc $(DOCARGS) $(subst /,.,$(DIRS)) $(TESTS)
+       #cat HEADER > html/Main.java
+       #more $(EXGORITE)/spacetravel/[A-Z]*.java >> html/Main.java
+
+TESTS = $(wildcard $(EXGORITE)/*/*.java)
+TESTS += $(wildcard $(EXDATA)/*/*.java)
+
+SKIP := $(EXGORITE)/mice/
+SKIP += $(EXGORITE)/klotski/
+SKIP += $(EXGORITE)/chapter3_sensors/
+
+
+TCP = $(shell pwd)/tests:$(shell pwd)/gorite.jar
+TDIR = $(filter-out $(SKIP),$(sort $(dir $(TESTS))))
+
+.PHONY: tests $(TDIR) .force
+
+#PROPS = -Dgorite.goal.trace=yes
+PROPS = -Xss60k -Xmx200m
+#PROPS += -Dgorite.tracer=com.intendico.gorite.addon.ConsoleTracer
+
+ifdef REGRESSION
+$(TDIR): gorite.jar .force
+       @echo "TEST: $@"
+       @CLASSPATH=$(TCP) java $(PROPS) $(@:tests/%=%)Main 2>&1 | $@filter > /tmp/tests.log
+       @diff -N $@output /tmp/tests.log
+
+$(SKIP): .force
+       @echo "SKIP: $@"
+
+tests:  gorite.jar $(TESTS)
+       @$(MAKE) -s $(filter-out ./,$(sort $(dir $?))) 2>&1 | \
+            tee regression.$$(date +%Y%m%d-%H%M)
+
+else
+
+tests/com/intendico/gorite/examples/fleet/: A = 20 100
+$(TDIR): gorite.jar
+       @echo "===== Test: $@"
+       CLASSPATH=$(TCP) javac  -Xlint:unchecked $@*.java
+       CLASSPATH=$(TCP) java $(PROPS) $(@:tests/%=%)Main $(A)
+       @echo "===== End: $@"
+
+tests:  gorite.jar $(TESTS)
+       echo "$(TESTS)"
+       @echo "****************************************************"
+       @$(MAKE) $(filter-out ./,$(sort $(dir $?))) 2>&1 | \
+            tee log.$$(date +%Y%m%d-%H%M)
+
+endif
+
+doc/%.html: doc/%.sdf
+       cd doc; sdf -2html $$(basename $?)
+
+DOCS = CHANGES
+
+TARFLAG = --exclude=CVS
+BINDIST = gorite.jar html $(DOCS)
+SRCDIST = $(BINDIST) $(SRC) $(TESTS) overview.html Makefile classpath
+SRCDIST += $(wildcard $(EXGORITE)/*/filter)
+SRCDIST += $(wildcard $(EXGORITE)/*/output)
+
+#SRCDIST += DIFF.baseline
+
+srcdist: DOCARGS += -linksource
+srcdist: javadoc $(DOCS) $(SRCDIST)
+       mkdir -p dist
+       tar czf dist/gorite-src-$$(date +%Y%m%d).tgz $(TARFLAG) $(SRCDIST)
+
+bindist: javadoc $(DOCS) $(BINDIST)
+       mkdir -p dist
+       tar czf dist/gorite-$$(date +%Y%m%d).tgz $(TARFLAG) $(BINDIST)
+
+regression:
+       $(MAKE) tests
diff --git a/classpath b/classpath
new file mode 100755 (executable)
index 0000000..3050976
--- /dev/null
+++ b/classpath
@@ -0,0 +1 @@
+echo $(pwd)
diff --git a/com/intendico/data/.cvsignore b/com/intendico/data/.cvsignore
new file mode 100644 (file)
index 0000000..6b468b6
--- /dev/null
@@ -0,0 +1 @@
+*.class
diff --git a/com/intendico/data/And.java b/com/intendico/data/And.java
new file mode 100644 (file)
index 0000000..4cafbd9
--- /dev/null
@@ -0,0 +1,200 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * The And class implements a conjunction of multiple queries. It
+ * provides a left-to-right short-cut evaluation, which returns
+ * immediately if a conjunct fails.
+ */
+public class And implements Query {
+
+    /**
+     * Holds the conjuncts being combined.
+     */
+    public Query [] conjuncts;
+
+    /**
+     * Keeps track of which query is current.
+     */
+    public int current;
+
+    /**
+     * Constructor.
+     */
+    public And(Query/*...*/ [] q) throws Exception {
+       conjuncts = q;
+       reset();
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * And object with copies of the conjuncts.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       Query [] q = new Query [ conjuncts.length ];
+       for ( int i = 0; i < q.length; i++ ) {
+           q[i] = conjuncts[i].copy( newrefs );
+       }
+       return new And( q );
+    }
+
+    /**
+     * The {@link Query#reset} method implemented by reverting to the
+     * first conjunct and resetting it.
+     */
+    public void reset()
+       throws Exception
+    {
+       current = 0;
+       if ( conjuncts != null && conjuncts.length > 0 )
+           conjuncts[ 0 ].reset();
+    }
+
+    /**
+     * The {@link Query#next} method implemented by advancing all
+     * conjucts to the next match in a left-to-right fashion. Thus,
+     * the right-most conjunct is advanced to its next match. If the
+     * conjunct is exhausted, it gets reset, but after that the
+     * preceding conjunct is advanced to its next match. If that is
+     * exhausted, then that gets reset, and so on, up to advancing the
+     * first conjunct until it gets exhausted.
+     */
+    public boolean next()
+       throws Exception
+    {
+       if ( conjuncts == null || conjuncts.length == 0 )
+           return true;
+       while ( current >= 0 ) {
+           if ( conjuncts[ current ].next() ) {
+               current += 1;
+               if ( current < conjuncts.length ) {
+                   conjuncts[ current ].reset();
+                   continue;
+               }
+               current -= 1;
+               return true;
+           }
+           current -= 1;
+       }
+       return false;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by combining the
+     * Ref objects of all conjuncts.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       if ( conjuncts != null ) {
+           for ( int i=0; i < conjuncts.length; i++ ) {
+               conjuncts[ i ].getRefs( v );
+           }
+       }
+       return v;
+    }
+
+    /**
+     * The {@link Query#addObserver} method implemented by adding
+     * the observer to all the conjuncts.
+     */
+    public void addObserver(Observer x) {
+       if ( conjuncts != null )
+           for ( int i=0; i < conjuncts.length; i++ )
+               conjuncts[ i ].addObserver( x );
+    }
+
+    /**
+     * The {@link Query#deleteObserver} method implemented by
+     * removing the observer from all the conjuncts.
+     */
+    public void deleteObserver(Observer x) {
+       if ( conjuncts != null )
+           for ( int i=0; i < conjuncts.length; i++ )
+               conjuncts[ i ].deleteObserver( x );
+    }
+
+    /**
+     * The {@link Query#addable} method implemented by forwarding to all
+     * conjuncts.
+     */
+    public boolean addable()
+    {
+       for ( int i=0; i < conjuncts.length; i++ )
+           if ( ! conjuncts[ i ].addable() )
+               return false;
+       return true;
+    }
+
+    /**
+     * The {@link Query#add} method implemented by forwarding to all
+     * conjuncts.
+     */
+    public boolean add()
+    {
+       boolean added = false;
+       if ( conjuncts != null )
+           for ( int i=0; i < conjuncts.length; i++ )
+               added |= conjuncts[ i ].add();
+       return added;
+    }
+
+    /**
+     * Implements {@link Query#removable} by finding a conjunct that
+     * is removable.
+     */
+    public boolean removable()
+    {
+       for ( int i=0; i < conjuncts.length; i++ )
+           if ( conjuncts[ i ].removable() )
+               return true;
+       return false;
+    }
+
+    /**
+     * Implements {@link Query#remove} by forawrding to the first
+     * conjunct that is removable.
+     */
+    public boolean remove() {
+       for ( int i=0; i < conjuncts.length; i++ )
+           if ( conjuncts[ i ].removable() )
+               return conjuncts[ i ].remove();
+       return false;
+    }
+
+    /**
+     * Returns a String representation of the And object.
+     */
+    public String toString() {
+       StringBuffer s = new StringBuffer();
+       s.append( "And(" );
+       if ( conjuncts != null )
+           for ( int i=0; i < conjuncts.length; i++ ) {
+               if ( i > 0 )
+                   s.append( "," );
+               s.append( " " );
+               s.append( conjuncts[ i ].toString() );
+           }
+       s.append( " )" );
+       return s.toString();
+    }
+}
diff --git a/com/intendico/data/Condition.java b/com/intendico/data/Condition.java
new file mode 100644 (file)
index 0000000..cac0cf6
--- /dev/null
@@ -0,0 +1,183 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+import java.util.Iterator;
+
+/**
+ * A utility class for including a boolean method in query
+ * processing. The typical use is as an inline extension that
+ * implements the {@link #condition} method.
+ */
+abstract public class Condition implements Query {
+
+    /**
+     * An always true Condition query.
+     */
+    public static Condition TRUE = new Condition() {
+           public boolean condition() {
+               return true;
+           }
+       };
+
+    /**
+     * Control flag to mark whether the condition has been tested or
+     * not.
+     */
+    public boolean tested = false;
+
+    /**
+     * Cache of initially unbound variables. These are captured on
+     * construction, just in case there is an extension that takes
+     * variables. In that case, the next() method on failing must
+     * restore the incoming unbound variables to be unbound.
+     */
+    public Vector variables;
+
+    /**
+     * The {@link Query#reset} method implemented by noting the
+     * condition as not tested.
+     */
+    public void reset() throws Exception {
+       tested = false;
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by using this same
+     * object.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       return this;
+    }
+
+    /**
+     * The {@link Query#next} method implemented by returning as per
+     * the {@link #condition} method on its first invocation, then
+     * returning false without invoking the {@link #condition} method,
+     * until {@link #reset()}. This method also captures any unbound
+     * variables (an extension class may have variables), and clears
+     * these before returning false.
+     */
+    public boolean next() throws Exception {
+       if ( ! tested ) {
+           variables = getRefs( new Vector() );
+           if ( variables.size() > 0 ) {
+               for ( Iterator i = variables.iterator(); i.hasNext(); ) {
+                   if ( ((Ref) i.next()).get() != null ) {
+                       i.remove();
+                   }
+               }
+               if ( variables.size() == 0 )
+                   variables = null;
+           } else {
+               variables = null;
+           }
+           tested = true;
+           if ( condition() )
+               return true;
+       }
+       if ( variables != null ) {
+           for ( Iterator i = variables.iterator(); i.hasNext(); ) {
+               ((Ref) i.next()).clear();
+           }
+       }
+       return false;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by adding
+     * nothing. An actual condition that uses {@link Ref} objects
+     * should override this method.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       return v;
+    }
+
+    /**
+     * The {@link Query#addObserver} method implemented by doing
+     * nothing. An actual condition that is triggering should override
+     * this method as well as {@link #deleteObserver}.
+     */
+    public void addObserver(Observer x) {
+    }
+
+    /**
+     * The {@link Query#deleteObserver} method implemented by doing
+     * nothing. An actual condition that is triggering should override
+     * this method as well as {@link #addObserver}.
+     */
+    public void deleteObserver(Observer x) {
+    }
+
+    /**
+     * The {@link Query#addable} method is implemented by a reset
+     * followed by next.
+     */
+    public boolean addable() {
+       try {
+           reset();
+           return next();
+       } catch (Exception e) {
+           e.printStackTrace();
+           return false;
+       }
+    }
+
+    /**
+     * The {@link Query#add} method implemented by returning false. An
+     * actual condition that gets updated should override this method.
+     */
+    public boolean add() {
+       return false;
+    }
+
+    /**
+     * Implements {@link Query#removable} by calling {@link #addable}
+     */
+    public boolean removable()
+    {
+       return addable();
+    }
+
+    /**
+     * Implements {@link Query#remove} by returning false;
+     */
+    public boolean remove() {
+       return false;
+    }
+
+    /**
+     * The actual condition returning <em>true</em> or
+     * <em>false</em>. If it is appropriate to re-test the condition
+     * several times before resetting, the actual condition may also
+     * set the {@link #tested} flag.
+     */
+    abstract public boolean condition() throws Exception;
+
+    /**
+     * Returns a {@link java.lang.String} representation of the
+     * condition object.
+     */
+    public String toString() {
+       return "Condition#" + System.identityHashCode( this );
+    }
+}
diff --git a/com/intendico/data/Distinct.java b/com/intendico/data/Distinct.java
new file mode 100644 (file)
index 0000000..7304ed3
--- /dev/null
@@ -0,0 +1,99 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Vector;
+import java.util.HashSet;
+
+/**
+ * The Distinct class is a query to ensure that a collection of given
+ * {@link Ref} objects have distinct bindings.
+ */
+public class Distinct extends Condition {
+
+    /**
+     * The Ref objects concerned.
+     */
+    public Object [] refs;
+
+    /**
+     * Constructor;
+     */
+    public Distinct(Object/*...*/ [] r) {
+       refs = r;
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * Distinct object with the copies of the refs.
+     */
+    //@SuppressWarnings("unchecked")
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       Object [] nrefs = new Object [ refs.length ];
+       for ( int i = 0; i < refs.length; i++ ) {
+           if ( refs[ i ] instanceof Ref ) {
+               nrefs[ i ] = ((Ref) refs[ i ]).find( newrefs );
+           } else {
+               nrefs[ i ] = refs[ i ];
+           }
+       }
+       return new Distinct( nrefs );
+    }
+
+    /**
+     * The distinction condition
+     * @return <em>false</em> if any {@link Ref} object is
+     * <em>null</em>, or equal to any other {@link Ref} object,
+     * otherwise <em>true</em>.
+     */
+    public boolean condition() {
+       HashSet/*<Object>*/ v = new HashSet/*<Object>*/();
+       for ( int i = 0; i < refs.length; i++ ) {
+           Object r = refs[ i ];
+           Object x = Ref.deref( r );
+           if ( x == null || v.contains( x ) )
+               return false;
+           v.add( x );
+       }
+       return true;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by adding any Ref
+     * object not already in the vector.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       for ( int i = 0; i < refs.length; i++ ) {
+           Object r = refs[ i ];
+           if ( r instanceof Ref ) {
+               if ( ! v.contains( (Ref) r ) )
+                   v.add( (Ref) r );
+           }
+       }
+       return v;
+    }
+
+    /**
+     * Returns the textual representation of the query.
+     */
+    public String toString() {
+       return "Distinct(" + Ref.toString( refs ) + ")";
+    }
+}
diff --git a/com/intendico/data/Equals.java b/com/intendico/data/Equals.java
new file mode 100644 (file)
index 0000000..dcc4ca9
--- /dev/null
@@ -0,0 +1,109 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Vector;
+
+/**
+ * The Equals class is a query to ensure that two or more given
+ * objects are equal, where any or both of the objects may be {@link
+ * Ref} objects.  If a {@link Ref} object is null, it is set to the
+ * other value.
+ */
+public class Equals extends Condition {
+
+    /**
+     * The distinct values concerned.
+     */
+    public Object [] values;
+
+    /**
+     * Constructor.
+     */
+    public Equals(Object/*...*/ [] v) {
+       values = v;
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * Except object with a copy of the ref, but the same values
+     */
+    //@SuppressWarnings("unchecked")
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       Object [] v = new Object [ values.length ];
+       for ( int i = 0; i < values.length; i++ ) {
+           if ( values[ i ] instanceof Ref ) {
+               v[ i ] = ((Ref)values[ i ]).find( newrefs );
+           } else {
+               v[ i ] = values[ i ];
+           }
+       }
+       return new Equals( v );
+    }
+
+    /**
+     * The exception condition. 
+     * @return <em>false</em> if the {@link Ref} object is equal to
+     * any value, otherwise <em>true</em>. Note that any Ref object as
+     * value is also dereferenced.
+     */
+    public boolean condition() {
+       Object x = null;
+       for ( int i = 0; i < values.length; i++ ) {
+           Object value = values[ i ];
+           Object y =  Ref.deref( value );
+           if ( x != null && ! x.equals( y ) )
+               return false;
+           x = y;
+       }
+       return true;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by adding and Ref
+     * not already contained in the vector.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       for ( int i = 0; i < values.length; i++ ) {
+            Object x = values[ i ];
+           if ( x instanceof Ref && ! v.contains( x ) )
+               v.add( (Ref) x );
+       }
+       return v;
+    }
+
+    /**
+     * Returns the textual representation of the query.
+     */
+    public String toString() {
+       StringBuilder s = new StringBuilder( "equals(" );
+       for ( int i = 0; i < values.length; i++ ) {
+            Object x = values[ i ];
+           s.append( "," );
+           if ( x instanceof Ref ) {
+               s.append( ((Ref) x).getName() );
+               s.append( "=" );
+           }
+           s.append( x.toString() );
+       }
+       s.append( ")" );
+       return s.toString();
+    }
+}
diff --git a/com/intendico/data/Inquirable.java b/com/intendico/data/Inquirable.java
new file mode 100644 (file)
index 0000000..84a7302
--- /dev/null
@@ -0,0 +1,39 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+/**
+ * This interface defines the requirements of an inquirable data
+ * structure, which basically is a named {@link Query} factory.
+ */
+public interface Inquirable {
+
+    /**
+     * Interface method to obtain the name of this Inquirable.
+     */
+    public String getName();
+
+    /**
+     * Interface method for providing an access {@link Query} given a
+     * collection of values and {@link Ref} objects.
+     */
+    public Query get(Object/*...*/ [] arguments) throws Exception ;
+
+}
diff --git a/com/intendico/data/Lost.java b/com/intendico/data/Lost.java
new file mode 100644 (file)
index 0000000..c7616a8
--- /dev/null
@@ -0,0 +1,103 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * The Lost class is a {@link Snapshot} that enumerates the lost
+ * tuples rather than the added tuples, and it also treats adding and
+ * removing like {@link Not}.
+ */
+public class Lost extends Snapshot {
+
+    /**
+     * Constructor;
+     */
+    public Lost(Query q) {
+       super( q );
+    }
+
+    /**
+     * Alternative constructor with synchronization object.
+     */
+    public Lost(Object s,Query q) {
+       super( s, q );
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * Lost with a copy of the monitored {@link #query}, and sharing
+     * any {@link #sync} object.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       return new Lost( sync, query.copy( newrefs ) );
+    }
+
+    /**
+     * The {@link Query#reset} method implemented by forwarding to the
+     * wrapped query.
+     */
+    public void reset() throws Exception {
+       super.reset();
+       bindings = lost.iterator();
+    }
+
+    /**
+     * Implements {@link Query#addable} by calling {@link
+     * Query#removable} on the wrapped query.
+     */
+    public boolean addable() {
+       return query.removable();
+    }
+
+    /**
+     * Implements {@link Query#add} by calling {@link Query#remove} on
+     * the wrapped query.
+     */
+    public boolean add() {
+       return query.remove();
+    }
+
+    /**
+     * Implements {@link Query#removable} by calling {@link
+     * Query#addable} on the wrapped query.
+     */
+    public boolean removable() {
+       return query.addable();
+    }
+
+    /**
+     * Implements {@link Query#remove} by calling {@link Query#add} on
+     * the wrapped query.
+     */
+    public boolean remove() {
+       return query.add();
+    }
+
+    /**
+     * Returns the textual representation of the wrapped query, within
+     * parentheses.
+     */
+    public String toString() {
+       return "Lost(" + query + ")";
+    }
+}
diff --git a/com/intendico/data/Not.java b/com/intendico/data/Not.java
new file mode 100644 (file)
index 0000000..380b736
--- /dev/null
@@ -0,0 +1,162 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.Observer;
+
+/**
+ * The Not class implements a negation-by-failure query, which is true
+ * only if the wrapped {@link Query} does not provide any valid
+ * bindings. The Not class also manages input bindings to ensure that
+ * nothing unbound gets bound by the wrapped query.
+ */
+public class Not implements Query {
+
+    /**
+     * Flag to mark whether the query has been tested.
+     */
+    public boolean tested;
+
+    /**
+     * Holds unbound input variables.
+     */
+    public Vector/*<Ref>*/ unbound;
+
+    /**
+     * Holds wrapped query.
+     */
+    public Query query;
+
+    /**
+     * Constructor.
+     */
+    public Not(Query q) throws Exception {
+       query = q;
+       reset();
+    }
+
+    /**
+     * Implements {@link Query#copy} by creating a new Not object
+     * around a copy of the wrapped query.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       return new Not( query.copy( newrefs ) );
+    }
+
+    /**
+     * Implements {@link Query#reset} by capturing unbound Ref
+     * objects, then forwarding the reset call to the wrapped query.
+     */
+    public void reset() throws Exception {
+       unbound = new Vector/*<Ref>*/();
+       for ( Iterator/*<Ref>*/ i =
+                 query.getRefs( new Vector/*<Ref>*/() ).iterator();
+             i.hasNext(); ) {
+           Ref r = (Ref) i.next();
+           if ( r.get() == null )
+               unbound.add( r );
+       }
+       tested = false;
+       query.reset();
+    }
+
+    /**
+     * Implements {@link Query#next} by calling on the wrapped query,
+     * then remove any bindings.
+     */
+    //@SuppressWarnings("unchecked")
+    public boolean next() throws Exception {
+       if ( tested )
+           return false;
+       tested = true;
+       boolean b = query.next();
+       for ( Iterator/*<Ref>*/ i = unbound.iterator(); i.hasNext(); ) {
+           Ref r = (Ref) i.next();
+           r.set( null );
+       }
+       return ! b;
+    }
+
+    /**
+     * Implements {@link Query#getRefs} by forwarding to the wrapped
+     * query.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       return query.getRefs( v );
+    }
+
+    /**
+     * Implements {@link Query#addObserver} by forwarding to the
+     * wrapped query.
+     */
+    public void addObserver(Observer x) {
+       query.addObserver( x );
+    }
+
+    /**
+     * Implements {@link Query#deleteObserver} by forwarding to the
+     * wrapped query.
+     */
+    public void deleteObserver(Observer x) {
+       query.deleteObserver( x );
+    }
+
+    /**
+     * Implements {@link Query#addable} by calling {@link
+     * Query#removable} on the wrapped query.
+     */
+    public boolean addable() {
+       return query.removable();
+    }
+
+    /**
+     * Implements {@link Query#add} by calling {@link Query#remove} on
+     * the wrapped query.
+     */
+    public boolean add() {
+       return query.remove();
+    }
+
+    /**
+     * Implements {@link Query#removable} by calling {@link
+     * Query#addable} on the wrapped query.
+     */
+    public boolean removable() {
+       return query.addable();
+    }
+
+    /**
+     * Implements {@link Query#remove} by calling {@link Query#add} on
+     * the wrapped query.
+     */
+    public boolean remove() {
+       return query.add();
+    }
+
+    /**
+     * Returns the textual representation of the wrapped query, within
+     * parentheses.
+     */
+    public String toString() {
+       return "(" + query + ")";
+    }
+}
diff --git a/com/intendico/data/Or.java b/com/intendico/data/Or.java
new file mode 100644 (file)
index 0000000..d18adf5
--- /dev/null
@@ -0,0 +1,181 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * The Or class implements a disjunction of multiple queries.
+ */
+public class Or implements Query {
+
+    /**
+     * Holds the disjuncts being combined.
+     */
+    public Query [] disjuncts;
+
+    /**
+     * Keeps track of which query is current.
+     */
+    public int current;
+
+    /**
+     * Constructor.
+     */
+    public Or(Query/*...*/ [] q)
+       throws Exception {
+       disjuncts = q;
+       reset();
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * Or object with copies of the disjuncts.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       Query [] q = new Query [ disjuncts.length ];
+       for ( int i = 0; i < q.length; i++ ) {
+           q[i] = disjuncts[i].copy( newrefs );
+       }
+       return new Or( q );
+    }
+
+    /**
+     * The {@link Query#reset} method implemented by reverting to the
+     * first disjunct and resetting it.
+     */
+    public void reset()
+       throws Exception
+    {
+       current = 0;
+       disjuncts[ 0 ].reset();
+    }
+
+    /**
+     * The {@link Query#next} method implemented by considering all
+     * disjucts one by one to the next match in a left-to-right fashion.
+     */
+    public boolean next()
+       throws Exception
+    {
+       while ( current < disjuncts.length ) {
+           if ( disjuncts[ current ].next() )
+               return true;
+           current += 1;
+           if ( current >= disjuncts.length )
+               break;
+           disjuncts[ current ].reset();
+       }
+       return false;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by combining the
+     * Ref objects of all disjuncts.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       for ( int i=0; i < disjuncts.length; i++ )
+           disjuncts[ i ].getRefs( v );
+       return v;
+    }
+
+    /**
+     * The {@link Query#addObserver} method implemented by adding
+     * the observer to all the disjuncts.
+     */
+    public void addObserver(Observer x) {
+       for ( int i=0; i < disjuncts.length; i++ )
+           disjuncts[ i ].addObserver( x );
+    }
+
+    /**
+     * The {@link Query#deleteObserver} method implemented by
+     * removing the observer from all the disjuncts.
+     */
+    public void deleteObserver(Observer x) {
+       for ( int i=0; i < disjuncts.length; i++ )
+           disjuncts[ i ].deleteObserver( x );
+    }
+
+    /**
+     * The {@link Query#addable} method implemented by forwarding to all
+     * disjuncts.
+     */
+    public boolean addable()
+    {
+       for ( int i=0; i < disjuncts.length; i++ )
+            if ( disjuncts[ i ].addable() )
+               return true;
+       return false;
+    }
+
+    /**
+     * The {@link Query#add} method implemented by forwarding to first
+     * addable disjunct.
+     */
+    public boolean add()
+    {
+       for ( int i=0; i < disjuncts.length; i++ ) {
+           if ( disjuncts[ i ].addable() )
+               return disjuncts[ i ].add();
+       }
+       return false;
+    }
+
+    /**
+     * Implements {@link Query#removable} by checking that all
+     * disjuncts are removable.
+     */
+    public boolean removable()
+    {
+       for ( int i=0; i < disjuncts.length; i++ )
+           if ( ! disjuncts[ i ].removable() )
+               return false;
+       return true;
+    }
+
+    /**
+     * Implements {@link Query#remove} by forwarding to all disjuncts.
+     */
+    public boolean remove() {
+       boolean removed = false;
+       for ( int i=0; i < disjuncts.length; i++ )
+           if ( disjuncts[ i ].removable() )
+               removed |= disjuncts[ i ].remove();
+       return removed;
+    }
+
+    /**
+     * Returns a {@link String} representation of the Or object.
+     */
+    public String toString() {
+       StringBuffer s = new StringBuffer();
+       s.append( "Or(" );
+       for ( int i=0; i < disjuncts.length; i++ ) {
+           if ( i > 0 )
+               s.append( "," );
+           s.append( " " );
+           s.append( disjuncts[ i ].toString() );
+       }
+       s.append( " )" );
+       return s.toString();
+    }
+}
diff --git a/com/intendico/data/Query.java b/com/intendico/data/Query.java
new file mode 100644 (file)
index 0000000..ae0e817
--- /dev/null
@@ -0,0 +1,144 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * The Query interface defines methods for query processing, to
+ * support left-to-right short-cut evaluation with multiple bindings.
+ *
+ * <p>The sequence for query processing would typically be as follows:
+ * <ol>
+ *
+ * <li> create the {@link Ref} objects to carry query outputs and
+ * intermediate values between query conjuncts;
+ *
+ * <li> create the query object structure;
+ *
+ * <li> (optionally) add an observer on the query, in order to
+ * capture triggerring from the source elements. This is only needed
+ * when the following query processing is deferred or temporal in some
+ * fashion;
+ *
+ * <li> invoke the {@link #reset} method. Most {@link Query}
+ * implementations include an automatic intital {@link #reset}, and
+ * therefore this call would only be needed at a second or subsequent
+ * use of the query object structure;
+ *
+ * <li> invoke the {@link #next} method. The first invokation
+ * establishes the first valid combination of bindings for the {@link
+ * Ref} objects involved, and subsequent calls establish subsequent
+ * valid bindings. The {@link #next} method returns <i>false</i> when
+ * it exhausts the vlid combinations of bindings.
+ *
+ * <li> (optionally) remove the observer previosuly added.
+ *
+ * </ol>
+ *
+ * <p> The following snippet is an illustration:
+ * <pre>
+ * Relation r = new Relation( "parent", String.class, String.class );
+ * Ref<String> p = new Ref<String>( "$parent" );
+ * Ref<String> pc = new Ref<String>( "$child_parent" );
+ * Ref<String> c = new Ref<String>( "$child" );
+ *
+ * Query grandparent = new And( r.get( p, ch ), r.get( ch, c ) );
+ * while ( grandparent.next() ) {
+ *     // Here r, pc and c have a valid combination of bindings
+ *     System.out.println( p + " is grand parent of " + c );
+ * }
+ * 
+ * </pre>
+ */
+public interface Query {
+
+    /**
+     * The reset() method is invoked in order for the Query to renew
+     * itself, and provide its binding sequence from the top. The
+     * Query should renew itself with regard to any changes in the
+     * input.
+     */
+    public void reset() throws Exception;
+
+    /**
+     * The next() method is invoked in order for the Query to
+     * establish the next binding in its output.
+     */
+    public boolean next() throws Exception;
+
+    /**
+     * The getRefs() method is invoked in order for the Query to
+     * collect and report all its Ref objects.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v);
+
+    /**
+     * The copy method is invoked for the purpose of obtaining a deep
+     * copy of a query, and in particular using new {@link Ref}
+     * objects by the original names.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception;
+
+    /**
+     * The addObserver method is invoked for the purpose of adding a
+     * Query processor to the observable source(s) of the query.
+     */
+    public void addObserver(Observer x);
+
+    /**
+     * The deleteObserver method is invoked by the Query processor in
+     * order to "detach" from the observable Query source(s).
+     */
+    public void deleteObserver(Observer x);
+
+    /**
+     * The addable method is invoked with bound {@link Ref} objects to
+     * ask whether these bindings could be added.
+     */
+    public boolean addable();
+
+    /**
+     * The add method is invoked with bound {@link Ref} objects in
+     * order to add the current combination to all query sources, if
+     * possible.
+     *
+     * @return <em>true</em> if any source element was updated, and
+     * <em>false</em> otherwise.
+     */
+    public boolean add();
+
+    /**
+     * The removable method is invoked with bound {@link Ref} objects
+     * to ask whether these bindings could be removed.
+     */
+    public boolean removable();
+
+    /**
+     * The remove method is invoked with bound {@link Ref} objects in
+     * order to remove the current combination to query sources.
+     *
+     * @return <em>true</em> if any source element was updated, and
+     * <em>false</em> otherwise.
+     */
+    public boolean remove();
+
+}
diff --git a/com/intendico/data/QueryBase.java b/com/intendico/data/QueryBase.java
new file mode 100644 (file)
index 0000000..f6654e3
--- /dev/null
@@ -0,0 +1,139 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * The QueryBase class is a wrapper class that provides transparent
+ * implementations for the {@link Query} methods. It is intended to be
+ * base class for simple query implementations that only need to
+ * override one or a few of the implementations, typically the
+ * constructor.
+ */
+public class QueryBase implements Query {
+
+    /**
+     * Holds a/the sub query wrapped by this QueryBase.
+     */
+    public Query query;
+
+    /**
+     * Constructor;
+     */
+    public QueryBase() {
+    }
+
+    /**
+     * Constructor;
+     */
+    public QueryBase(Query q) {
+       query = q;
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by returning this
+     * same query.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       return this;
+    }
+
+    /**
+     * The {@link Query#reset} method implemented by forwarding to the
+     * wrapped query.
+     */
+    public void reset() throws Exception {
+       query.reset();
+    }
+
+    /**
+     * The {@link Query#next} method implemented by forwarding to the
+     * wrapped query.
+     */
+    public boolean next() throws Exception {
+       return query.next();
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by forwarding to the
+     * wrapped query.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       return query.getRefs( v );
+    }
+
+    /**
+     * The {@link Query#addObserver} method implemented by forwarding
+     * to the wrapped query.
+     */
+    public void addObserver(Observer x) {
+       query.addObserver( x );
+    }
+
+    /**
+     * The {@link Query#deleteObserver} method implemented by
+     * forwarding to the wrapped query.
+     */
+    public void deleteObserver(Observer x) {
+       query.deleteObserver( x );
+    }
+
+    /**
+     * The {@link Query#addable} method implemented by forwarding to
+     * the wrapped query.
+     */
+    public boolean addable() {
+       return query.addable();
+    }
+
+    /**
+     * The {@link Query#add} method implemented by forwarding to the
+     * wrapped query.
+     */
+    public boolean add() {
+       return query.add();
+    }
+
+    /**
+     * The {@link Query#removable} method implemented by forwarding to
+     * the wrapped query.
+     */
+    public boolean removable() {
+       return query.removable();
+    }
+
+    /**
+     * The {@link Query#remove} method implemented by forwarding to
+     * the wrapped query.
+     */
+    public boolean remove() {
+       return query.remove();
+    }
+
+    /**
+     * Returns the textual representation of the wrapped query, within
+     * parentheses.
+     */
+    public String toString() {
+       return "(" + query + ")";
+    }
+}
diff --git a/com/intendico/data/Ref.java b/com/intendico/data/Ref.java
new file mode 100644 (file)
index 0000000..adc497e
--- /dev/null
@@ -0,0 +1,259 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Vector;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/**
+ * This class is a carrier of values. The Relation class knows of Ref
+ * objects, so as to allow passing of values between queries.
+ */
+public class Ref/*<T>*/ implements Serializable {
+
+    /**
+     * Utility method to obtain an actual value of a possible Ref
+     * object.
+     */
+    public static Object deref(Object x) {
+       return x instanceof Ref? ((Ref) x).get() : x;
+    }
+
+    /**
+     * Utility method that creates an array of Object values from an
+     * array of Ref
+     */
+    public static Object [] getValues(Ref [] rs) {
+       Object [] v = new Object [ rs.length ];
+       for ( int i = 0; i < rs.length; i++ ) {
+           v[ i ] = rs[ i ].get();
+       }
+       return v;
+    }
+
+    /**
+     * Utility method to set an array of Ref from an array of Object
+     * values.
+     */
+    //@SuppressWarnings("unchecked")
+    public static void setValues(Ref [] rs,Object [] v) {
+       for ( int i = 0; i < rs.length; i++ ) {
+           rs[ i ].set( Ref.deref( v[ i ] ) );
+       }
+    }
+
+    /**
+     * Utility method that collects the bindings of given {@link Ref}
+     * objects into a {@link java.util.Vector}.
+     */
+    public static Vector getBinding(Vector/*<Ref>*/ refs) {
+       Vector/*<Object>*/ v = new Vector/*<Object>*/();
+       for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); )
+           v.add( ((Ref) i.next()).get() );
+       return v;
+    }
+
+    /**
+     * Utility method that binds the given {@link Ref} objects with
+     * corresponding values from a {@link java.util.Vector}.
+     */
+    //@SuppressWarnings("unchecked")
+    public static void bind(Vector/*<Ref>*/ refs,Vector v) {
+       int c = 0;
+       for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); )
+           ((Ref) i.next()).set( deref( v.get( c++ ) ) );
+    }
+
+    /**
+     * Utility method to create a vector of ref objects from a string
+     * array of names.
+     */
+    public static Vector/*<Ref>*/ create(String [] names) {
+       Vector/*<Ref>*/ v = new Vector/*<Ref>*/();
+       if ( names == null )
+           return v;
+       for ( int i = 0; i < names.length; i++ ) {
+           v.add( new Ref( names[i] ) );
+       }
+       return v;
+    }
+
+    /**
+     * Utility method that invokes {@link #clear} for the given {@link
+     * Ref} objects with.
+     */
+    public static void clear(Vector/*<Ref>*/ refs) {
+       for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); )
+           ((Ref) i.next()).clear();
+    }
+
+    /**
+     * Creates copies of Ref objects.
+     */
+    //@SuppressWarnings("unchecked")
+    public static Vector/*<Ref>*/ copy(Vector/*<Ref>*/ src) {
+       Vector/*<Ref>*/ refs = new Vector/*<Ref>*/();
+       for ( Iterator/*<Ref>*/ i = src.iterator(); i.hasNext(); )
+           refs.add( new Ref( (Ref) i.next() ) );
+       return refs;
+    }
+
+    /**
+     * Returns a textual representation of a vector of {@link Ref}
+     * objects.
+     */
+    public static String toString(Vector/*<Ref>*/ refs) {
+       return toString( (Ref[]) refs.toArray( new Ref [ refs.size() ] ) );
+    }
+
+    /**
+     * Returns a textual representation of an expected array of Ref
+     * objects. Each Ref object is presented by name and value, using
+     * {@link #toString()}, but the value is clipped to at most 40
+     * characters. The whole things is put within a matching pair of
+     * curly braces.
+     */
+    public static String toString(Object [] refs) {
+       StringBuffer s = new StringBuffer();
+       s.append( "{" );
+       for ( int i = 0; i < refs.length; i++ ) {
+           if ( i > 0 )
+               s.append( "," );
+           if ( refs[ i ] instanceof Ref ) {
+               Ref r = (Ref) refs[ i ];
+               s.append( r.getName() );
+               s.append( "=" );
+               String v = r.toString();
+               if ( v.length() > 40 )
+                   v = v.substring( 0, 37 ) + "...";
+               s.append( v );
+           } else {
+               s.append( refs[ i ] );
+           }
+       }
+       s.append( "}" );
+       return s.toString();
+    }
+
+    /**
+     * Two Ref vectors are equals if they are of same size, and
+     * dereference to the same values in the same order.
+     */
+    public static boolean equals(Vector/*<Ref>*/ a,Vector/*<Ref>*/ b) {
+       if ( a.size() != b.size() )
+           return false;
+       for ( int i = 0; i < a.size(); i++ ) {
+           Object ra = deref( a.get( i ) );
+           Object rb = deref( b.get( i ) );
+           if ( ra == null || rb == null )
+               if ( ra != rb )
+                   return false;
+           if ( ! ra.equals( rb ) )
+               return false;
+       }
+       return true;
+    }
+
+    /**
+     * The name given to the {@link Ref} object at construction.
+     */
+    public String name;
+
+    /**
+     * The value of the {@link Ref} object, or <em>null</em> if not
+     * bound.
+     */
+    private /*T*/ Object value;
+
+    /**
+     * Constructor. Each Ref object is named.
+     */
+    public Ref(String n) {
+       name = n;
+    }
+
+    /**
+     * Constructor with both name and value.
+     */
+    public Ref(String n,/*T*/ Object v) {
+       name = n;
+       value = v;
+    }
+
+    /**
+     * Constructor that copies another Ref object.
+     */
+    public Ref(Ref/*<T>*/ src) {
+       name = src.getName();
+       value = src.get();
+    }
+
+    /**
+     * Finds a same named Ref object in a {@link Vector}.
+     */
+    public Ref find(Vector/*<Ref>*/ refs) {
+       for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); ) {
+           Ref ref = (Ref) i.next();
+           if ( name.equals( ref.name ) )
+               return ref;
+       }
+       return null;
+    }
+
+    /**
+     * Returns the value. This is <em>null</em> unless bound to
+     * something.
+     */
+    public /*T*/ Object get() {
+       return value;
+    }
+
+    /**
+     * Accessor method for the name.
+     */
+    public String getName() {
+       return name == null? "Ref#" + System.identityHashCode( this ) : name;
+    }
+
+    /**
+     * Assigns the value.
+     */
+    public void set(/*T*/ Object v) {
+       value = v;
+    }
+
+    /**
+     * Resets its value to null.
+     */
+    public void clear() {
+       value = null;
+    }
+
+    /**
+     * Returns a String representation of this {@link Ref} object. The
+     * object is transparent, and it presents itself only via its
+     * value. Use {@link #getName} to present the {@link Ref} object.
+     */
+    public String toString() {
+       return "" + value;
+    }
+
+}
diff --git a/com/intendico/data/Relation.java b/com/intendico/data/Relation.java
new file mode 100644 (file)
index 0000000..18922f4
--- /dev/null
@@ -0,0 +1,1160 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * A Relation is a set of tuples of the same size. It is created by
+ * enumerating the types of its columns.
+ *
+ * <p> A relation may have one or more <em>key constraints</em>. A key
+ * constraint combines one or more fields into a <em>key</em>, which
+ * the relation may hold only one tuple for any combination of
+ * values. The key constraints are used by the {@link #add} method,
+ * such that addition of a new tuple may cause the removal of prior
+ * tuples that are equal in some key combination of fields.
+ *
+ * <p> A relation is modified via the {@link #add} and {@link #remove}
+ * methods. Both of these notify observers if the relation is changed.
+ *
+ * <p> A relation can be queried via the {@link #query} method, which
+ * returns an {@link java.util.Iterator} for the matching tuples, or
+ * by the {@link #get} method, which instead returns a {@link
+ * Relation.Cursor} that implements the {@link Query} interface.
+ */
+public class Relation extends Observable implements Store, Inquirable {
+
+    /**
+     * Utility method to create a copy of an object array. Only the
+     * array is copied, not its values.
+     */
+    public static Object [] copyArray(Object[] in)
+    {
+       Object [] out = new Object [ in.length ];
+       System.arraycopy( in, 0, out, 0, in.length);
+       return out;
+    }
+
+    /**
+     * An instance name for this relation.
+     */
+    public final String name;
+
+    /**
+     * The required field types.
+     */
+    public final Class [] column_types;
+
+    /**
+     * The special key pattern for all fields being key.
+     */
+    public final KeyPattern main_key;
+
+    /**
+     * Holds the relation data in a layered structure. First hash
+     * index is the key pattern, and second hash index is the key (of
+     * a pattern). Actual data is a HashSet of Tuple objects, which
+     * are compared by using {@link java.lang.Object#equals} of their
+     * fields.
+     */
+    public Hashtable/*<KeyPattern,Hashtable<Tuple,HashSet<Tuple>>>*/ indexes =
+       new Hashtable/*<KeyPattern,Hashtable<Tuple,HashSet<Tuple>>>*/();
+
+    /**
+     * Holds the collection of constraining key patterns. When a tuple
+     * is added, any already existing tuple of the same key (according
+     * to any constraining key pattern) is seen as an opposing tuple,
+     * which is removed (without observer notification).
+     */
+    public HashSet/*<KeyPattern>*/ constraints;
+
+    /**
+     * The ArityException is thrown when a relation is accessed with a
+     * wrong number of fields.
+     */
+    public class ArityException extends Exception {
+
+       /**
+        * Version identity required for serialization.
+        */
+       public static final long serialVersionUID = 1L;
+
+       /**
+        * Telling the required number of fields.
+        */
+       public final int requires;
+
+       /**
+        * Telling the given number of fields.
+        */
+       public final int attempted;
+
+       /**
+        * Constructor.
+        */
+       public ArityException(int r,int a) {
+           requires = r;
+           attempted = a;
+       }
+    }
+
+    /**
+     * The ColumnTypeException exception is thrown when a realtion is
+     * accessed by means of wrong field types.
+     */
+    public class ColumnTypeException extends Exception {
+
+       /**
+        * Version identity required for serialization.
+        */
+       public static final long serialVersionUID = 1L;
+
+       /**
+        * The required type of the first erroneous field.
+        */
+       public final Class requires;
+
+       /**
+        * The given type of the first erroneous field.
+        */
+       public final Class attempted;
+
+       /**
+        * The field index.
+        */
+       public final int index;
+
+       /**
+        * Constructor.
+        */
+       public ColumnTypeException(int i,Class r,Class a) {
+            requires = r;
+            attempted = a;
+           index = i;
+       }
+    }
+
+    /**
+     * Constructor. This defines both the relation arity and the
+     * individual types of the fields.
+     */
+    public Relation(String n,Class/*...*/ [] types) {
+       name = n;
+       column_types = types;
+       main_key = new KeyPattern();
+    }
+
+    /**
+     * Alternative constructor with name and arity only.
+     */
+    public Relation(String n,int arity) {
+       name = n;
+       column_types = new Class [ arity ];
+       for ( int i = 0; i < arity; i++ ) {
+           column_types[ i ] = Object.class;
+       }
+       main_key = new KeyPattern();
+    }
+
+    /**
+     * Clears the relation, i.e. removes all its tuples. Its key
+     * constraints are unchanged.
+     */
+    public void clear() {
+       indexes =
+           new Hashtable/*<KeyPattern,Hashtable<Tuple,HashSet<Tuple>>>*/();
+       setChanged();
+       notifyObservers();
+    }
+
+    /**
+     * Returning an Observable that notifies about subsequent changes
+     * to this Relation.
+     */
+    public Observable getMonitor() {
+       return this;
+    }
+
+    /**
+     * Adds a constraining key pattern. The relation is managed to
+     * allow at most one tuple of any key of the constraining key
+     * patterns. This is enforced when new tuples are added, which
+     * thus results in that a prior tuple of the same key is removed
+     * (without observer notification).
+     *
+     * <p> Note that a relation that violates the key constraint when
+     * the constraint is added will not be corrected until an opposing
+     * tuple is added. It does make good sense to add all key
+     * constraints before any tuples are added.
+     */
+    public Relation addConstraint(boolean/*...*/ [] pattern)
+       throws ArityException
+    {
+       if ( constraints == null )
+           constraints = new HashSet/*<KeyPattern>*/();
+       constraints.add( new KeyPattern( pattern ) );
+       return this;
+    }
+
+    /**
+     * Removes a constraining key pattern.
+     * 
+     * <p> Note that the general advice is <em>aginst</em> removing
+     * key constraints, but rather creating a new relation with the
+     * reduced set of constraints and then populate that.
+     */
+    public void removeConstraint(boolean/*...*/ [] pattern)
+       throws ArityException
+    {
+       if ( constraints == null )
+           return;
+       constraints.remove( new KeyPattern( pattern ) );
+       if ( constraints.size() == 0 )
+           constraints = null;
+    }
+
+    /**
+     * Utility method to remove all tuples opposed to a given tuple
+     * according to a given key pattern. Tuples are removed without
+     * observer notification.
+     */
+    public void applyConstraint(KeyPattern constraint,Tuple adding)
+       throws ArityException, ColumnTypeException
+    {
+       Tuple key = constraint.getKey( adding );
+       for ( Iterator/*<Tuple>*/ i = query( key ); i.hasNext(); ) {
+           addPending( (Tuple) i.next() );
+       }
+       processPending( false );
+    }
+
+    /**
+     * Utility method to remove all tuples that are opposite to the
+     * given tuple with respect to the constraining key patterns.
+     * Tuples are removed without observer notification.
+     */
+    public void applyConstraints(Tuple adding)
+       throws ArityException, ColumnTypeException
+    {
+       if ( constraints == null )
+           return;
+       for ( Iterator/*<KeyPattern>*/ i =
+                 constraints.iterator(); i.hasNext(); ) {
+           applyConstraint( (KeyPattern) i.next(), adding );
+       }
+    }
+
+    /**
+     * Utility method that investigates that fields are appropriate
+     * for the relation. Any violations are reported by throwing the
+     * appropriate exception.
+     * @throws ArityException if there is a wrong number of fields.
+     * @throws ColumnTypeException if any field is of wrong type.
+     */
+    public void runtimeTyping(Object/*...*/ [] fields)
+       throws ArityException, ColumnTypeException
+    {
+       if ( column_types == null ) {
+           if ( fields == null )
+               return;
+           throw new ArityException( 0, fields.length );
+       }
+
+       if ( fields == null )
+           throw new ArityException( column_types.length, 0 );
+
+       if ( fields.length != column_types.length )
+           throw new ArityException( column_types.length, fields.length );
+
+       for ( int i = 0; i < fields.length; i++ ) {
+           Object x = Ref.deref( fields[ i ] );
+           if ( x == null )
+               continue;
+           if ( column_types[ i ].isInstance( x ) )
+               continue;
+           throw new ColumnTypeException(
+               i, column_types[ i ], x.getClass() );
+       }
+    }
+
+    
+    /**
+     * Represents an I/O key pattern for the relation. This is used
+     * for identifying keyed access tables.
+     */
+    public class KeyPattern {
+
+       /**
+        * The I/O pattern expressed as a boolean array. A true means
+        * input, and a false means output.
+        */
+       boolean [] pattern;
+
+       /**
+        * Constructor by boolean indicators. True indicates an input
+        * field, and false indicates an output field.
+        */
+       public KeyPattern(boolean/*...*/ [] io)
+           throws ArityException
+       {
+           if ( io.length != column_types.length )
+               throw new ArityException( column_types.length, io.length );
+           pattern = io;
+       }
+
+       /**
+        * Default constructor, with all fields key fields.
+        */
+       public KeyPattern() {
+           pattern = new boolean[ column_types.length ];
+           for ( int i = 0; i < pattern.length; i++ )
+               pattern[ i ] = false;
+       }
+
+       /**
+        * Constructor by field presence. The constructor is called
+        * with the right number of fields, leaving output fields as
+        * null and input fields non-null.
+        */
+       public KeyPattern(Object/*...*/ [] io)
+           throws ArityException
+       {
+           if ( io.length != column_types.length )
+               throw new ArityException( column_types.length, io.length );
+           // check types as well?
+           pattern = new boolean [ io.length ];
+           for ( int i = 0; i < io.length; i++ )
+               pattern[ i ] = Ref.deref( io[ i ] ) != null;
+       }
+
+       /**
+        * Equality of KeyPattern objects is determined by comparing
+        * their boolean patterns.
+        */
+       public boolean equals(Object p) {
+           return p instanceof KeyPattern ? equals( (KeyPattern) p ) : false ;
+       }
+
+       /**
+        * Equality of KeyPattern objects is determined by comparing
+        * their boolean patterns.
+        */
+       public boolean equals(KeyPattern p) {
+           if ( pattern == null || p.pattern == null )
+               return pattern == null && p.pattern == null;
+           if ( pattern.length != p.pattern.length )
+               return false;
+           for ( int i = 0; i < pattern.length; i++ )
+               if ( pattern[ i ] != p.pattern[ i ] )
+                   return false;
+           return true;
+       }
+
+       /**
+        * The hashCode of a KeyPattern is derived by treating its
+        * boolean pattern as a bit pattern.
+        */
+       public int hashCode() {
+           int code = 0;
+           for ( int i = 0; i < pattern.length; i++ )
+               code = ( code << 1 ) + ( pattern[ i ]? 1 : 0 );
+           return code;
+       }
+
+       /**
+        * Returns a tuple by projecting the given tuple through this
+        * key pattern.
+        */
+       public Tuple getKey(Tuple t) {
+           return new Tuple( t, pattern );
+       }
+
+       /**
+        * Returns a String representation of the KeyPattern object.
+        */
+       public String toString() {
+           StringBuffer s = new StringBuffer();
+           String sep = "";
+            for ( int i = 0; i < pattern.length; i++ ) {
+               s.append( sep );
+               s.append( pattern[ i ] );
+               sep = ",";
+           }
+           return "KeyPattern(" + s.toString() +")";
+       }
+    }
+
+    /**
+     * Representation of relation row. In a contents row, all fields
+     * of a tuple should be non-<em>null</em>. Tuples that represent
+     * access patterns typically have <em>null</em>-valued {@link Ref}
+     * objects, or are <em>null</em> directly, for the output fields,
+     * whereas the input fields provide values to match with.
+     */
+    public class Tuple {
+       
+       /**
+        * Holds the field values.
+        */
+       public Object [] fields;
+
+       /**
+        * Constructor.
+        */
+       public Tuple() {
+           fields = new Object[ column_types.length ];
+       }
+
+       /**
+        * Constructor.
+        */
+       public Tuple(Object/*...*/ [] tuple) {
+           fields = copyArray( tuple );
+       }
+
+       /**
+        * Returns the number of <em>null</em> fields of this tuple.
+        */
+       public int nullCount() {
+           int x = 0;
+           for (int i = 0; i < fields.length; i++) {
+               if ( Ref.deref( fields[ i ] ) == null )
+                   x += 1;
+           }
+           return x;
+       }
+
+       /**
+        * Key constructor. Creates a copy of the given tuple, but
+        * clears the output fields, as given by the key pattern.
+        */
+       public Tuple(Tuple t,boolean [] pattern) {
+           this();
+           for (int i = 0; i < fields.length; i++) {
+               if ( pattern[ i ] )
+                   fields[ i ] = Ref.deref( t.fields[ i ] );
+               else if ( fields[ i ] instanceof Ref )
+                   ((Ref) fields[ i ]).clear();
+           }
+       }
+
+       /**
+        * Utility method to clear this tuple according to the key
+        * pattern.
+        */
+       public void clear(boolean [] pattern) {
+           for ( int i = 0; i < pattern.length; i++ ) {
+               if ( pattern[ i ] )
+                   continue;
+               if ( fields[ i ] instanceof Ref )
+                   ((Ref) fields[ i ] ).clear();
+               else 
+                   fields[ i ] = null;
+           }
+       }
+
+       /**
+        * Determine whether a given query matches this tuple, by
+        * comparing all non-null query fields with the tuple fields
+        * using the equals method.
+        *
+        * @return <em>true</em> if this tuple matches the given query
+        * tuple when ignoring its <em>null</em> fields, and
+        * <em>false</em> otherwise.
+        */
+       public boolean match(Tuple query) {
+           for ( int i = 0; i < fields.length; i++ ) {
+               Object x = Ref.deref( query.fields[ i ] );
+               if ( x == null )
+                   continue;
+               if ( ! x.equals( Ref.deref( fields[ i ] ) ) )
+                   return false;
+           }
+           return true;
+       }
+
+       /**
+        * Fill in the fields of a query from this tuple using the
+        * given key pattern.
+        */
+       //@SuppressWarnings("unchecked")
+       public void getFields(KeyPattern kp, Object [] output) {
+           for ( int i = 0; i < fields.length; i++ ) {
+               if ( ! kp.pattern[ i ] ) {
+                   if ( output[ i ] instanceof Ref )
+                       ((Ref) output[ i ]).set( fields[ i ] );
+                   else
+                       output[ i ] = fields[ i ];
+               }
+           }
+       }
+
+       /**
+        * Returns a given field value.
+        */
+       public Object getField(int index) {
+           return fields[ index ];
+       }
+
+       /**
+        * Determine whether this tuple is equal to a given object.
+        */
+       public boolean equals(Object x) {
+           return x instanceof Tuple? equals( (Tuple) x ) : false;
+       }
+
+       /**
+        * Determine whether this tuple is equal to another. The
+        * method dereferences any {@link Ref} objects of both this
+        * tuple and the other tuple.
+        *
+        * @return <em>true</em> if all field values (after
+        * dereference) are equal, and <em>false</em> otherwise.
+        */
+       public boolean equals(Tuple x) {
+           for ( int i = 0; i < fields.length; i++ ) {
+               if ( ! equalFields(
+                        Ref.deref( fields[ i ] ),
+                        Ref.deref( x.fields[ i ] ) ) )
+                   return false;
+           }
+           return true;
+       }
+
+       /**
+        * Utility method to compare field values that may be
+        * <em>null</em>.
+        *
+        * @return <em>true</em> if the field values are equal, and
+        * <em>false</em> otherwise.
+        */
+       public boolean equalFields(Object a,Object b) {
+           return ( a == null || b == null )? a == b : a.equals( b );
+       }
+
+       /**
+        * Compute a tuple hash code, by combining the dereferenced
+        * field values in a deterministic way.
+        *
+        * <p> Note that tuples may have the same hash code without
+        * being equal.
+        *
+        * @return the hash code.
+        */
+       public int hashCode() {
+           int x = 0;
+           for ( int i = 0; i < fields.length; i++ ) {
+               x <<= 2 ;
+               x += ( Ref.deref( fields[ i ] ) == null?
+                      0 :
+                      Ref.deref( fields[ i ] ).hashCode() );
+           }
+           return x;
+       }
+
+       /**
+        * Support method to queue up this tuple for removal from its
+        * relation. Use method {@link #processPending} to process
+        * delayed removals.
+        *
+        * <p> This method is intended to simplify the combination of
+        * iterating over several tuples of the relation while
+        * removing some of them, without this causing problems due to
+        * {@link java.util.ConcurrentModificationException} being
+        * thrown. (Please refer to notes about {@link
+        * java.util.HashSet}).
+        */
+       public void delayedRemove() {
+           addPending( this );
+       }
+
+       /**
+        * Returns a String representation of this tuple.
+        */
+       public String toString() {
+           StringBuffer s = new StringBuffer("<");
+           for ( int i = 0; i < fields.length; i++ ) {
+               if ( i > 0 )
+                   s.append( "," );
+               s.append( fields[ i ] == null? "null" : fields[ i ].toString() );
+           }
+           s.append( ">" );
+           return s.toString();
+       }
+
+       /**
+        * Utility method to collect Ref objects in the fields.
+        */
+       public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+           for ( int i = 0; i < fields.length; i++ ) {
+               if ( fields[ i ] instanceof Ref ) {
+                   Ref r = (Ref) fields[ i ];
+                   if ( ! v.contains( r ) ) 
+                       v.add( r );
+               }
+           }
+           return v;
+       }
+
+    }
+
+    /**
+     * Utility method for accessing the data table of a key pattern
+     * and keying tuple.
+     */
+    public HashSet/*<Tuple>*/ getData(KeyPattern kp,Tuple tuple,boolean add) {
+        Hashtable/*<Tuple,HashSet<Tuple>>*/ table =
+           (Hashtable/*<Tuple,HashSet<Tuple>>*/) indexes.get( kp );
+       if ( table == null ) {
+           table = new Hashtable/*<Tuple,HashSet<Tuple>>*/();
+           indexes.put( kp, table );
+       }
+       Tuple key = kp.getKey( tuple );
+        HashSet/*<Tuple>*/ data = (HashSet/*<Tuple>*/) table.get( key );
+       if ( add && data == null ) {
+           data = new HashSet/*<Tuple>*/();
+           table.put( key, data );
+       }
+       return data;
+    }
+    
+    /**
+     * Utility method to obtain an iterator over existing key
+     * patterns.
+     */
+    public Iterator/*<KeyPattern>*/ keyPatterns() {
+       return indexes.keySet().iterator();
+    }
+
+    /**
+     * Access method to add a tuple to the relation. Returns false if
+     * the relation already contains the tuple. Otherwise all tables
+     * are updated, and the method returns true.
+     *
+     * <p> Note that all values a dereferenced before being added to
+     * the relation.
+     *
+     * @return <em>true</em> if the relation is changed, and
+     * <em>false</em> otherwise.
+     *
+     * @see #runtimeTyping
+     * @see #remove
+     */
+    synchronized public boolean add(Object/*...*/ [] values)
+       throws ArityException, ColumnTypeException
+    {
+       runtimeTyping( values );
+
+       for ( int i=0; i < values.length; i++ )
+           values[ i ] = Ref.deref( values[ i ] );
+
+       Tuple tuple = new Tuple( values );
+
+       HashSet/*<Tuple>*/ data = getData( main_key, tuple, true );
+       if ( data.contains( tuple ) ) {
+           return false;
+       }
+       
+       applyConstraints( tuple );
+
+       for (Iterator/*<KeyPattern>*/ kpi = keyPatterns(); kpi.hasNext(); ) {
+           getData( (KeyPattern) kpi.next(), tuple, true ).add( tuple );
+       }
+
+       setChanged();
+       notifyObservers();
+       return true;
+    }
+
+    /**
+     * Access method to remove a tuple from the relation. Returns
+     * false if the tuple is missing from the relation. Otherwise all
+     * tables are updated, and the method returns true.
+     *
+     * @see #runtimeTyping
+     * @see #add
+     */
+    synchronized public boolean remove(Object/*...*/ [] values)
+       throws ArityException, ColumnTypeException
+    {
+       if ( removeQuietly( values ) ) {
+           setChanged();
+           notifyObservers();
+           return true;
+       }
+       return false;
+    }
+
+    /**
+     * Internal working method to remove tuples without notifying any
+     * observer. The remove function is split up in this way in order
+     * to allow the {@link #add} method to apply the key constraints
+     * without notifications of key constraint knock-outs.
+     */
+    public boolean removeQuietly(Object/*...*/ [] values)
+       throws ArityException, ColumnTypeException
+    {
+       runtimeTyping( values );
+       Tuple tuple = new Tuple( values );
+
+       HashSet/*<Tuple>*/ data = getData( main_key, tuple, true );
+       if ( !data.contains( tuple ) )
+           return false;
+
+       for (Iterator/*<KeyPattern>*/ kpi = keyPatterns(); kpi.hasNext(); ) {
+           getData( (KeyPattern) kpi.next(), tuple, true ).remove( tuple );
+       }
+       return true;
+    }
+
+    /**
+     * Returns the size of the relation, i.e., the number of key
+     * tuples in the {@link #main_key} collection.
+     */
+    synchronized public int size() {
+        Hashtable/*<Tuple,HashSet<Tuple>>*/ table =
+           (Hashtable/*<Tuple,HashSet<Tuple>>*/) indexes.get( main_key );
+       return table == null? 0 : table.size();
+    }
+
+    /**
+     * Returns the projected size of the relation, i.e., the number of
+     * tuples matching to the tuple of the given values, with null
+     * values (or unbound Ref objects) indicating non-key columns.
+     */
+    public int size(Object/*...*/ [] values) {
+       return size( new Tuple( values ) );
+    }
+    
+    /**
+     * Returns the projected size of the relation, i.e., the number of
+     * tuples matching to the given key tuple.
+     */
+    synchronized public int size(Tuple key) {
+       try {
+           KeyPattern kp = new KeyPattern( key.fields );
+           if ( indexes.get( kp ) == null ) {
+               return 0;
+           }
+           HashSet/*<Tuple>*/ data = getData( kp, key, false );
+           return data == null? 0 : data.size();
+       } catch (Exception e) {
+           e.printStackTrace();
+           return 0;
+       }
+    }
+
+    /**
+     * Utility method to populate a HashSet matching to a key
+     */
+    public void populate(KeyPattern kp)
+    {
+       Hashtable/*<Tuple,HashSet<Tuple>>*/ table =
+           new Hashtable/*<Tuple,HashSet<Tuple>>*/();
+       indexes.put( kp, table );
+        HashSet/*<Tuple>*/ db = getData( main_key, new Tuple(), true );
+       for ( Iterator/*<Tuple>*/ i = db.iterator(); i.hasNext(); ) {
+           Tuple t = (Tuple) i.next();
+           HashSet/*<Tuple>*/ data = getData( kp, kp.getKey( t ), true );
+           data.add( t );
+       }
+    }
+
+    /**
+     * Utility method to obtain an Iterator for the tuple set that
+     * matches the given key values.
+     */
+    public Iterator/*<Tuple>*/ query(Object/*...*/ [] values)
+       throws ArityException, ColumnTypeException
+    {
+        runtimeTyping( values );
+        Tuple key = new Tuple( values );
+       return query( key );
+    }
+
+    /**
+     * Utility method to obtain an Iterator for the tuple set that
+     * matches the given key tuple.
+     */
+    synchronized public Iterator/*<Tuple>*/ query(Tuple key)
+        throws ArityException, ColumnTypeException
+    {
+       KeyPattern kp = new KeyPattern( key.fields );
+       if ( indexes.get( kp ) == null ) {
+           populate( kp );
+       }
+       HashSet/*<Tuple>*/ data = getData( kp, key, true );
+       return data.iterator();
+    }
+
+    /**
+     * The Cursor class provides Query processing support for a
+     * relation.
+     */
+    public class Cursor implements Query {
+
+       /**
+        * A {@link Relation.Tuple} object that is updated for new
+        * mathing values.
+        */
+       public Tuple output;
+
+       /**
+        * The querying key pattern.
+        */
+       public KeyPattern key_pattern;
+
+       /**
+        * The result iterator.
+        */
+       public Iterator/*<Tuple>*/ current;
+
+       /**
+        * Constructor for key values. Output fields are marked by
+        * {@link Ref} objects of <em>null</em> values, and ignored
+        * fields are marked by <em>null</em>.
+        */
+       public Cursor(Object/*...*/ [] values)
+           throws ArityException, ColumnTypeException
+       {
+           output = new Tuple( values );
+           key_pattern = new KeyPattern( output.fields );
+           reset();
+       }
+
+       /**
+        * The {@link Query#copy} method implemented by creating a new
+        * Cursor with new {@link Ref} objects.
+        */
+       //@SuppressWarnings("unchecked")
+       public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+           Object [] fields = copyArray( output.fields );
+           for ( int i = 0; i < fields.length; i++ ) {
+               if ( fields[ i ] instanceof Ref ) {
+                   fields[ i ] = ((Ref) fields[ i ]).find( newrefs );
+               }
+           }
+           return new Cursor( fields );
+       }
+
+       /**
+        * The {@link Query#reset} method implemented by recomputing
+        * the key pattern and the matching tuple set.
+        */
+       public void reset()
+           throws ArityException, ColumnTypeException
+       {
+           key_pattern = new KeyPattern( output.fields );
+           current = query( key_pattern.getKey( output ) );
+       }
+
+       /**
+        * The {@link Query#next} method implemented by binding the
+        * output {@link Ref} objects for the next match.
+        */
+       public boolean next() {
+           if ( ! current.hasNext() ) {
+               output.clear( key_pattern.pattern );
+               return false;
+           }
+           ((Tuple) current.next()).getFields( key_pattern, output.fields );
+           return true;
+       }
+
+       /**
+        * The {@link Query#getRefs} method implemented by returning the
+        * {@link Ref} objects given to this Cursor object.
+        */
+       public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+           return output.getRefs( v );
+       }
+
+       /**
+        * The {@link Query#addObserver} method implemented by adding
+        * the observer to the relation.
+        */
+       public void addObserver(Observer x) {
+           Relation.this.addObserver( x );
+       }
+
+       /**
+        * The {@link Query#deleteObserver} method implemented by
+        * removing the observer from the relation.
+        */
+       public void deleteObserver(Observer x) {
+           Relation.this.deleteObserver( x );
+       }
+
+       /**
+        * Implements the {@link Query#addable} method by verifying
+        * that all {@link Ref} objects are non-<em>null</em>.
+        */
+       public boolean addable()
+       {
+           return output.nullCount() == 0;
+       }
+
+       /**
+        * The {@link Query#add} method implemented by adding to
+        * current output tuple to the relation, provided that all its
+        * {@link Ref} objects are non-<em>null</em>.
+        */
+       public boolean add()
+       {
+           try {
+               if ( output.nullCount() == 0 ) {
+                   Object [] fields = new Object [ output.fields.length ];
+                   System.arraycopy(
+                       output.fields, 0, fields, 0, fields.length );
+                   return Relation.this.add( fields );
+               }
+           } catch (Exception e) {
+               e.printStackTrace();
+               System.err.println( "** ignored **" );
+           }
+           return false;
+       }
+
+       /**
+        * Implements {@link Query#removable} by checking that the
+        * indicated tuple is stored.
+        */
+       public boolean removable() {
+           HashSet/*<Tuple>*/ data = getData( main_key, output, true );
+           return data.contains( output );
+       }
+
+       /**
+        * Implements {@link Query#remove} by removing the indicated
+        * tuple. 
+        *
+        * @return <em>true</em> if tuple was removed, and
+        * <em>false</em> otherwise.
+        */
+       public boolean remove() {
+           try {
+               return Relation.this.remove( output.fields );
+           } catch (Exception e) {
+               e.printStackTrace();
+               return false;
+           }
+       }
+       
+       /**
+        * Returns a {link java.lang.String} representation of this
+        * Cursor. This uses the {@link Relation#name} field as well
+        * as the {@link Ref#name} field of any {@link Ref} object
+        * involved (both input and output).
+        */
+       public String toString() {
+           StringBuffer s = new StringBuffer();
+           s.append( Relation.this.name );
+           s.append( "(" );
+           for ( int i = 0; i < output.fields.length; i++ ) {
+               if ( i > 0 )
+                   s.append( "," );
+               s.append( " " );
+               if ( output.fields[ i ] instanceof Ref ) {
+                   s.append( ((Ref) output.fields[ i ] ).getName() );
+                   s.append( "=" );
+               }
+               s.append( output.fields[ i ] );
+           }
+           s.append( " )" );
+           return s.toString();
+       }
+
+    }
+
+    /**
+     * An interface method to obtain a {@link Query} implementation
+     * object, {@link Relation.Cursor} for given key. The output
+     * fields would be marked by <em>null</em> valued {@link Ref}
+     * objects, which get successively assigned for the matching
+     * tuples.
+     */
+    public Query get(Object/*...*/ [] values) throws Exception {
+       return new Cursor( values );
+    }
+
+    /**
+     * Interface method to obtain the Inquirable name.
+     */
+    public String getName() {
+       return name;
+    }
+
+    //
+    // Pending removal support
+    //
+
+    /**
+     * Transient support for delayed removal of tuples.
+     */
+    public HashSet/*<Tuple>*/ pending;
+
+    /**
+     * Adds a tuple to the pending table.
+     *
+     * @see Relation.Tuple#delayedRemove
+     */
+    synchronized public void addPending(Tuple t) {
+       if ( pending == null )
+           pending = new HashSet/*<Tuple>*/();
+       pending.add( t );
+    }
+
+    /**
+     * Utility method to remove all pending tuples. Invokes {@link
+     * #processPending(boolean)} with argument <em>true</em>.
+     * @return <em>true</em> if the relation is changed, and
+     *<em>false</em> otherwise.
+     *
+     * @return <em>true</em> if the relation is changed, and
+     * <em>false</em> otherwise.
+     *
+     * @see #processPending(boolean)
+     */
+    synchronized public boolean processPending()
+    {
+       return processPending( true );
+    }
+
+    /**
+     * Utility method to remove all pending tuples. The noise argument
+     * marks whether (<em>true</em>) or not (<em>false</em>) to notify
+     * observers if the relation is changed.
+     *
+     * @return <em>true</em> if the relation is changed, and
+     *<em>false</em> otherwise.
+     *
+     * @see #removeQuietly
+     */
+    synchronized public boolean processPending(boolean noise)
+    {
+       if ( pending == null )
+           return false;
+       boolean removed = false;
+       for ( Iterator/*<Tuple>*/ i = pending.iterator(); i.hasNext(); ) {
+           try {
+               removed |= removeQuietly( ((Tuple) i.next()).fields );
+           } catch (Exception e) {
+               System.err.println( "processPending: " + e.toString() );
+           }
+       }
+       pending = null;
+       if ( noise && removed ) {
+           setChanged();
+           notifyObservers();
+       }
+       return removed;
+    }
+
+    ///
+    /// Convenience methods
+    ///
+
+    /**
+     * Convenience method for unary relation key constraint setting.
+     */
+    public void addConstraint(boolean x)
+       throws ArityException {
+       addConstraint( new boolean [] { x } );
+    }
+
+    /**
+     * Convenience method for unary relation assertion.
+     */
+    public boolean add(Object x)
+       throws ArityException, ColumnTypeException {
+       return add( new Object [] { x } );
+    }
+       
+    /**
+     * Convenience method for unary relation retraction.
+     */
+    public boolean remove(Object x)
+       throws ArityException, ColumnTypeException {
+       return remove( new Object [] { x } );
+    }
+
+
+    /**
+     * Convenience method for binary relation key constraint setting.
+     */
+    public void addConstraint(boolean x1,boolean x2)
+       throws ArityException {
+       addConstraint( new boolean [] { x1, x2 } );
+    }
+
+    /**
+     * Convenience method for binary relation assertion.
+     */
+    public boolean add(Object x1,Object x2)
+       throws ArityException, ColumnTypeException {
+       return add( new Object [] { x1, x2 } );
+    }
+       
+    /**
+     * Convenience method for binary relation retraction.
+     */
+    public boolean remove(Object x1,Object x2)
+       throws ArityException, ColumnTypeException {
+       return remove( new Object [] { x1, x2 } );
+    }
+
+
+    /**
+     * Convenience method for tertiary relation key constraint setting.
+     */
+    public void addConstraint(boolean x1,boolean x2,boolean x3)
+       throws ArityException {
+       addConstraint( new boolean [] { x1, x2, x3 } );
+    }
+
+    /**
+     * Convenience method for tertiary relation assertion.
+     */
+    public boolean add(Object x1,Object x2,Object x3)
+       throws ArityException, ColumnTypeException {
+       return add( new Object [] { x1, x2, x3 } );
+    }
+       
+    /**
+     * Convenience method for tertiary relation retraction.
+     */
+    public boolean remove(Object x1,Object x2,Object x3)
+       throws ArityException, ColumnTypeException {
+       return remove( new Object [] { x1, x2, x3 } );
+    }
+
+    /**
+     * Returns a String representation of this relation.
+     */
+    public String toString() {
+       return indexes.toString();
+    }
+}
diff --git a/com/intendico/data/Rule.java b/com/intendico/data/Rule.java
new file mode 100644 (file)
index 0000000..b503e43
--- /dev/null
@@ -0,0 +1,89 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.ConcurrentModificationException;
+
+/**
+ * The Rule class defines a belief propagation relationship
+ * between a pair of {@link Query} objects that share {@link Ref}
+ * objects. The Rule provides a forward directed implication such
+ * that when the antecedent comes true for a binding, then the
+ * consequent is made true for that binding.
+ *
+ * <p> A Rule object is operated by first calling {@link #reset},
+ * which results in a {link Snapshot#reset} that collects all new
+ * bindings for the antecedent {@link Query}. The second step is a
+ * subsequent call to {@link #propagate}, which results in calls to
+ * {@link Query#add} on the consequent {@link Query} with bindings as
+ * given by the antecedent snapshot.
+ */
+public class Rule {
+
+    /**
+     * The rule antecedent.
+     */
+    public Snapshot antecedent;
+
+    /**
+     * The rule consequent.
+     */
+    public Query consequent;
+
+    /**
+     * Constructor.
+     */
+    public Rule(Query a,Query c) {
+       if ( a != null )
+           antecedent = new Snapshot( a );
+       consequent = c;
+    }
+
+    /**
+     * Invoked to restart the Rule from the current belief state.
+     */
+    public void reset() throws Exception {
+       try {
+           antecedent.reset();
+       } catch (ConcurrentModificationException e) {
+           System.err.println( "Rule: " + this );
+           throw e;
+       }
+    }
+
+    /**
+     * Invoked to propagate antecedent bindings into consequent
+     * additions.
+     */
+    public void propagate() throws Exception {
+       while ( antecedent.next() ) {
+           consequent.reset();
+           if ( ! consequent.next() )
+               consequent.add();
+       }
+    }
+
+    /**
+     * Returns a textual representation of a Rule.
+     */
+    public String toString() {
+       return antecedent.query + " => " + consequent;
+    }
+}
diff --git a/com/intendico/data/RuleSet.java b/com/intendico/data/RuleSet.java
new file mode 100644 (file)
index 0000000..799690f
--- /dev/null
@@ -0,0 +1,243 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Vector;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * A RuleSet is a reason maintenance unit, which attempts to uphold
+ * given rules. In normal use, the RuleSet is populated with some
+ * number of Rule objects, via either {@link #add(Rule)} or {@link
+ * #add(Query,Query)}, which tie the {@link Rule} objects to the
+ * RuleSet. Each {@link Rule} object, qua {@link Observer}, gets
+ * notified about possible changes by their antecedent source , which
+ * result in them invoking the {@link #activate} method. The RuleSet
+ * {@link #run} method then pops and processes all activated rules to
+ * achieve their belief propagation effects.
+ */
+public class RuleSet extends Observable implements Runnable {
+
+    /**
+     * Utility class to observe source update
+     */
+    class RuleObserver implements Observer {
+
+       /**
+        * The {@link Rule} concerned.
+        */
+       Rule rule;
+
+       /**
+        * Constructor.
+        */
+       RuleObserver(Rule r) {
+           rule = r ;
+       }
+
+       /**
+        * Implements {@link Observer#update} by activating the rule
+        * concerned. However, if the {@link Rule} is not contained in
+        * {@link #all_rules} then this observer just deletes itself
+        * from the calling {@link Observable}.
+        */
+       public void update(Observable x,Object y) {
+           if ( ! all_rules.contains( rule ) ) {
+               x.deleteObserver( this );
+           } else {
+               activate( rule );
+           }
+       }
+
+       /**
+        * Returns a String representation of this object.
+        */
+       public String toString() {
+           return "RuleObserver: " + rule;
+       }
+    }
+
+    /**
+     * The collection of {@link Rule} objects explicitly added to this
+     * RuleSet.
+     */
+    public HashSet/*<Rule>*/ all_rules = new HashSet/*<Rule>*/();
+
+    /**
+     * The current set of activated rules.
+     */
+    public HashSet/*<Rule>*/ active = new HashSet/*<Rule>*/();
+
+    /**
+     * Utility method to activate a {@link Rule} in this RuleSet. This
+     * includes adding the {@link Rule} to the {@link #active}
+     * collection, then both {@link Object#notify} on that object, and
+     * invoke {@link Observable#notifyObservers} to signal that this
+     * RuleSet has activated rules.
+     * @see #run
+     */
+    public void activate(Rule r) {
+       synchronized ( active ) {
+           if ( ! active.contains( r ) ) {
+               active.add( r );
+               active.notifyAll();
+               setChanged();
+               notifyObservers();
+           }
+       }
+    }
+
+    /**
+     * Returns true if there are any activated rules.
+     */
+    public boolean hasActive() {
+       return active.size() > 0;
+    }
+
+    /**
+     * This is a flag to signal that the run method should keep
+     * waiting for next update rather than returning.
+     */
+    public boolean wait = false;
+
+    /**
+     * The reset method calls {@link Rule#reset} on all given rules so
+     * as to reset their antecedent snapshots.
+     */
+    public void reset(Vector/*<Rule>*/ rules) throws Exception {
+       for ( Iterator/*<Rule>*/ i = rules.iterator(); i.hasNext(); ) {
+           Rule r = (Rule) i.next();
+           r.reset();
+       }
+    }
+
+    /**
+     * The propagate method calls {@link Rule#propagate} on all rules
+     * so as to apply the rules for all their new bindings, which
+     * previosuly have been snapshot by {@link Rule#reset} calls.
+     */
+    public void propagate(Vector/*<Rule>*/ rules) throws Exception {
+        for ( Iterator/*<Rule>*/ i = rules.iterator(); i.hasNext(); ) {
+            Rule r = (Rule) i.next();
+           r.propagate();
+       }
+    }
+
+    /**
+     * Returns a String representation of this RuleSet.
+     */
+    public String toString() {
+       StringBuilder s = new StringBuilder();
+       s.append( "{" );
+       boolean first = true;
+        for ( Iterator/*<Rule>*/ i = all_rules.iterator(); i.hasNext(); ) {
+            Rule r = (Rule) i.next();
+           if ( ! first )
+               s.append( "; " );
+           else
+               first = false;
+           s.append( r.toString() );
+       }
+       s.append( "}" );
+       return s.toString();
+    }
+
+    /**
+     * Utility method to add a {@link Rule} object and tie it to this
+     * RuleSet.
+     */
+    public void add(Rule r) {
+       if ( all_rules.contains( r ) ) {
+           return;
+       }
+       all_rules.add( r );
+       r.antecedent.addObserver( new RuleObserver( r ) );
+    }
+
+    /**
+     * Utility method to create a new {@link Rule} object with given
+     * antecedent and consequent {@link Query} objects, and add it to
+     * this RuleSet.
+     */
+    public void add(Query a,Query c) {
+       add( new Rule( a, c ) );
+    }
+
+    /**
+     * Deactivates this rule.
+     */
+    public void cancel(Rule r) {
+       all_rules.remove( r );
+    }
+
+    /**
+     * Utility method that adds all currently active rules to a
+     * {@link Vector}, and clears the active set.
+     */
+    public boolean popActivated(Vector/*<Rule>*/ v,boolean w) {
+       synchronized ( active ) {
+           while ( active.size() == 0 ) {
+               if ( w ) {
+                   try {
+                       active.wait();
+                   } catch (InterruptedException e) {
+                   }
+               } else {
+                   return false;
+               }
+           }
+           v.addAll( active );
+           active.clear();
+       }
+       return true;
+    }
+
+    ///
+    /// Runnable implementation
+    ///
+
+    /**
+     * Utility method to keep refreshing the rules if and while there
+     * are source updates. The {@link #wait} flag controls whether
+     * this method should block and wait for a new update (true) or
+     * return (false) when a propagation session ends. Note that if
+     * there are rules to propagate competing assertions in a cyclic
+     * manner, then the propagation will not end and the overall
+     * application will suffer badly.
+     */
+    public void run() {
+       try {
+           Vector/*<Rule>*/ rules = new Vector/*<Rule>*/();
+           for ( ;; ) {
+               if ( ! popActivated( rules, wait ) )
+                   return;
+               reset( rules );
+               propagate( rules );
+               rules.clear();
+           }
+       } catch (Exception e) {
+           e.printStackTrace();
+       }
+    }
+
+}
diff --git a/com/intendico/data/Snapshot.java b/com/intendico/data/Snapshot.java
new file mode 100644 (file)
index 0000000..1508f03
--- /dev/null
@@ -0,0 +1,249 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Vector;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Observer;
+
+/**
+ * The Snapshot class implements a query overlay for capturing changes
+ * to a source {@link Query} in respect of the added and lost valid
+ * binding combinations for the {@link Ref} objects
+ * involved. Temporally a snapshot is taken by the {@link #reset}
+ * method, as well as at construction. Thereafter successive {@link
+ * #next} invocations provide the added valid binding combinations
+ * since the prior snapshot.
+ *
+ * <p> Following a reset, the Snapshot also holds the lost bindings,
+ * which can be inspected separately.
+ *
+ * <p> A Snapshot may hold a non-null {@link #sync} object, which is
+ * used for synchronizing on during {@link #reset}. Any concurrent
+ * thread that updates queried components should synchronize on the
+ * same object, and in that way avoid multi-threading conflict.
+ */
+public class Snapshot implements Query {
+
+    /**
+     * Holds all the bindings found at the most recent snapshot.
+     */
+    public HashSet/*<Vector>*/ snapshot;
+
+    /**
+     * Holds the bindings that were valid before the most recent
+     * snapshot, but were no longer valid at that snapshot.
+     */
+    public HashSet/*<Vector>*/ lost = new HashSet/*<Vector>*/();
+
+    /**
+     * Holds the Ref objects in a Vector.
+     */
+    public Vector/*<Ref>*/ refs;
+
+    /**
+     * The iterator for recalling successive new bindings by the
+     * {@link #next} method.
+     */
+    public Iterator/*<Vector>*/ bindings;
+
+    /**
+     * The query being monitored.
+     */
+    public Query query;
+
+    /**
+     * The object, if any, to synchronize on during {@link
+     * #reset}. This is to support multi-thread access to queried
+     * components; any concurrent thread needs to synchronize on the
+     * same object.
+     */
+    public Object sync = null;
+
+    /**
+     * Constructor.
+     */
+    public Snapshot(Query q) {
+       query = q;
+       refs = query.getRefs( new Vector/*<Ref>*/() );
+       try {
+           reset();
+       } catch (Exception e) {
+           e.printStackTrace();
+       }
+    }
+
+    /**
+     * Alternative constructor with a synchronization object.
+     */
+    public Snapshot(Object lock,Query q) {
+       this( q );
+       sync = lock;
+    }
+
+    /**
+     * The {@link Query#copy} method implemented by creating a new
+     * Snapshot with a copy of the monitored {@link #query}, and
+     * sharing any {@link #sync} object.
+     */
+    public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
+       return new Snapshot( sync, query.copy( newrefs ) );
+    }
+
+    /**
+     * The {@link Query#reset} method implemented by capturing a new
+     * snapshot of the given query, and initialising the Iterator for
+     * the collection of bindings that are new since the last
+     * snapshot.
+     *
+     * <p> This method updates {@link #snapshot}, {@link #lost} and
+     * {@link #bindings}.
+     */
+    public void reset()        throws Exception {
+       if ( sync == null ) {
+           doReset();
+       } else {
+           synchronized( sync ) {
+               doReset();
+           }
+       }
+    }
+
+    /**
+     * Actual reset method, which may and might not be synchronized.
+     */
+    private void doReset() throws Exception {
+       lost = snapshot != null? snapshot : new HashSet/*<Vector>*/();
+       HashSet/*<Vector>*/ is = new HashSet/*<Vector>*/();
+       snapshot = new HashSet/*<Vector>*/();
+       Ref.clear( refs );
+       query.reset();
+       boolean none = true;
+       while ( query.next() ) {
+           none = false;
+           Vector v = Ref.getBinding( refs );
+           if ( snapshot.contains( v ) )
+               continue;
+           snapshot.add( v );
+           if ( lost != null && lost.contains( v ) ) {
+               lost.remove( v );
+           } else {
+               is.add( v );
+           } 
+       }
+       bindings = none? null : is.iterator();
+    }
+
+    /**
+     * Produces the succession of bindings that the qurey was valid
+     * for at the last snapshot, but not at the one prior.
+     *
+     * <p> This method uses {@link #bindings}.
+     */
+    public boolean next() {
+       if ( bindings == null )
+           return false;
+       if ( bindings.hasNext() ) {
+           Ref.bind( refs, (Vector) bindings.next() );
+       } else {
+           bindings = null;
+           return false;
+       }
+       if ( ! bindings.hasNext() )
+           bindings = null;
+       return true;
+    }
+
+    /**
+     * The {@link Query#getRefs} method implemented by returning the
+     * query's Ref object.
+     */
+    public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
+       return query.getRefs( v );
+    }
+
+    /**
+     * The {@link Query#addObserver} method implemented by adding
+     * the observer to the query.
+     */
+    public void addObserver(Observer x) {
+       query.addObserver( x );
+    }
+
+    /**
+     * The {@link Query#deleteObserver} method implemented by
+     * removing the observer from the query.
+     */
+    public void deleteObserver(Observer x) {
+       query.deleteObserver( x );
+    }
+
+    /**
+     * The {@link Query#addable} method implemented by forwarding to
+     * the contained query.
+     */
+    public boolean addable() {
+       return query.addable();
+    }
+
+    /**
+     * The {@link Query#add} method implemented by forwarding to the
+     * contained query.
+     */
+    public boolean add() {
+       return query.add();
+    }
+
+    /**
+     * The {@link Query#removable} method implemented by forwarding to
+     * the wrapped query.
+     */
+    public boolean removable() {
+       return query.removable();
+    }
+
+    /**
+     * The {@link Query#remove} method implemented by forwarding to
+     * the wrapped query.
+     */
+    public boolean remove() {
+       return query.remove();
+    }
+
+    /**
+     * Utility method to remove all remaining bindings for the
+     * query. This method applies to the current snapshot, and might
+     * make most sense for a newly created Snapshot object, where it
+     * will remove all matching bindings.
+     */
+    public void removeAll() {
+       while ( next() ) {
+           remove();
+       }
+    }
+
+    /**
+     * Returns a String representation of the Snapshot object.
+     */
+    public String toString() {
+       return "Snapshot( " + query.toString() + " )";
+    }
+}
diff --git a/com/intendico/data/Store.java b/com/intendico/data/Store.java
new file mode 100644 (file)
index 0000000..0cc2460
--- /dev/null
@@ -0,0 +1,55 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data;
+
+import java.util.Observable;
+
+/**
+ * This interface defines the requirements of a data store, which is
+ * an object that contains "tuples" represented as Object arrays. Note
+ * that stores typically should implement {@link Inquirable} as well.
+ */
+public interface Store {
+
+    /**
+     * Interface method for adding a tuple of values to this
+     * Store. The method returns true if the store was modified due to
+     * this action, otherwise false.
+     */
+    public boolean add(Object/*...*/ [] values) throws Exception ;
+
+    /**
+     * Interface method for removing a tuple of values from this
+     * Store. The method returns true if the store was modified due to
+     * this action, otherwise false.
+     */
+    public boolean remove(Object/*...*/ [] values) throws Exception ;
+
+    /**
+     * Interface method for clearing this store of all its data.
+     */
+    public void clear() throws Exception ;
+
+    /**
+     * Interface method for returning an Observable that notifies
+     * about subsequent changes to the Store.
+     */
+    public Observable getMonitor();
+}
diff --git a/com/intendico/data/addon/Language.java b/com/intendico/data/addon/Language.java
new file mode 100644 (file)
index 0000000..ed643bd
--- /dev/null
@@ -0,0 +1,464 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data.addon;
+
+import com.intendico.data.*;
+import com.intendico.gorite.Data;
+import com.intendico.sdp.BNFGrammar;
+import java.lang.reflect.Constructor;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+/**
+ * This class defines the textual predicate language. The detailed
+ * syntax is as follows:
+ *
+ * <pre>
+ * rule ::= &lt;predicate&gt; '=&gt; &lt;predicate&gt;
+ *
+ * predicate ::= 'And '( &lt;predicates&gt; ')
+ *   | 'Or '( &lt;predicates&gt; ')
+ *   | 'Not &lt;predicate&gt;
+ *   | 'Lost &lt;predicate&gt;
+ *   | 'Equals '( &lt;arguments&gt; ')
+ *   | 'Distinct '( &lt;arguments&gt; ')
+ *   | &lt;name&gt; '( &lt;arguments&gt; ')
+ *
+ * predicates ::= &lt;predicate&gt; ', &lt;predicates&gt;
+ *   | &lt;predicate&gt;
+ *
+ * arguments ::= &lt;argument&gt; ', &lt;arguments&gt;
+ *   | &lt;argument&gt;
+ *
+ * argument ::= &lt;number&gt;
+ *   | &lt;string&gt;         -- double-quoted
+ *   | &lt;xstring&gt;        -- single-quoted
+ *   | '$ &lt;name&gt;
+ * </pre>
+ *
+ * <p> Note that this lanugage allows single-quotes as well as
+ * double-quotes for strings (though the latter are called xstring in
+ * the grammar).
+ *
+ * <p> The Language object includes three lookup tables: {@link
+ * #inquirables} for lookup of relations (or primitive query names),
+ * {@link #data} for translation time binding of variables, and {@link
+ * #variables} for collating and re-using same named variables.
+ * 
+ * <p> Translation is not multi-thread safe.
+ */
+public class Language extends BNFGrammar {
+
+    private static String SYNTAX =
+       "rule ::= <predicate> '=> <predicate> " +
+
+       "predicate ::= " +
+       "   'And '( <predicates> ') # AND " +
+       " | 'Or '( <predicates> ') # OR " +
+       " | 'Lost <predicate> # LOST " +
+       " | 'Not <predicate> # NOT " +
+       " | 'Equals '( <arguments> ') # EQUALS " +
+       " | 'Distinct '( <arguments> ') # DISTINCT " +
+       " | <name> '( <arguments> ') # GET " +
+
+       "predicates ::= <predicate> ', <predicates> # Enlist " +
+       " | <predicate> # Enlist " +
+
+       "arguments ::= <argument> ', <arguments> # Enlist " +
+       " | <argument> # Enlist " +
+
+       "argument ::= <number> # NUMBER" +
+       " | <string> # STRING " +
+       " | <xstring> # STRING " +
+       " | '$ <name> # VARIABLE " +
+       ""
+       ;
+
+    /**
+     * Constant that describes the construction argument types for
+     * value queries such as {@link Equals} and {@link Distinct}.
+     */
+    private final static Class [] QUERYARGVAL =
+       new Class [] { Object[].class };
+
+    /**
+     * Constant that describes the construction argument types for
+     * single query queries such as {@link Not} and {@link Lost}.
+     */
+    private final static Class [] QUERYARGONE =
+       new Class [] { Query.class };
+
+    /**
+     * Constant that describes the construction argument types for
+     * multi query queries such as {@link And} and {@link Or}.
+     */
+    private final static Class [] QUERYARGMANY =
+       new Class [] { Query[].class };
+
+    /**
+     * Base class for queries taking multiple value arguments.
+     */
+    public class ArgsQuery extends Production {
+       /**
+        * Holds the target constructor for this kind of queries.
+        */
+       private Constructor cnr;
+
+       /**
+        * Constructor for a given target class.
+        */
+       public ArgsQuery(Class c) {
+            try {
+               cnr = c.getConstructor( QUERYARGVAL );
+            } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 1" );
+            }
+       }
+
+       /**
+        * The rule action. Create an instance of the target class for
+        * the given arguments.
+        */
+       public Object action(Vector v) {
+           try {
+               v = (Vector) v.get( 0 );
+               return cnr.newInstance(
+                   new Object [] { v.toArray( new Object [ v.size() ] ) } );
+           } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 2" );
+           }
+       }
+    }
+
+    /**
+     * Base class for queries taking multiple arguments.
+     */
+    public class ManyQuery extends Production {
+       /**
+        * Holds the target constructor for this kind of queries.
+        */
+       private Constructor cnr;
+
+       /**
+        * Constructor for a given target class.
+        */
+       public ManyQuery(Class c) {
+            try {
+               cnr = c.getConstructor( QUERYARGMANY );
+            } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 1" );
+            }
+       }
+
+       /**
+        * The rule action. Create an instance of the target class for
+        * the given arguments.
+        */
+       public Object action(Vector v) {
+           try {
+               v = (Vector) v.get( 0 );
+               return cnr.newInstance( new Object [] {
+                       (Query[]) v.toArray( new Query [ v.size() ] ) } );
+           } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 2" );
+           }
+       }
+    }
+
+    /**
+     * Base class for queries taking a single argument.
+     */
+    public class OneQuery extends Production {
+       /**
+        * Holds the target constructor for this kind of queries.
+        */
+       private Constructor cnr;
+
+       /**
+        * Constructor for a given target class.
+        */
+       public OneQuery(Class c) {
+            try {
+               cnr = c.getConstructor( QUERYARGONE );
+            } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 1" );
+            }
+       }
+
+       /**
+        * The rule action. Create an instance of the target class for
+        * the given argument.
+        */
+       public Object action(Vector v) {
+           try {
+               return cnr.newInstance( new Object[] { (Query) v.get( 0 ) } );
+           } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "BUG 2" );
+           }
+       }
+    }
+
+    /**
+     * Rule action for making an {@link And} query
+     */
+    public class AND extends ManyQuery {
+       public AND() {
+           super( And.class );
+       }
+    }
+
+    /**
+     * Rule action for making an {@link Or} query
+     */
+    public class OR extends ManyQuery {
+       public OR() {
+           super( Or.class );
+       }
+    }
+
+    /**
+     * Rule action for making a {@link Not} query
+     */
+    public class NOT extends OneQuery {
+       public NOT() {
+           super( Not.class );
+       }
+    }
+
+    /**
+     * Rule action for making a {@link Lost} query
+     */
+    public class LOST extends OneQuery {
+       public LOST() {
+           super( Lost.class );
+       }
+    }
+
+    /**
+     * Rule action for making an {@link Equals} query
+     */
+    public class EQUALS extends ArgsQuery {
+       public EQUALS() {
+            super( Equals.class );
+       }
+    }
+
+    /**
+     * Rule action for making a {@link Distinct} query
+     */
+    public class DISTINCT extends ArgsQuery {
+       public DISTINCT() {
+            super( Distinct.class );
+       }
+    }
+
+    /**
+     * Rule action for making an {@link Inquirable#get} query.
+     */
+    public class GET extends Production {
+       public Object action(Vector v) {
+           String name = (String) v.get( 0 );
+           try {
+               v = (Vector) v.get( 1 );
+               Inquirable inquirable = (Inquirable) inquirables.get( name );
+               return inquirable.get( v.toArray( new Object[ v.size() ] ) );
+           } catch (Exception e) {
+               e.printStackTrace();
+               throw new Error( "Unknown Inquirable: " + name );
+           }
+       }
+    }
+
+    /**
+     * Rule action for making a number value.
+     */
+    public class NUMBER extends Production {
+       /**
+        * Makes an Integer object for the given number
+        */
+        public Object action(Vector v) {
+           int i = 0;
+           try {
+               i = Integer.parseInt( (String) v.get( 0 ) );
+           } catch (Exception e) {
+               e.printStackTrace();
+           }
+           return Integer.valueOf( i );
+        }
+    }
+
+    /**
+     * Rule action for making a string value.
+     */
+    public class STRING extends Production {
+       /**
+        * Makes a String object for the given string
+        */
+        public Object action(Vector v) {
+            return v.get( 0 );
+        }
+    }
+
+    /**
+     * Rule action for making a variable. The name is first looked up
+     * in the {@link #variables} collection, and if not found, a new
+     * variable is created and added to the collection. Further, if
+     * the {@link #data} context is set and the variable is unbound,
+     * then the variable is also initialised from the equally named
+     * data element, if any.
+     */
+    public class VARIABLE extends Production {
+       /**
+        * Makes a String object for the given string
+        */
+        public Object action(Vector v) {
+            String name = "$" + (String) v.get( 0 );
+           Ref x = (Ref) variables.get( name );
+           if ( x == null ) {
+               x = new Ref( name );
+               variables.put( name, x );
+           }
+           if ( data != null && x.get() == null ) {
+               Object value = data.getValue( name );
+               if ( value != null )
+                   x.set( value );
+           }
+           return x;
+        }
+    }
+
+    /**
+     * Data context for pre-assignment of variables.
+     */
+    private Data data;
+
+    /**
+     * Collection of variables.
+     */
+    private Map variables = new HashMap();
+
+    /**
+     * Table of Inquirables.
+     */
+    private Map inquirables = new HashMap();
+
+    /**
+     * Constructor, which installs the language.
+     */
+    public Language() {
+       install( SYNTAX );
+       addRule( new Identifier( "name" ) );
+       addRule( new QuotedString( "string", '"' ) );
+       addRule( new QuotedString( "xstring", '\'' ) );
+       addRule( new Number( "number" ) );
+       
+    }
+
+    /**
+     * Holds the singleton grammar.
+     */
+    private static Language grammar;
+
+    /**
+     * Returns the singleton Language object, creating it on first
+     * call.
+     */
+    public static Language getLanguage() {
+       if ( grammar == null )
+           grammar = new Language();
+       return grammar;
+    }
+
+    /**
+     * Utility method that translates a {@link String} into a {@link
+     * Query} with given collections of inquirables and variables, and
+     * data context. The inquirables are used for lookup of relation
+     * symbols, and the {@link Data} context is used for initialising
+     * variables. Variables mentioned in the {@link String} are
+     * located from the variables collection, or created as needed.
+     * The inquirables collection must define all relations that are
+     * mentioned in the {@link String}, while the variables collection
+     * and the data context may be given as null.
+     */
+    public static Query textToQuery(
+       String text,Collection/*<Inquirable>*/ inquirables,
+       Collection/*<Ref>*/ vars,Data settings) {
+       Language g = getLanguage();
+       g.inquirables = new HashMap/*<String,Inquirable>*/();
+       if ( inquirables != null ) {
+           for ( Iterator/*<Inquirable>*/ si =
+                     inquirables.iterator(); si.hasNext(); ) {
+               Inquirable inquirable = (Inquirable) si.next();
+               g.inquirables.put( inquirable.getName(), inquirable );
+           }
+       }
+       g.data = settings;
+       g.variables = new HashMap/*<String,Ref>*/();
+       if ( vars != null ) {
+           for ( Iterator/*<Ref>*/ vi = vars.iterator(); vi.hasNext(); ) {
+               Ref variable = (Ref) vi.next();
+               g.variables.put( variable.getName(), variable );
+           }
+       }
+       try {
+           return (Query) g.parseAndProcess( "predicate", text );
+       } catch (Throwable t) {
+           t.printStackTrace();
+           return null;
+       }
+    }
+
+    /**
+     * Utility method that translates a {@link String} into a {@link
+     * Rule} with a given collection of inquirables.  The inquirables
+     * collection must define all relations that are mentioned in the
+     * {@link String}
+     */
+    public static Vector/*<Query>*/ textToRule(
+       String text,Collection/*<Inquirable>*/ inquirables) {
+        Language g = getLanguage();
+       g.inquirables = new HashMap/*<String,Inquirable>*/();
+       if ( inquirables != null ) {
+           for ( Iterator/*<Inquirable>*/ si =
+                     inquirables.iterator(); si.hasNext(); ) {
+               Inquirable inquirable = (Inquirable) si.next();
+               g.inquirables.put( inquirable.getName(), inquirable );
+           }
+       }
+       g.variables = new HashMap/*<String,Ref>*/();
+       g.data = null;
+       try {
+           return (Vector/*<Query>*/) g.parseAndProcess( "rule", text );
+       } catch (Throwable t) {
+           t.printStackTrace();
+           return null;
+       }
+    }
+
+}
diff --git a/com/intendico/data/addon/TextQueryCapability.java b/com/intendico/data/addon/TextQueryCapability.java
new file mode 100644 (file)
index 0000000..698b4bc
--- /dev/null
@@ -0,0 +1,147 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.data.addon;
+
+import com.intendico.data.*;
+import com.intendico.gorite.addon.Reflector;
+import com.intendico.gorite.*;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Observer;
+import java.util.Vector;
+
+/**
+ * This is a Capability extension that offers the use of predicates in
+ * text form using the language defined by the {@link Language}
+ * grammar.
+ *
+ * <p> The method {@link #textQuery(Data,String)} is used for
+ * immediate {@link com.intendico.data.Query Query} construction,
+ * which is suitable for the {@link Plan#context(Data)}
+ * implementations. The following is an illustration of its use:
+ *
+ * <pre>
+ * addGoal( new Plan( ... ) {
+ *     public Query context(Data d) {
+ *         return <b>textQuery</b>( d, "And( foo( 45, $x ), bar( $x ) )" );
+ *     }
+ * } );
+ * </pre>
+ *
+ * <p> The method {@link #addRule(String)} provides a deferred {@link
+ * Rule} installation from a text form rule. The following is an
+ * illustration of its use:
+ *
+ * <pre>
+ * addRule( "Lost primary($a,$b,$c) => secondary($a,$c)" );
+ * </pre>
+ *
+ * <p> The method {@link #addReflector(String,String,String)} provides
+ * a deferred {@link Reflector} installation with a text form
+ * query. The following is an illustration of its use:
+ *
+ * <pre>
+ * addReflector( "secondary lost", "percept","Lost secondary($a,$c)" );
+ * </pre>
+ *
+ * The deferred declarations are processed and installed by the first
+ * invokation of the {@link Capability#shareInquirables(Collection)}
+ * method, when all the inquirables have been shared. In that way the
+ * text form references get resolved to actual relations when
+ * translated into the actual {@link Query} or {@link Rule} structure.
+ *
+ * <p> Note that more arbitrary conditions in text form predicates
+ * need to be supported by appropriate {@link Inquirable}
+ * implementations.  For example, the following could be a way to
+ * define a computed "greater" relation between Integer values:
+ *
+ * <pre>
+ * putInquirable( new Inquirable() {
+ *     public String getName() { return "greater"; }
+ *     public Query get(final Object [] args) {
+ *         return new Condition() {
+ *             public boolean condition() {
+ *                 int a = ((Integer) Ref.deref( args[0] )).intValue();
+ *                 int b = ((Integer) Ref.deref( args[1] )).intValue();
+ *                 return a > b;
+ *             }
+ *         };
+ *     }
+ * } );
+ * </pre>
+ *
+ * @see Language
+ */
+public class TextQueryCapability extends Capability {
+
+    /**
+     * Adds a textual rule definition. {@link Rule} creation is set up
+     * to be deferred until after the first {@link
+     * Capability#shareInquirables} invocation.
+     */
+    public void addRule(final String rule) {
+       add( new Deferred() {
+           /**
+            * Installs this as a proper {@link Reflector}.
+            */
+           public void install() {
+               Vector/*<Query>*/ q =
+                   Language.textToRule( rule, getInquirables().values() );
+               addRule( (Query) q.get( 0 ), (Query) q.get( 1 ) );
+           }
+       } );
+    }
+
+    /**
+     * Utility method to declare a {@link Reflector} for the owner
+     * {@link Performer} using a text form query. This is deferred to
+     * after the first {@link Capability#shareInquirables(Collection)}
+     * as needed.
+     */
+    public void addReflector(
+       final String goal,final String todo,final String query) {
+       add( new Deferred() {
+           /**
+            * Installs this as a proper {@link Reflector}.
+            */
+           public void install() {
+               Reflector r = new Reflector( getPerformer(), goal, todo );
+               r.addQuery( Language.textToQuery(
+                               query,
+                               getInquirables().values(), null, null ),
+                           goal );
+           }
+       } );
+    }
+
+    /**
+     * Utility method that creates a {@link Query} by resolving to the
+     * {@link Capability#inquirables} and the given {@link Data} context.
+     * Note that using this method before inquirables are shared, or more
+     * specifically if relation names are undefined, may result in an
+     * {@link Error}. Further, if the method returns null if the
+     * predicate is not parsable.
+     */
+    public Query textQuery(Data data,String predicate) {
+       return Language.textToQuery(
+           predicate, getInquirables().values(), null, data );
+    }
+
+}
diff --git a/com/intendico/gorite/.cvsignore b/com/intendico/gorite/.cvsignore
new file mode 100644 (file)
index 0000000..6b468b6
--- /dev/null
@@ -0,0 +1 @@
+*.class
diff --git a/com/intendico/gorite/Action.java b/com/intendico/gorite/Action.java
new file mode 100644 (file)
index 0000000..b29f4c4
--- /dev/null
@@ -0,0 +1,298 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+/**
+ * The Action class is a utility base class for implementation of task
+ * goals that operate on a common entity. The created action object
+ * becomes a factory for goals that refer back to it, and that end up
+ * invoking the Action's {@link #execute} method as way of achieving
+ * the task goal.
+ *
+ * <p> Generally, an action is a named definition of a function
+ * performed on data. It would occur in a process model in terms of a
+ * task goal, which is recognised as a usage point of the action,
+ * linking it to particular input and output data. The same action may
+ * be used elsewhere, with other data names.
+ *
+ * <p> Task goals referring to the same action object will invoke the
+ * same {@link #execute} method.
+ *
+ * <p>Example of use:
+ * <pre>
+ * public class MachineControlAdapter {
+ *     Action open_lid = new Action( "open lid" ) {
+ *         public boolean execute(
+ *             boolean reentry,Data.Element [] ins,Data.Element [] * outs ) {
+ *                 .. code to perform the "open lid" action
+ *             }
+ *     }
+ *     Action status = new Action( "check status" ) {
+ *         public boolean execute(
+ *             boolean reentry,Data.Element [] ins,Data.Element [] * outs ) {
+ *                 .. code to perform the "check status" action
+ *             }
+ *     }
+ *     public Capability actions() {
+ *         return new Capability() {{
+ *             open_lid.create( new String [] { "angle" }, null );
+ *             on_off.create( null, new String [] { "status" } );
+ *         }};
+ *     }
+ * }
+ * </pre>
+ *
+ * The example code illustrates a "machine adapter" with two actions:
+ * to "open lid" to a certain "angle", and to "check status" giving a
+ * "status". The "machine adapter" includes an "actions()" method that
+ * creates a control capability of two goals for invoking the actions.
+ */
+public class Action {
+
+    /**
+     * The name of the action. This is also the name of the goal for
+     * the action created via the {@link #create} method.
+     */
+    public String name;
+
+    /**
+     * Nominates the {@link Performer.TodoGroup} to use for performing
+     * this action.
+     */
+    private String group;
+
+    /**
+     * Constructor without group.
+     */
+    public Action(String n) {
+       name = n;
+    }
+
+    /**
+     * Constructor.
+     */
+    public Action(String n,String g) {
+       name = n;
+       group = g;
+    }
+
+    /**
+     * This is a utility class to represent the appearance of the
+     * action within a goal hierarchy. Each such apprearance involves
+     * its own data connections, which are contained within the Usage
+     * class. When this task goal is executed, it firstly ensures that
+     * all input data is available, or otherwise delays the execution
+     * until they are available. Thereafter it ensures that some
+     * output data needs to be produces, before invoking the action's
+     * exectution. If all output data is already produced, the task
+     * goal execution succeeds immediately without the actual action
+     * execution.
+     */
+    public class Usage extends Goal {
+
+       /**
+        * The names of data elements that are inputs to the goal; the
+        * performance of a goal will be postponed until all inputs
+        * are available, although maybe not ready.
+        */
+       public String [] inputs;
+
+       /**
+        * The names of data elements that are outputs from the
+        * action. The task goal for an action considers these: if
+        * they are all ready and available when the goal is
+        * performed, then the action execution is skipped, and the
+        * goal terminates successfully.  However, an action without
+        * outputs is always executed.
+        */
+       public String [] outputs;
+
+       /**
+        * Constructor.
+        */
+       public Usage(String [] ins,String [] outs) {
+           super( Action.this.name );
+           inputs = ins;
+           outputs = outs;
+           group = getGoalGroup();
+       }
+
+       /**
+        * Creates and returns an instance object for achieving
+        * a Usage.
+        */
+       public Instance instantiate(String head,Data d) {
+           return new Invoke( head, d );
+       }
+
+       /**
+        * The Invoke class represents the intention to performa Usage
+        * goal.
+        */
+       public class Invoke extends Instance {
+
+           /**
+            * Constructor.
+            */
+           public Invoke(String head,Data d) {
+               super( head );
+           }
+
+           /**
+            * Tracking of first or sub sequence perform calls.
+            */
+           public boolean reentry = false;
+
+           /**
+            * The execution of an action involves an initial step of
+            * establishing the data connections. This will postpone the
+            * actual action execution until all input data is
+            * available. It will also skip the action execution if all
+            * outputs are already marked ready. It is the responsibility
+            * of the actual action execution to mark outputs as ready.
+            */
+           public States action(String head,Data d) {
+               Data.Element [] ins = null;
+               Data.Element [] outs = null;
+
+               // Synchronise with existence and readiness of inputs.
+               if ( inputs != null ) {
+                   ins = new Data.Element [ inputs.length ];
+                   for ( int i = 0; i < inputs.length; i++ ) {
+                       ins[ i ] = d.find( inputs[ i ] );
+                       if ( ins[ i ] == null )
+                           return States.BLOCKED;
+                       if ( ! ins[ i ].ready )
+                           return States.BLOCKED;
+                   }
+               }
+
+               if ( outputs != null ) {
+                   outs = new Data.Element [ outputs.length ];
+                   for ( int i = 0; i < outputs.length; i++ ) {
+                       outs[ i ] = d.create( outputs[ i ] );
+                   }
+               }
+
+               // Invoke action implementation 
+               Goal.States s = Action.this.execute( reentry, ins, outs );
+               reentry = true;
+
+               if ( s == Goal.States.PASSED ) {
+                   // Mark all outputs as ready.
+                   if ( outs != null ) {
+                       for ( int i = 0; i < outs.length; i++ ) {
+                           outs[ i ].markReady();
+                       }
+                   }
+               }
+               return s;
+           }
+       }
+
+       /**
+        * Makes a string representation of a string array.
+        */
+       public String toString(String [] data)
+       {
+            StringBuffer s = new StringBuffer( "[ " );
+           if ( data != null ) {
+               String sep = "";
+               for ( int i = 0; i < data.length; i++ ) {
+                   s.append( sep );
+                   s.append( nameString( data[i] ) );
+                   sep = ", ";
+               }
+           }
+           s.append( " ]" );
+           return s.toString();
+       }
+
+       /**
+        * Makes a textual representation of this action's goal.
+        */
+       public String toString(String counter) {
+           StringBuffer s = new StringBuffer( super.toString( counter ) );
+           s.append( toString( inputs ) );
+           s.append( " >> " );
+           s.append( toString( outputs ) );
+           //s.append( "\n" );
+           return s.toString();
+       }
+    }
+
+    /**
+     * A factory method that makes a "usage object" for this action,
+     * representing its usage in a goal sentence. When performed,
+     * the action's execute method is invoked with the actual in and out
+     * Data.Element objects.
+     */
+    public Goal create(String [] ins,String [] outs)
+    {
+       return new Usage( ins, outs );
+    }
+
+    /**
+     * The generic action performance method. Should be overridden by
+     * extension class for action implementation.
+     */
+    public Goal.States execute(
+       boolean reentry,Data.Element [] ins,Data.Element [] outs) {
+
+       StringBuffer s = new StringBuffer( "Action " );
+       s.append( Goal.nameString( name ) );
+       s.append( " [" );
+       String sep = "";
+       if ( ins != null && ins.length > 0 ) {
+           for ( int i=0; i < ins.length; i++ ) {
+               s.append( sep );
+               sep = ", ";
+               s.append( ins[i].toString() );
+           }
+       }
+       s.append( "] >> [" );
+       sep = "";
+       if ( outs != null && outs.length > 0 ) {
+            for ( int i=0; i < outs.length; i++ ) {
+               s.append( sep );
+               sep = ", ";
+               s.append( outs[i].toString() );
+            }
+       }
+       s.append( "]" );
+       System.err.println( s.toString() );
+       return Goal.States.PASSED;
+    }
+
+    /**
+     * Returns the {@link #group} attribute.
+     */
+    public String getTodoGroup() {
+       return group;
+    }
+
+    /**
+     * Assigns the {@link #group} attribute.
+     */
+    public void setTodoGroup(String v) {
+       group = v;
+    }
+
+}
diff --git a/com/intendico/gorite/BDIGoal.java b/com/intendico/gorite/BDIGoal.java
new file mode 100644 (file)
index 0000000..8c3ee97
--- /dev/null
@@ -0,0 +1,395 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.Random;
+
+/**
+ * A BDIGoal is achieved by means of finding and performing a goal
+ * hierarchy as named by the BDI goal in the {@link Capability} that
+ * is the value of the {@link Data.Element} named by the BDIGoal's
+ * {@link #control} attribute. By default, the {@link #PERFORMER} data
+ * name is used, which is maintained by the execution machinery to
+ * resolve to the current performer. The BDIGoals created by the
+ * {@link TeamGoal} execution uses the role name as data name, which
+ * then is set up to be the role filling.
+ *
+ * <p> A BDIGoal is executed in a sequence of steps in an attempt to
+ * find a goal hierarchy for the named goal that acheives it, or more
+ * precisely, that executes without returning FAILED. The goal
+ * hierarchy alternatives are found via the {@link
+ * Capability#lookup(String)} method of the executing {@link
+ * Performer}. Any such alternative that implements the {@link
+ * Context} interface is considered a plan whose {@link
+ * Context#context(Data)} method defines its applicable variants.
+ *
+ * <p> The BDIGoal collates all applicable plan options, then selects
+ * one of them as an attempt to achieve the goal. If that fails, then
+ * the plan option is remembered, and the BDIGoal again collates all
+ * applicable, non-failed plan options, picks one and tries that. This
+ * repeats until either one plan execution succeeds (returns PASSED),
+ * or the plan options are exhausted, in which case the BDIGoal fails.
+ * 
+ * <p> The choice of which among the applicable plan options to use
+ * can be modified via the plan choice settings. By default, the plan
+ * options are queried for their {@link Precedence#precedence(Data)}
+ * values, and the first of highest precedence option is
+ * choosen. However, if the goal is associated with a {@link Random}
+ * object, then the option is choosen by a random draw among the
+ * highest precedence options. Further, if the goal is associated with
+ * a plan choice goal name, then that plan choice goal is performed
+ * for effectuating the plan choice (instead of using precedence
+ * values).
+ *
+ * <p> A BDIGoal that has a {@link #specific} object set, makes that
+ * object available for instance execution as a data element named by
+ * the BDIInstance head name, which for invoked sub goals is
+ * everything up to the last '*' of their heads.
+ *
+ * @see Plan
+ * @see PlanChoiceGoal
+ * @see ContextualGoal
+ * @see Performer#getPlanChoice(String)
+ * @see Performer#setPlanChoice(String,Object)
+ * @see TeamGoal
+ */
+public class BDIGoal extends Goal {
+
+    /**
+     * The name of the data element whose value is the name of the
+     * current role for the executing performer.
+     */
+    public static final String ROLE = "current role";
+
+    /**
+     * Constructor. 
+     */
+    public BDIGoal(String n) {
+       super( n );
+       setGoalControl( PERFORMER );
+    }
+
+    /**
+     * Cache of construction object (when not a String).
+     */
+    public Object specific;
+
+    /**
+     * Constructor using an object other than String. Then the class
+     * name of the object is used as goal name, and the object is held
+     * as {@link #specific}. However, if the given object is a {@link
+     * String}, then its value (rather than its type) is used as goal
+     * name anyhow.
+     */
+    public BDIGoal(Object x) {
+       this( ( x instanceof String )? (String) x : x.getClass().getName() );
+       specific = x instanceof String? null : x;
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a BDIGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new BDIInstance( head );
+    }
+
+    /**
+     * Utility method to add a Goal unless it's contained among
+     * failed.
+     */
+    static public void maybeAdd(
+       Goal g,Vector/*<Goal>*/ v,Vector/*<Goal>*/ failed) {
+       if ( failed == null || ! failed.contains( g ) ) {
+           v.add( g );
+       }
+    }
+
+    /**
+     * Expand context sensitive alternative plans. This method gets
+     * invoked with the current selection of plans matching to the
+     * BDIGoal to achieve, the current collection of tried but failed
+     * plan variants, and the current {@link Data}. It processes all
+     * plan contexts so as to produce the currently possible
+     * alternative contextual plan invocations, by determining
+     * validating bindings for the plan's context queries.
+     *
+     * <p> The class {@link ContextualGoal} is used to represent a
+     * plan variant, which consists of the plan together with the
+     * query {@link Ref} object binding. This takes care of
+     * presenting the binding in the {@link Data} when the plan is
+     * invoked, to unset this binding from the {@link Data} if the
+     * plan execution fails, and to extract new bindings from the
+     * {@link Data} when the plan succeeds.
+     *
+     * <p> This method recognises the {@link Context#EMPTY} query
+     * as marker that a plan does not have any applicable variant,
+     * and it also catches the {@link Context.None} exception for
+     * the same purpose.
+     *
+     * <p> Plan variants are filtered against the failed set, to
+     * avoid the same plan variant be attempted more than once.
+     *
+     * @see Context
+     * @see ContextualGoal
+     * @see BDIInstance#action
+     */
+    static public Vector/*<Goal>*/ applicable(
+       Vector/*<Goal>*/ plans, Vector/*<Goal>*/ failed, Data data) {
+       Vector/*<Goal>*/ v = new Vector/*<Goal>*/();
+
+       for ( Iterator/*<Goal>*/ i = plans.iterator(); i.hasNext(); ) {
+           Goal goal = (Goal) i.next();
+           if ( ! ( goal instanceof Context ) ) {
+               maybeAdd(
+                   new ContextualGoal( null, null, goal, data ),
+                   v, failed );
+               continue;
+           }
+           try {
+               Query query = ((Context) goal).context( data );
+               if ( query == null ) {
+                   maybeAdd(
+                       new ContextualGoal( null, null, goal, data ),
+                       v, failed );
+                   continue;    
+               }
+               if ( query != Context.EMPTY ) {
+                   Vector/*<Ref>*/ vars =
+                       query.getRefs( new Vector/*<Ref>*/() );
+                   Vector/*<Ref>*/ orig = Ref.copy( vars );
+                   query.reset();
+                   while ( query.next() ) {
+                       maybeAdd(
+                           new ContextualGoal( orig, vars, goal, data ),
+                           v, failed );
+                   }
+               }
+           } catch (Context.None e) {
+               // No applicable context
+           } catch (Throwable e) {
+               e.printStackTrace();
+               System.err.println( "Ignored plan " + goal );
+           }
+       }
+       return v;
+    }
+
+    /**
+     * Return the plan choice for this goal, relative a given root
+     * capability executing the goal. This treats 
+     */
+    public Object getPlanChoice(Capability root) {
+       return root.getPerformer().getPlanChoice( getGoalName() );
+    }
+
+    /**
+     * Implements a BDI method choice.
+     */
+    public class BDIInstance extends Instance {
+
+       /**
+        * The goal alternatives. (Called "relevant set" in BDI
+        * terminology)
+        */
+       public Vector/*<Goal>*/ relevant;
+
+       /**
+        * The capability hierarchy offering goal methods.
+        */
+       public Capability root;
+
+       /**
+        * The tried and failed goals.
+        */
+       public Vector/*<Goal>*/ failed = new Vector/*<Goal>*/();
+
+       /**
+        * The count of attempts.
+        */
+       public int count = 0;
+
+       /**
+        * The current goal.
+        */
+       Goal goal;
+
+       /**
+        * The current goal instance.
+        */
+       Instance instance = null;
+
+       /**
+        * Determine the context sensitive alternatives by invoking
+        * {@link #applicable}
+        *
+        * <p> At end, this method invokes {@link #precedenceOrder} on
+        * the collection of applicable plan variants, which sorts the
+        * plan variants by descending precedence.
+        * @see Context
+        * @see ContextualGoal
+        * @see #precedenceOrder
+        * @see BDIInstance#action
+        */
+       public Goal contextual(
+           Vector/*<Goal>*/ set,Vector/*<Goal>*/ failed,Data data) {
+           Vector/*<Goal>*/ v = applicable( set, failed, data );
+
+           // If there is a plan choice goal for this goal, then let
+           // it make the choice.
+           if ( plan_choice instanceof String ) {
+               return new PlanChoiceGoal(
+                   root.getPerformer(), (String) plan_choice, v, failed );
+           }
+           return precedenceOrder( v, data );
+       }
+
+       /**
+        * Apply precedence ordering destructively to the given goal
+        * set.
+        *
+        * @see Precedence
+        */
+       public Goal precedenceOrder(Vector/*<Goal>*/ set,Data data) {
+           int level = 0;
+           Vector/*<Goal>*/ best = new Vector();
+           for ( Iterator/*<Goal>*/ i = set.iterator(); i.hasNext(); ) {
+               Goal g = (Goal) i.next();
+               int p = g instanceof Precedence? 
+                   ((Precedence) g).precedence( data ) :
+                   Plan.DEFAULT_PRECEDENCE ;
+               if ( best.size() == 0 ) {
+                   level = p;
+                   best.add( g );
+               } else if ( p > level ) {
+                   best.clear();
+                   level = p;
+                   best.add( g );
+               } else if ( p == level ) {
+                   best.add( g );
+               }
+           }
+           if ( best.size() == 0 )
+               return null;
+           if ( best.size() == 1 || ! ( plan_choice instanceof Random ) )
+               return (Goal) best.get( 0 );
+           return (Goal) best.get(
+               ((Random) plan_choice).nextInt( best.size() ) );
+       }
+
+       /**
+        * Holds the plan choice goal, or null.
+        */
+       public Object plan_choice;
+
+       /**
+        * Constructor.
+        */
+        public BDIInstance(String h) {
+            super( h );
+        }
+
+       /**
+        * Cancel the execution.
+        */
+       public void cancel() {
+           if ( instance != null )
+               instance.cancel();
+       }
+
+       /**
+        * Instantiates and performs sub goals in sequence, until the
+        * first one that does not fail. If a sub goal fails, then
+        * that is caught, and the next sub goal in sequence is
+        * instantiated and performed.
+        */
+       public States action(String head,Data data)
+            throws LoopEndException, ParallelEndException {
+           if ( root == null ) {
+               root = (Capability) data.getValue( getGoalControl() );
+               if ( root == null ) {
+                   System.err.println(
+                       "** Missing capability '" + getGoalControl() +
+                       "' for " + BDIGoal.this.toString( head ) );
+                   return States.FAILED;
+               }
+               plan_choice = getPlanChoice( root );
+           }
+           if ( goal == null ) {
+               if ( isTracing() ) {
+                   System.err.println(
+                       "** Lookup \"" + getGoalName() + "\"" );
+                }
+               data.setArgs( head, specific );
+               goal = contextual(
+                   root.lookup( getGoalName() ), failed, data );
+               if ( goal == null && isTracing() ) {
+                   System.err.println(
+                       "** Goal \"" + getGoalName() + "\" unknown " );
+               }
+           }
+
+           while ( goal != null ) {
+               if ( instance == null ) {
+                   instance = goal.instantiate( head + "*" + count, data );
+                   count += 1;
+               }
+               if ( isTracing() ) {
+                   System.err.println(
+                       "** " + nameString( getGoalName() ) +
+                       " attempt " + count );
+               }
+               String was_role = (String) data.getValue( ROLE );
+               States b;
+               data.setValue( ROLE, getGoalControl() );
+               try {
+                   b = instance.perform( data );
+               } finally {
+                   data.restoreValue( ROLE, was_role );
+               }
+               //
+               // Note, the following doesn't happen if instance.perform()
+               // above throws an exception.
+               //
+               if ( b != States.FAILED )
+                   return b;
+               instance = null;
+               if ( goal instanceof PlanChoiceGoal ) {
+                   PlanChoiceGoal pg = (PlanChoiceGoal) goal;
+                   if ( pg.choice == null )
+                       break;
+                   if ( pg.done == States.PASSED )
+                       failed.add( pg.choice );
+               } else {
+                   failed.add( goal );
+               }
+               goal = contextual(
+                   root.lookup( getGoalName() ), failed, data );
+           }
+           return States.FAILED;
+       }
+    }
+}
diff --git a/com/intendico/gorite/BranchingGoal.java b/com/intendico/gorite/BranchingGoal.java
new file mode 100644 (file)
index 0000000..a7b3765
--- /dev/null
@@ -0,0 +1,327 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import java.util.Vector;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+ * The BranchingGoal class is an intermediate, abstract class that
+ * provides common execution support for parallel branches.
+ */
+abstract public class BranchingGoal extends Goal {
+
+    /**
+     * Constructor.
+     */
+    public BranchingGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public BranchingGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Helper class that implements suport for parallel goal execution
+     * with interrupt control. 
+     */
+    abstract public class MultiInstance extends Instance {
+
+       /**
+        * States of execution; the number of branches remaining.
+        */
+       public int counter;
+
+       /**
+        * Currently active branches.
+        */
+       public Vector/*<Branch>*/ branches = null;
+
+       /**
+        * Names of data elements that are output from any branch.
+        */
+       public HashSet/*<String>*/ outs = new HashSet/*<String>*/();
+
+       /**
+        * Utility method for counting branches when they are created.
+        */
+       public synchronized void increment() {
+           counter++;
+       }
+
+       /**
+        * Constructor.
+        */
+       public MultiInstance(String h) {
+           super( h );
+       }
+
+       /**
+        * A class to monitor the combined runnability of all the
+        * branches, by the logic that this intention is runnable if
+        * any of the branch intentions are runnable.
+        */
+       class RunnableMonitor extends Observable implements Observer {
+
+           private boolean some_runnable = false;
+
+           public void update(Observable x,Object y) {
+               if ( ! some_runnable ) {
+                   some_runnable = true;
+                   setChanged();
+                   notifyObservers();
+               }
+               if ( x != null ) {
+                   x.deleteObserver( this );
+               }
+           }
+       }
+
+       /**
+        * Keeps the monitor for branch runnability state.
+        */
+       private RunnableMonitor runnable_monitor = null;
+
+       /**
+        * Utility class to set up runnability monitoring.
+        */
+       private void setupMonitor(Data d) {
+           runnable_monitor = new RunnableMonitor();
+           d.setTrigger( runnable_monitor );
+           boolean some = false;
+           for ( Iterator/*<Branch>*/ i =
+                     branches.iterator(); i.hasNext(); ) {
+               some |= ((Branch)i.next()).setupObserver( d );
+           }
+           if ( some ) {
+               removeMonitor( d );
+               d.clearTrigger();
+           }
+       }
+
+       /**
+        * Utility method to remove the runnability monitoring.
+        */
+       private void removeMonitor(Data d) {
+           runnable_monitor = null;
+           for ( Iterator/*<Branch>*/ i =
+                     branches.iterator(); i.hasNext(); ) {
+                ((Branch)i.next()).removeObserver( d );
+           }
+       }
+
+       /**
+        * Branch head and execution.
+        */
+       public class Branch {
+
+           /**
+            * Collecting all outputs.
+            */
+           public HashSet/*<String>*/ outputs;
+
+           /**
+            * The goal instance to execute.
+            */
+            public Instance instance;
+
+           /**
+            * Branch thread name. This needs to be distinct from the
+            * actual branch Instance in order to join data properly.
+            */
+           public String thread_name;
+
+           /**
+            * The Data operated on.
+            */
+           public Data data;
+
+           /**
+            * Propagate cancellation to actual branch.
+            */
+           public void cancel() {
+               if ( instance != null ) {
+                   instance.cancel();
+               }
+           }
+
+           /**
+            * Constructor, tying the branch to a goal instance.
+            */
+           public Branch(int ix,Instance i,Data d,HashSet/*<String>*/ outs) {
+               instance = i;
+               data = d;
+               outputs = outs;
+               increment();
+               thread_name = instance.thread_name + "(branch)";
+               data.fork( getGoalControl(), ix, thread_name );
+           }
+
+           /**
+            * Branch main method: performs the associated goal
+            * instance, and manages current "thread name".
+            */
+           public States action()
+               throws LoopEndException, ParallelEndException {
+               String old = data.setThreadName( thread_name );
+               try {
+                   States s = instance.perform( data );
+                   // TODO: collate all result data for join at end
+                   if ( s == States.PASSED ) {
+                       //data.join( getGoalControl(), outputs, thread_name );
+                       data.join( getGoalControl(), null, thread_name );
+                   } else if ( s == States.FAILED ) {
+                       data.join( getGoalControl(), null, thread_name );
+                   }
+                   return s;
+               } catch (ParallelEndException e) {
+                   //data.join( getGoalControl(), outputs, thread_name );
+                   data.join( getGoalControl(), null, thread_name );
+                   throw e;
+               } finally {
+                   data.setThreadName( old );
+               }
+           }
+
+           /**
+            * Utility method to set up the runnable_monitor as
+            * observer of runnability change for this branch, and
+            * then return the current runnability state.
+            */
+           boolean setupObserver(Data d) {
+               d.addObserver( instance.thread_name, runnable_monitor );
+               return d.isRunning( instance.thread_name );
+           }
+
+           /**
+            * Utility method to remove the runnable_monitor as
+            * runnability observer.
+            */
+           void removeObserver(Data d) {
+               d.deleteObserver( instance.thread_name, runnable_monitor );
+           }
+       }
+
+       /**
+        * Cancels the MultiInstance (Parallel or Repeat)
+        */
+       public void cancel() {
+           super.cancel();
+           propagateCancel( null );
+       }
+
+       /**
+        * Propagates cancel to all branches
+        */
+       public void propagateCancel(Branch skip)
+       {
+           if ( branches == null )
+               return;
+           for ( Iterator/*<Branch>*/ i =
+                     branches.iterator(); i.hasNext(); ) {
+               Branch b = (Branch) i.next();
+               if ( b != skip )
+                   b.cancel();
+           }
+       }
+
+       /**
+        * Process all subgoals in parallel, and complete when they
+        * complete.
+        */
+       public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           if ( branches == null ) {
+               branches = new Vector/*<Branch>*/();
+               for ( int i = 0; more( i, d ); i++ ) {
+                   Instance instance = getBranch( i, head + ":" + i, d );
+                   Branch b = new Branch( i, instance, d, outs );
+                   branches.add( b );
+               }
+           }
+
+           if ( runnable_monitor != null ) {
+               removeMonitor( d );
+           }
+
+           int icount = 0;
+           int bcount = 0;
+
+           try {
+               while ( branches.size() > 0 ) {
+                   States s = ((Branch)branches.get( 0 )).action();
+                   if ( s == States.STOPPED || s == States.BLOCKED ) {
+                       branches.add( branches.remove( 0 ) );
+                       icount += 1;
+                       if ( s == States.BLOCKED )
+                           bcount += 1;
+                       if ( icount < branches.size() )
+                           continue;
+                       if ( bcount != icount )
+                           return States.STOPPED;
+                       setupMonitor( d );
+                       return States.BLOCKED;
+                   }
+                   icount = bcount = 0;
+                   if ( s == States.PASSED ) {
+                       branches.remove( 0 );
+                       continue;
+                   }
+                   propagateCancel( (Branch)branches.get( 0 ) );
+                   return s;
+               }
+           } catch (LoopEndException e) {
+               propagateCancel( null );
+               throw e;
+           } catch (ParallelEndException e) {
+               propagateCancel( (Branch) branches.get( 0 ) );
+               // Discard data from cancelled branches
+           } finally {
+               // Signal elevated branch outputs in calling data context.
+               for ( Iterator/*<String>*/ i =
+                         outs.iterator(); i.hasNext(); ) {
+                   String key = (String) i.next();
+                   d.ready( key );
+               }
+           }
+           // TODO join collated data
+           return States.PASSED;
+       }
+
+       /**
+        * The method for determining whether there is another branch.
+        */
+        abstract public boolean more(int i,Data d);
+
+       /**
+        * Utility method to obtain the actual branch instance.
+        */
+        abstract public Instance getBranch(int i,String head,Data d);
+
+    }
+
+}
diff --git a/com/intendico/gorite/Capability.java b/com/intendico/gorite/Capability.java
new file mode 100644 (file)
index 0000000..1bff038
--- /dev/null
@@ -0,0 +1,427 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Inquirable;
+import com.intendico.data.Query;
+import com.intendico.data.Rule;
+import com.intendico.data.Store;
+import com.intendico.gorite.addon.Reflector;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A Capability is a container of {@link Goal} hierarchies, which are
+ * accessible by their names, and understood to represent alternative
+ * ways in which that named goal may be achieved. It provides lookup
+ * context for {@link BDIGoal} goals.
+ *
+ * <p> A Capability may also contain {@link Rule} objects, which are
+ * added by the {@link #addRule} method. The {@link Rule} objects are
+ * collated into the owning {@link Performer} {@link
+ * Performer#rule_set rule_set} to be applied as part of the goal
+ * execution.
+ */
+public class Capability {
+
+    /**
+     * The goals of this capability, clustered by name.
+     */
+    private Hashtable/*<String,Vector<Goal>>*/ goals =
+        new Hashtable/*<String,Vector<Goal>>*/();
+
+    /**
+     * The table of inqueriable belief structures of this capability,
+     * primarily understood qua the {@link Inquirable} interface. This
+     * table is initially built at construction time by {@link
+     * #putInquirable(Inquirable)} calls, and by elevating
+     * inqueriables of sub capabilities that are added to this
+     * capability.  Eventually, there is also a {@link
+     * #shareInquirables} invocation, which accepts incoming
+     * inqueriables, and the distributes this {@link Inquirable}
+     * collection downwards to sub capabilities. In general, all
+     * capabilities form the same name-to-store mapping throughout a
+     * performer, except where name conflicts arise.
+     */
+    private Hashtable/*<String,Inquirable>*/ inquirables =
+        new Hashtable/*<String,Inquirable>*/();
+
+    /**
+     * The inner capabilities of this capability.
+     */
+    private Vector/*<Capability>*/ inner;
+
+    /**
+     * The {@link Rule} objects of this capability.
+     */
+    private Vector/*<Rule>*/ rules;
+
+    /**
+     * The performer that has this capability.
+     */
+    Performer performer;
+
+    /**
+     * This interface is implemented by the deferred text form
+     * entities (rules or reflectors).
+     */
+    public interface Deferred {
+       /**
+        * The method by which this entity installs itself.
+        */
+       public void install();
+    }
+
+    /**
+     * The collection of deferred entities. This is set to null by the
+     * first {@link #shareInquirables(Collection)} call.
+     */
+    private Vector/*<Deferred>*/ deferred = new Vector/*<Deferred>*/();
+
+    /**
+     * Utility method to install a Deferred object subsequent to the
+     * first invokation of {@link #shareInquirables(Collection)}
+     */
+    public void add(Deferred entity) {
+       if ( deferred == null ) {
+           entity.install();
+       } else {
+           deferred.add( entity );
+       }
+    }
+
+    /**
+     * Constructs a vector of all goals under the given name, by
+     * considering the local table and recursively from inner
+     * capabilities.
+     */
+    public Vector/*<Goal>*/ lookup(String name) {
+       Vector/*<Goal>*/ g = new Vector/*<Goal>*/();
+       if ( goals.get( name ) != null )
+           g.addAll( (Vector/*<Goal>*/) goals.get( name ) );
+       if ( inner != null ) {
+           for ( Iterator/*<Capability>*/ e =
+                     inner.iterator(); e.hasNext(); ) {
+               g.addAll( ((Capability)e.next()).lookup( name ) );
+           }
+       }
+       return g;
+    }
+
+    /**
+     * Add a goal to this capability.
+     */
+    public void addGoal(Goal g) {
+       Vector/*<Goal>*/ v = (Vector/*<Goal>*/) goals.get( g.getGoalName() );
+       if ( v == null ) {
+           v = new Vector/*<Goal>*/();
+           goals.put( g.getGoalName(), v );
+       }
+       v.add( g );
+    }
+
+    /**
+     * Add a plan to this capability.
+     */
+    public void addPlan(Plan g) {
+       addGoal( g );
+    }
+
+    /**
+     * Add an inner capability. All {@link #inquirables} of the added
+     * capability are added to the {@link #inquirables} of this
+     * capability, unless their name starts with underscore, or an
+     * equally named {@link Inquirable} is already defined.
+     *
+     * <p> Note that a capability tree built by adding children before
+     * grand children yield a different, less complete elevation of
+     * inquirables than if the grand children are added to the
+     * children first.
+     *
+     * <p> When adding a capability late (i.e., after the first
+     * shareInquirables invocation, or more precisely when {@link
+     * #deferred} is null), then {@link #shareInquirables} is invoked;
+     * it's only invoked on the added capability unless an inquirable
+     * was gained, in which case {@link #shareInquirables} is invoked
+     * on this capability, for distributing the gained inquirables
+     * over the whole capability sub tree.
+     *
+     * @see #shareInquirables(Collection)
+     */
+    public void addCapability(Capability c) {
+       if ( inner == null )
+           inner = new Vector/*<Capability>*/();
+       if ( ! inner.contains( c ) )
+           inner.add( c );
+       if ( performer != null ) {
+           c.setPerformer( performer );
+       }
+       boolean extended = false;
+       for ( Iterator/*<Inquirable>*/ si = c.inquirables.values().iterator();
+             si.hasNext(); ) {
+           Inquirable inquirable = (Inquirable) si.next();
+           String name = inquirable.getName();
+           if ( ( ! name.startsWith( "_" ) ) &&
+                inquirables.get( name ) == null ) {
+               putInquirable( inquirable );
+               extended = true;
+           }
+       }
+       if ( deferred == null ) {
+           if ( extended ) {
+               shareInquirables( null );
+           } else {
+               c.shareInquirables( inquirables.values() );
+           }
+       }
+    }
+
+    /**
+     * Creates a new {@link Rule} object with given antecedent and
+     * consequent {@link Query} objects.
+     */
+    public void addRule(Query a,Query c) {
+       if ( rules == null )
+           rules = new Vector/*<Rule>*/();
+       Rule r = new Rule( a, c );
+       rules.add( r );
+       if ( performer != null ) {
+           if ( Goal.isTracing() )
+               System.err.println( "** Installing " + r + " for " + performer );
+           performer.rule_set.add( r );
+       }
+    }
+
+    /**
+     * Utility method to lookup a given goal name.
+     */
+    public boolean hasGoal(String name) {
+       if ( goals.get( name ) != null )
+           return true;
+       if ( inner == null )
+           return false;
+       for ( Iterator/*<Capability>*/ ci = inner.iterator(); ci.hasNext(); ) {
+           if ( ((Capability) ci.next()).hasGoal( name ) )
+               return true;
+       }
+       return false;
+    }
+
+    /**
+     * Tells whether or not this capability provides some method or
+     * methods for all named goals.
+     */
+    public boolean hasGoals(String [] gs) {
+       if ( gs == null )
+           return true;
+       for ( int i = 0; i < gs.length; i++ ) {
+           if ( ! hasGoal( gs[i] ) )
+               return false;
+       }
+       return true;
+    }
+
+    /**
+     * Returns an Inquirable qua Store, or null if it's not a Store.
+     */
+    public Store getStore(String name) {
+       Object x = inquirables.get( name );
+       if ( x instanceof Store )
+           return (Store) x;
+       return null;
+    }
+
+    /**
+     * Returns the Inquirable as mapped, or null if not found.
+     */
+    public Inquirable getInquirable(String name) {
+       return (Inquirable) inquirables.get( name );
+    }
+
+    /**
+     * Registers an {@link Inquirable} to be mapped. A Capability
+     * extension should register all locally created inquirables.
+     */
+    public void putInquirable(Inquirable inquirable) {
+       inquirables.put( inquirable.getName(), inquirable );
+    }
+
+    /**
+     * Registers an {@link Inquirable} under an alias. A Capability
+     * extension should register all locally created inquirables.
+     */
+    public void putInquirable(String name,Inquirable inquirable) {
+       inquirables.put( name, inquirable );
+    }
+
+    /**
+     * This method extends the inquirables collection with the given
+     * collection, then propagates its map downwards to sub
+     * capabilities. Finally all {@link Deferred} entities held in
+     * {@link #deferred} are installed, before setting {@link
+     * #deferred} to null.
+     *
+     * <p> Note that a {@link Performer} (which is a Capability) will
+     * invoke this once the first time it is added to the {@link
+     * Executor default executor}. A multi {@link Executor} model may
+     * need additional, explicit initial calls to this method in order
+     * to establish complete inquirables sharing.
+     *
+     * @see #addCapability(Capability)
+     */
+    public void shareInquirables(Collection/*<Inquirable>*/ shared) {
+       if ( Goal.isTracing() ) {
+           System.err.println( "** Sharing inquirables in " + this );
+       }
+       // Install incoming inquirables unless defined locally
+       if ( shared != null ) {
+           for ( Iterator/*<Inquirable>*/ si =
+                     shared.iterator(); si.hasNext(); ) {
+               Inquirable inquirable = (Inquirable) si.next();
+               if ( inquirables.get( inquirable.getName() ) == null ) {
+                   putInquirable( inquirable );
+               }
+           }
+       }
+       // Propagate all local inquirables to inner capabilities
+       if ( inner != null ) {
+           shared = inquirables.values();
+           for ( Iterator/*<Capability>*/ i = inner.iterator();
+                 i.hasNext(); ) {
+                Capability c = (Capability) i.next();
+               c.shareInquirables( shared );
+           }
+       }
+       // Install deferred entities
+       if ( deferred != null ) {
+           for ( Iterator/*<Deferred>*/ di = deferred.iterator();
+                 di.hasNext(); ) {
+               ((Deferred) di.next()).install();
+           }
+       }
+       deferred = null;
+    }
+
+    /**
+     * Sets the performer for this capability, and propagates it to
+     * all inner capabilities.
+     */
+    public void setPerformer(Performer p) {
+       if ( performer != null ) {
+           if ( performer != p ) {
+               // The capability is moved
+               throw new Error( "Invalid GORITE model" );
+           }
+           return;
+       }
+       performer = p;
+       if ( rules != null ) {
+           for ( Iterator/*<Rule>*/ i = rules.iterator(); i.hasNext(); ) {
+               Rule r = (Rule) i.next();
+               performer.rule_set.add( r );
+           }
+       }
+       if ( inner != null ) {
+           for ( Iterator/*<Capability>*/ i =
+                     inner.iterator(); i.hasNext(); ) {
+               Capability c = (Capability) i.next();
+               c.setPerformer( p );
+           }
+       }
+       initialize();
+    }
+
+    /**
+     * Overridable method where to add Capability initialisation
+     * code. This method is invoked when the performer is set up, and
+     * all inner capabilites are initialized. This base implementation
+     * does not do anything.
+     */
+    public void initialize() {
+    }
+
+    /**
+     * Returns the performer that has this capability.
+     */
+    public Performer getPerformer() {
+       return performer;
+    }
+
+    /**
+     * Utility method that creates the goal of establishing a
+     * task team.
+     */
+    public Goal deploy(String name) {
+       return new Goal( "deploy " + name ) {
+           public States execute(Data d) {
+               String ttn = getGoalName().substring( 7 );
+               Team t = (Team) performer;
+               Team.TaskTeam tt = t.getTaskTeam( ttn );
+               if ( tt == null )
+                   throw new Error( "Missing task team '" + ttn +
+                                    "' in team '" + t.getName() + "'" );
+               if ( tt.establish( d ) )
+                   return States.PASSED;
+               return States.FAILED;
+           }
+       };
+    }
+
+    /**
+     * Adds a {@link Reflector} to this capability. The actual
+     * addition is deferred until after the inquirables are shared, at
+     * which time the (@link Performer} is known.
+     */
+    public void addReflector(
+       final String goal,final String todo,final Query query) {
+       add( new Deferred() {
+           /**
+            * Installs this as a proper {@link Reflector}.
+            */
+           public void install() {
+               Reflector r = new Reflector( getPerformer(), goal, todo );
+               r.addQuery( query );
+           }
+       } );
+    }
+
+    /**
+     * Returns the {@link #inquirables} collection.
+     */
+    public Hashtable/*<String,Inquirable>*/ getInquirables() {
+       return inquirables;
+    }
+
+    /**
+     * Return the goals table.
+     */
+    public Hashtable/*<String,Vector<Goal>>*/ getGoals() {
+       return goals;
+    }
+
+    /**
+     * Return the inner capabilities, if any.
+     */
+    public Vector/*<Capability>*/ getInner() {
+       return inner;
+    }
+}
diff --git a/com/intendico/gorite/ConditionGoal.java b/com/intendico/gorite/ConditionGoal.java
new file mode 100644 (file)
index 0000000..127b136
--- /dev/null
@@ -0,0 +1,117 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A ConditionGoal is achieved by achieving any sub goal, which are
+ * attempted in sequence. The goal fails when and if all sub goals
+ * have been attempted and failed.
+ */
+public class ConditionGoal extends Goal {
+
+    /**
+     * Constructor.
+     */
+    public ConditionGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public ConditionGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a ConditionGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new ConditionInstance( head );
+    }
+
+    /**
+     * Implements sequential, conditional sub goal execution
+     * attempt. I.e., if a sub goal fails, then try next, otherwise
+     * succeed.
+     */
+    public class ConditionInstance extends Instance {
+
+       /**
+        * Constructor.
+        */
+       public ConditionInstance(String h) {
+           super( h );
+       }
+
+       /**
+        * Next sub goal index.
+        */
+       public int index = 0;
+
+       /**
+        * Current ongoing Instance.
+        */
+       public Instance ongoing = null;
+
+       /**
+        * Cancels this execution by propagating the cancel call to
+        * all sub goals.
+        */
+       public void cancel() {
+           super.cancel();
+           if ( ongoing != null )
+               ongoing.cancel();
+       }
+
+       /**
+        * Instantiates and performs sub goals in sequence, until the
+        * first one that does not fail. If a sub goal fails, then
+        * that is caught, and the next sub goal in sequence is
+        * instantiated and performed.
+        */
+       public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           Goal [] subgoals = getGoalSubgoals();
+           if ( subgoals == null )
+               return States.FAILED;
+           while ( index < subgoals.length ) {
+               if ( ongoing == null ) {
+                   ongoing = subgoals[ index ].instantiate(
+                       head + "." + index, d );
+               }
+               States s = ongoing.perform( d );
+               if ( s != States.FAILED )
+                   return s;
+               index += 1;
+               ongoing = null;
+           }
+           return States.FAILED;
+       }
+    }
+}
diff --git a/com/intendico/gorite/Context.java b/com/intendico/gorite/Context.java
new file mode 100644 (file)
index 0000000..40da3c9
--- /dev/null
@@ -0,0 +1,107 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+import com.intendico.data.Condition;
+
+/**
+ * The Context interface is implemented by a plan (i.e. top level
+ * goal) that has different variants for different contexts. The
+ * different contexts are defined by means of a {@link Query} that
+ * provides multiple bindings, and each binding defines a variant
+ * execution of the plan.
+ * @see Plan
+ * @see BDIGoal
+ */
+
+public interface Context {
+
+    /**
+     * A constant that a {@link #context} method may return when a
+     * plan doesn't have any applicable variants. Returning this
+     * {@link Query}, which is eternally false, has the same effect as
+     * throwing a {@link None} exception in marking that the plan does
+     * not have any applicable variants.  The point is that a null
+     * return from the {@link #context} method means that there is no
+     * context query, and that the goal with this context is
+     * applicable once.
+     */
+    public static final Query EMPTY = new Condition() {
+           public boolean condition() {
+               return false;
+           }
+       };
+
+    /**
+     * A constant that a {@link #context} method may return when a
+     * plan has a single applicable variant without bindings to {@link
+     * Ref} objects. Returning this {@link Query}, which in fact is
+     * null, has the same effect as returning null in marking that the
+     * plan has a single applicable variant.  The point is that a null
+     * return from the {@link #context} method means that there is no
+     * context query, and therefore the plan with this context is
+     * applicable.
+     */
+    public static final Query TRUE = null;
+
+    /**
+     * A constant that a {@link #context} method may return when a
+     * plan doesn't have any applicable variants. This is merely an
+     * alias for {@link #EMPTY} added for convenience.
+     */
+    public static final Query FALSE = EMPTY;
+
+    /**
+     * An exception that a {@link #context} method may throw in
+     * order to mark that there is no applicable context.
+     * @see #EMPTY
+     */
+    public static class None extends Exception {
+
+       /**
+        * A default constructor.
+        */
+       public None() {
+           super( "no applicable context" );
+       }
+
+       /**
+        * Version identity required for serialization.
+        */
+       public static final long serialVersionUID = 1L;
+    }
+
+    /**
+     * The context method returns the {@link Query} that defines the
+     * multiple alternative contexts for the execution.  The {@link
+     * #context} method is invoked for defining the {@link Query}. The
+     * method may return null, which means that the plan has a single
+     * applicable variant, or it may return {@link #EMPTY} or throw
+     * the {@link None} excpetion, which both indicate that the plan
+     * does not have any applicable variant (right now). Any other
+     * query is taken as defining the alternative applicable contexts.
+     * @see BDIGoal.BDIInstance#contextual
+     */
+    public Query context(Data d) throws Exception;
+
+
+}
diff --git a/com/intendico/gorite/ContextualGoal.java b/com/intendico/gorite/ContextualGoal.java
new file mode 100644 (file)
index 0000000..abd52bb
--- /dev/null
@@ -0,0 +1,213 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.*;
+import java.util.Vector;
+
+/**
+ * This is a utility class used by BDIGoal execution, to associate a
+ * particular binding context with performing a goal.  It is not
+ * intended for explicit use within goal hierarchies.
+ *
+ * <p> The {@link BDIGoal} creates individual ContextualGoal objects
+ * for each applicable plan option where the plans {@link
+ * Context#context} method returns a {@link Query} that offers
+ * multiple bindings (i.e., not null, {@link Context#EMPTY} or {@link
+ * Context#FALSE}). Plan options whose {@link Context#context} method
+ * returns null are collated without the wrapping of a ContextualGoal
+ * object, and plan options whose {@link Context#context} method
+ * returns {@link Context#EMPTY} or {@link Context#FALSE} are not
+ * applicable.
+ *
+ * <p> The ContextualGoal captures the entry data settings for the
+ * variables as well as the proposed settings that defines the plan
+ * option.  It then manages the set up of the proposed settings into
+ * the data prior to executing the plan body, and the restoration of
+ * these settings if the plan body execution fails. (The variable
+ * settings are not restored if the ContextualGoal execution is
+ * cancelled)
+ *
+ * @see BDIGoal
+ */
+public class ContextualGoal extends Goal implements Precedence {
+
+    /**
+     * Holds the Data object for the calling BDIInstance that created this
+     * ContextualGoal.
+     */
+    public Data goal_data;
+
+    /**
+     * Holds the actual {@link Ref} objects of the context
+     * {@link Query}.
+     */
+    public Vector/*<Ref>*/ variables;
+
+    /**
+     * Holds {@link Ref} objects with values representing this
+     * invocation context.
+     */
+    public Vector/*<Ref>*/ context;
+
+    /**
+     * The original assignments for the variables.
+     */
+    public Vector/*<Ref>*/ original;
+
+    /**
+     * Two ContextualGoal are equal if they are of the same goal and
+     * have the same context.
+     */
+    public boolean equals(Object c) {
+       return c instanceof ContextualGoal && equals( (ContextualGoal) c );
+    }
+
+    /**
+     * Two ContextualGoal are equal if they are of the same goal
+     * and have the same context.
+     */
+    public boolean equals(ContextualGoal c) {
+       if ( ! c.getGoalSubgoals()[0].equals( getGoalSubgoals()[0] ) )
+           return false;
+       if ( context == null ) {
+           return c.context == null;
+       }
+       return Ref.equals( c.context, context );
+    }
+
+    /**
+     * Constructor.
+     */
+    public ContextualGoal(Vector/*<Ref>*/ orig,Vector/*<Ref>*/ vars,
+       Goal goal,Data data) {
+       super( goal.getGoalName() );
+       goal_data = data;
+       original = orig;
+       variables = vars;
+       if ( variables != null )
+           context = Ref.copy( variables );
+       setGoalSubgoals( new Goal[] { goal } );
+    }
+
+    /**
+     * A wrapping for invoking precedence of the wrapped goal. If the
+     * wrapped goal provides a precedence method, then it is invoked
+     * using the cached {@link #goal_data}, transiently establishing
+     * the invocation context, which is restored upon return.
+     */
+    public int precedence(Data data) {
+       if ( ! ( getGoalSubgoals()[ 0 ] instanceof Precedence ) )
+           return Plan.DEFAULT_PRECEDENCE;
+       if ( context == null )
+           return ((Precedence) getGoalSubgoals()[ 0 ])
+               .precedence( goal_data );
+       goal_data.set( context );
+       Ref.bind( variables, context );
+       int x = ((Precedence) getGoalSubgoals()[ 0 ]).precedence( goal_data );
+       Ref.bind( variables, original );
+       goal_data.get( context, false );
+       return x;
+    }
+
+    /**
+     * Return the intention instance to be executed.
+     */
+    public Instance instantiate(String head,Data d) {
+       // Assuming data == d without checking it
+       return new ContextualGoalInstance( head, d );
+    }
+
+    /**
+     * Utility class to represent the intention
+     */
+    public class ContextualGoalInstance extends Instance {
+
+       /**
+        * The target intention instance
+        */
+       public Instance instance;
+
+       /**
+        * Constructor. This established the invocation context in the
+        * intention {@link Data}.
+        */
+       public ContextualGoalInstance(String h,Data d) {
+           super( h );
+           if ( context != null ) {
+               d.set( context );
+               Ref.bind( variables, context );
+           }
+           String sub = context != null? Ref.toString( context ) : "{}";
+           instance = getGoalSubgoals()[ 0 ].instantiate( h + sub, d );
+       }
+
+       /**
+        * Control callback whereby the intention gets notified that
+        * it is cancelled. This will forward the cancellation to the
+        * currently progressing instance, if any.
+        */
+       public void cancel() {
+           if ( instance != null )
+               instance.cancel();
+       }
+
+       /**
+        * Excution of this goal, which means to execute the wrapped
+        * goal. If successful, the invocation {@link Ref} objects are
+        * updated from the final {@link Data}.  Execution catches
+        * LoopEndException, but not ParallelEndException, and makes
+        * it cause a failure rather then propagate up. If execution
+        * fails, the {@link Data} context is restored by removing the
+        * local context.
+        */
+       public States action(String head,Data d)
+           throws LoopEndException, ParallelEndException {
+           States s = States.FAILED;
+           try {
+               s = instance.perform( d );
+           } catch (LoopEndException e) {
+               s = States.FAILED;
+           } catch (ParallelEndException e) {
+               s = States.FAILED;
+               throw e; // Re-thrown
+           } finally {
+               if ( variables != null ) {
+                   if ( s == States.PASSED ) {
+                       d.get( variables, true );
+                   } else if ( s == States.FAILED ) {
+                       Ref.bind( variables, original );
+                       d.get( context, false );
+                   }
+               }
+           }
+           return s;
+       }
+
+    } // End of class ContextualGoalInstance
+
+    /**
+     * Textual representation of this goal.
+     */
+    public String toString() {
+       return ( context != null? context.toString() : "{}" ) +
+           getGoalSubgoals()[ 0 ].toString();
+    }
+}
diff --git a/com/intendico/gorite/ControlGoal.java b/com/intendico/gorite/ControlGoal.java
new file mode 100644 (file)
index 0000000..db7b344
--- /dev/null
@@ -0,0 +1,115 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A ControlGoal is achieved by performing all its sub goals in
+ * sequence, which results in a control effect on an enclosing
+ * parallel execution by terminating all other branches when it
+ * succeeds. Thus, the execution either fails, or continues after the
+ * enclosing parallel goal.
+ *
+ * <p> The following code snippet illustrates an optional interrupt,
+ * i.e., how to either interrupt or succeed (rather than either
+ * interrupt or fail).
+ * <pre>
+ * new FailGoal( "not interrupting is fine", new Goal [] {
+ *     new ControlGoal( "maybe interrupt", new Goal {
+ *        // Goal sequence for causing interrupt if achieved successfully
+ *     } );
+ * }
+ * </pre>
+ *
+ * <p> Note that the ControlGoal effect propagates up to the nearest
+ * parallel type goal through {@link BDIGoal} execution, and it
+ * therefore is not necessary for the ControlGoal to be explicitly
+ * within the sub goal hiearachy of the parallel type goal of
+ * concern.
+ *
+ * <p> In particular, a {@link Plan} of a {@link Team.Role} may
+ * include a ControlGoal to express that a {@link TeamGoal} is
+ * achieved by any one filler rather than requiring all of them.
+ *
+ * @see ParallelGoal
+ * @see RepeatGoal
+ * @see TeamGoal
+ * @see FailGoal
+ * @see Team.Role
+ */
+public class ControlGoal extends SequenceGoal {
+
+    /**
+     * Constructor.
+     */
+    public ControlGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public ControlGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a ControlGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new ControlInstance( head );
+    }
+
+    /**
+     * Implements a control action for parallel execution.
+     */
+    public class ControlInstance extends SequenceInstance {
+
+       /**
+        * Constructor.
+        */
+        public ControlInstance(String h) {
+            super( h );
+        }
+
+        /**
+         * Process all subgoals in sequence, then throw a
+        * LoopEndException if success. If a subgoals fail, then fail
+        * without throwing exception.
+         */
+        public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           States s = super.action( head, d );
+           if ( s == States.PASSED ) {
+               throw new ParallelEndException( head );
+           }
+           return s;
+       }
+    }
+
+
+}
diff --git a/com/intendico/gorite/Data.java b/com/intendico/gorite/Data.java
new file mode 100644 (file)
index 0000000..877c2e3
--- /dev/null
@@ -0,0 +1,935 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.Enumeration;
+import java.util.Formatter;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+ * This is a container class for data elements that goals use and
+ * produce.
+ *
+ * <p> The data that a business process is instantiated for has an
+ * internal structure which allows references to individual data
+ * elements, and that a data element is either singular or a plurality
+ * of other data elements.
+ *
+ * <p> A model includes data references, primarily in connection with
+ * task goals where the data references identify the data elements
+ * that the associated tasks are to operate on, but also in connection
+ * with repeat goals, which get instantiated for the constituent
+ * elements of a plural element.
+ *
+ * <p> Any actual transformation of data is done in tasks, and the
+ * business process model is limited to data reference.
+ *
+ * <p> A task is associated with two collections of data elements,
+ * known as inputs and results. The association is defined by means of
+ * using data element names, which are symbols that refer to
+ * individual or groups of data elements.
+ *
+ * <p> A data object is a container for data references, which are
+ * names of inidividual or groups of data elements.
+ */
+public class Data {
+
+    /**
+     * This is a base class for data elements.
+     */
+    public class Element {
+
+       /**
+        * The name of this data element.
+        */
+       public final String name;
+
+       /**
+        * The values of this data element.
+        */
+       public Vector/*<Object>*/ values = new Vector/*<Object>*/();
+
+       /**
+        * Constructor with initial value
+        */
+       public Element(String n,Object v) {
+           name = n;
+           if ( v != null )
+               values.insertElementAt( v, 0 );
+       }
+
+       /**
+        * Constructor.
+        */
+       public Element(String n) {
+           this( n, null );
+       }
+
+       /**
+        * Tracking whether this element is ready or not.
+        */
+       public boolean ready = false;
+
+       /**
+        * Mark data as ready.
+        */
+       public synchronized void markReady() {
+           ready = true;
+       }
+
+       /**
+        * Utility method to set value and mark ready.
+        */
+       public void set(Object v)
+       {
+           set( v, true );
+       }
+
+       /**
+        * Utility method to set value and maybe mark ready.
+        */
+       public void set(Object v,boolean mark)
+       {
+           values.insertElementAt( v, 0 );
+           if ( goldfish )
+               forget( 1 );
+           if ( mark )
+               markReady();
+       }
+
+       /**
+        * Utility method to return top of values.
+        */
+       public Object get() {
+           return values.size() > 0? values.get( 0 ) : null ;
+       }
+
+       /**
+        * Utility method to return and remove top of values.
+        */
+       public Object pop() {
+           if ( values.size() == 0 )
+               return null;
+           return values.remove( 0 );
+       }
+
+       /**
+        * Utility method to forget all but N values. A given N less
+        * than 1 results in silently using 1.
+        */
+       public void forget(int n) {
+           if ( n >= 0 ) {
+               while ( values.size() > n ) {
+                   values.remove( n );
+               }
+           } else {
+               while ( n++ < 0 && values.size() > 0 ) {
+                   values.remove( 0 );
+               }
+           }
+       }
+
+       /**
+        * Returns the i:th sub element, if any, or null otherwise.
+        */
+       public Object get(int i)
+       {
+           return ( i < values.size() )? values.get( i ): null;
+       }
+
+       /**
+        * Returns the number of values.
+        */
+       public int size() {
+           return values.size();
+       }
+
+       /**
+        * Text representation of this data element.
+        */
+       public String toString() {
+           /********* 1.5
+           Formatter f = new Formatter( new StringBuilder() );
+           f.format( "\"%s\" ", name );
+           if ( ready ) {
+               f.format( "%s", values.toString() );
+           } else {
+               f.format( "[not ready]" );
+           }
+           return f.toString();
+           **********/
+           StringBuilder s = new StringBuilder();
+           s.append( "\"" );
+           s.append( name );
+           s.append( "\" " );
+           if ( ready ) {
+               s.append( values.toString() );
+           } else {
+               s.append( "[not ready]" );
+           }
+           return s.toString();
+       }
+    }
+
+    /**
+     * Utility method to attach an observer to a given observable, to
+     * be used for triggering continuation of blocked intention.
+     */
+    public void setTrigger(Observable s) {
+       getBindings().setTrigger( s );
+    }
+
+    /**
+     * Utility method to attach an observer to a given query, to be
+     * used for triggering continuation of blocked intention. The
+     * query is reset and re-tested upon notification, and if true,
+     * the blocked intention is allowed to continue.
+     */
+    public void setTrigger(Query q) {
+       getBindings().setTrigger( q );
+    }
+
+    /**
+     * Utility method to remove a trigger.
+     */
+    public void clearTrigger() {
+       getBindings().clearTrigger();
+    }
+
+    /**
+     * Utility method to check whether a goal instance execution is
+     * running or not. All instance executions are considered running
+     * unless there a trigger is set via a {@link #setTrigger
+     * setTrigger} call, and still armed.
+     */
+    public boolean isRunning(String thread_name) {
+       return ! ((Bindings) bindings.get( thread_name )).monitoring ;
+    }
+
+    /**
+     * Utility method to attach an observer on the intention-local
+     * bindings. The observer will then be notified when any trigger
+     * (see {@link #setTrigger setTrigger}) fires.
+     */
+    public void addObserver(String thread_name,Observer x) {
+       ((Bindings) bindings.get( thread_name )).addObserver( x );
+    }
+
+    /**
+     * Utility method to remove a trigger observer.
+     */
+    public void deleteObserver(String thread_name,Observer x) {
+       ((Bindings) bindings.get( thread_name )).deleteObserver( x );
+    }
+    
+    /**
+     * A class for allowing transient name space split.
+     */
+    public class Bindings extends Observable {
+
+       /**
+        * The intention thread name that caused this Bindings
+        * object.
+        */
+       private String name;
+
+       /**
+        * A bindings-local flag to indicate that the associated
+        * intention is has an unblocking monitor.
+        */
+       public boolean monitoring = false;
+
+       /**
+        * Utility method to clear the monitoring flag.
+        */
+       void clearTrigger() {
+           monitoring = false;
+       }
+
+       /**
+        * Utility method to clear the monitoring flag and propagate
+        * notification to any observer.
+        */
+       void clearMonitoring() {
+           if ( monitoring ) {
+               if ( Goal.isTracing() ) {
+                   System.err.println(
+                       "** Clearing monitoring  for " + name );
+               }
+               monitoring = false;
+               setChanged();
+               notifyObservers();
+                unsetFlag();
+               //setFlag(); // and wake executor....
+               Performer p = (Performer) getValue( Goal.PERFORMER );
+               if ( p != null ) {
+                   p.signalExecutor();
+               }
+           }
+       }
+
+       /**
+        * Utility method to attach an Observer to the given
+        * Observable, to capture its notification as a trigger.
+        */
+       public void setTrigger(Observable s) {
+           monitoring = true;
+            setFlag();
+           s.addObserver( new Observer() {
+                   public void update(Observable x,Object y) {
+                       if ( Goal.isTracing() ) {
+                           System.err.println(
+                               "** Observer updated by " + x +
+                               " for " + name );
+                       }
+                       if ( monitoring )
+                           clearMonitoring();
+                       x.deleteObserver( this );
+                   }
+               } );
+       }
+
+       /**
+        * Utility class to observe a query, to allow an intention to
+        * continue when the query becomes true.
+        */
+       private class QueryMonitor implements Observer {
+
+           /**
+            * The query being observed.
+            */
+           Query query;
+
+           /**
+            * Constructor.
+            */
+           QueryMonitor(Query q) {
+               monitoring = true;
+               query = q;
+               if ( q != null )
+                   q.addObserver( this );
+               update( null, null );
+           }
+
+           /**
+            * The Observer callback method. This will call reset()
+            * and next() on the given query, and optionally reset
+            * monitoring and call setFlag().
+            */
+           synchronized public void update(Observable x,Object y) {
+               if ( Goal.isTracing() ) {
+                   System.err.println(
+                       "** Query " + query + " updated by " + x );
+               }
+               if ( ! monitoring ) {
+                   if ( query != null )
+                       query.deleteObserver( this );
+                   return;
+               }
+               boolean test = false;
+               try {
+                   if ( Goal.isTracing() ) {
+                       System.err.println( "** Query reset on " + query );
+                   }
+                   if ( query != null )
+                       query.reset();
+                   if ( Goal.isTracing() ) {
+                       System.err.println( "** Query next on " + query );
+                   }
+                   test = ( query != null )? query.next() : true;
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "** Query " + query + " is " + test );
+                   }
+               } catch (Exception e) {
+                   e.printStackTrace();
+               }
+               if ( test ) {
+                   clearMonitoring();
+                   if ( query != null )
+                       query.deleteObserver( this );
+               }
+               if ( Goal.isTracing() ) {
+                   System.err.println(
+                       "** Query data: " + Data.this.toString() );
+               }
+           }
+
+       }
+
+       /**
+        * Utility method to attach an Observer to the given Query, to
+        * capture its becoming true as a trigger.
+        */
+       public void setTrigger(Query q) {
+           new QueryMonitor( q );
+       }
+
+       /**
+        * This is the transient data store for an execution path.
+        */
+       public Hashtable/*<String,Element>*/ elements =
+           new Hashtable/*<String,Element>*/();
+
+       /**
+        * Link to a prior Bindings object; the base of this one.
+        */
+       Bindings prior = null;
+
+       /**
+        * Constructor.
+        */
+       public Bindings(String n,Bindings p)
+       {
+           name = n;
+           prior = p;
+       }
+
+       /**
+        * Special constructor to transiently bind a given name to its
+        * i:th sub element, if it exists.
+        */
+       public Bindings(String n,Bindings p,String name,int i)
+       {
+           this.name = n;
+           prior = p;
+           if ( name != null ) {
+               Element e = Data.this.find( p, name );
+               if ( e != null ) {
+                   Object v = e.get( i );
+                   if ( v != null ) {
+                       Element x = new Element( name, v );
+                       x.markReady();
+                       elements.put( name, x );
+                   }
+               }
+           }
+       }
+
+       /**
+        * Returns the named data elements, if any, or null otherwise.
+        */
+       public Element find(String name)
+       {
+           return (Element) elements.get( name );
+       }
+
+       /**
+        * Utility method to create an Element instance.
+        */
+       public Element create(String name) {
+           Element e = (Element) elements.get( name );
+           if ( e == null ) {
+               e = new Element( name );
+               elements.put( name, e );
+           }
+           return e;
+       }
+
+       /**
+        * Utility method that pushes all data except the given one
+        * into the prior bindings.
+        */
+       public void push(String except,HashSet/*<String>*/ h) {
+           if ( prior == null )
+               return;
+           boolean gf = goldfish;
+           goldfish = false;
+           for ( Enumeration/*<String>*/ i = elements.keys();
+                 i.hasMoreElements(); ) {
+               String key = (String) i.nextElement();
+               if ( key.equals( except ) )
+                   continue;
+               h.add( key );
+               Element e = prior.create( key );
+               e.set( ((Element) elements.get( key )).get(), false );
+           }
+           goldfish = gf;
+       }
+
+       /**
+        * Returns a String representation of this Bindings object.
+        */
+       public String toString() {
+           /************ 1.5
+           Formatter f = new Formatter( new StringBuilder() );
+           f.format( "Data.Bindings %s", name );
+           if ( prior != null )
+               f.format( " [prior = %s]", prior.name );
+           for (Iterator/*<String>* / i =
+                  elements.keySet().iterator(); i.hasNext(); ) {
+           f.format( "\n  %s", elements.get( i.next() ).toString() );
+           }
+           return f.toString();
+           ***************/
+
+           StringBuilder s = new StringBuilder();
+           s.append( "Data.Bindings " );
+           s.append( name );
+           if ( prior != null ) {
+               s.append( " [prior = " );
+               s.append( prior.name );
+               s.append( "]" );
+           }
+           for (Iterator/*<String>*/ i =
+                    elements.keySet().iterator(); i.hasNext(); ) {
+               s.append( "\n  " );
+               s.append( elements.get( i.next() ).toString() );
+           }
+           return s.toString();
+       }
+    }
+
+    /**
+     * Flags this data into goldfish mode, which means for data
+     * elements to forget past values when new values are set.
+     */
+    public boolean goldfish = false;
+
+    /**
+     * Keeps the 'global' bindings object.
+     */
+    public Hashtable/*<String,Bindings>*/ bindings =
+        new Hashtable/*<String,Bindings>*/();
+
+    /**
+     * Utility method that links a thread name with the current
+     * bindings. This may be overridden later by parallel goals, which
+     * get their local bindings.
+     */
+    public void link(String thread_name) {
+       bindings.put( thread_name, getBindings() );
+    }
+
+    /**
+     * Utility method to remove a bindings entry.
+     */
+    public void dropBindings(String thread_name) {
+       bindings.remove( thread_name );
+    }
+
+    /**
+     * Utility method to locate an element from within a "thread".
+     */
+    public Element getLocalElement(String thread_name,String name) {
+       Bindings b = (Bindings) bindings.get( thread_name );
+       return find( b != null? b : getBindings(), name );
+    }
+
+    /**
+     * Utility method that creates a thread-local binding for the
+     * given name to its i:th sub element. If name is null, or the
+     * i:th sub element missing, a thread local bindings object is
+     * still created, but without initialising the name.
+     */
+    public synchronized void fork(String name,int i,String thread_name)
+    {
+       bindings.put( thread_name,
+                     new Bindings( thread_name, getBindings(), name, i ) );
+    }
+
+    /**
+     * Utility method to push data. All names of the given HashSet
+     * except the exception are pushd down to be sub elements of the
+     * equally named element of the prior bindings object.
+     */
+    public synchronized void join(
+       String except,HashSet/*<String>*/ h,String thread_name) {
+       if ( h != null )
+           getBindings().push( except, h );
+       dropBindings( thread_name );
+    }
+       
+    /**
+     * Obtains the bindings object chain for the calling thread.
+     */
+    public Bindings getBindings() {
+       Bindings b = (Bindings) bindings.get( thread_name );
+       if ( b == null ) {
+           b = new Bindings( thread_name, null );
+           bindings.put( thread_name, b );
+       }
+       return b;
+    }
+
+    //
+    // Tracking of threads and data dependencies.
+    //
+
+    /**
+     * Holds the current "thread name".
+     */
+    public String thread_name = "X";
+
+    /**
+     * Change thread name and return the prior name.
+     */
+    public String setThreadName(String n) {
+       String old = thread_name;
+       thread_name = n;
+       return old;
+    }
+
+    /**
+     * Determine the call data element name from a given head string
+     * or thread_name string.
+     */
+    public String getArgsName(String name) {
+       int i = name.indexOf( '"' );
+       if ( i < 0 )
+           i = name.length();
+       i = name.lastIndexOf( '*', i );
+       if ( i < 0 ) 
+           i = name.length();
+       return name.substring( 0, i ) + "-ARG";
+    }
+
+    /**
+     * Determine the call data element name from the thread_name, and
+     * return its value.
+     * @see BDIGoal
+     */
+    public Object getArgs(String head) {
+       return getValue( getArgsName( head ) );
+    }
+
+    /**
+     * The most recently assigned arg.
+     */
+    public Object arg;
+
+    /**
+     * Set up the call data agrument for the given instance head name
+     * as given, adding the "-ARG" suffix.
+     */
+    public void setArgs(String name,Object x) {
+       arg = x;
+       if ( x != null ) {
+           setValue( name + "-ARG", x );
+       }
+    }
+
+    //
+    // Normal mode accessors
+    //
+
+    /**
+     * Utility method to obtain data element value.
+     */
+    public Object getValue(String name) {
+       Element e = find( name );
+       return e != null? e.get() : null ;
+    }
+
+    /**
+     * Utility method to obtain data element value as seen by a given
+     * "thread".
+     */
+    public Object getValue(String name,String th_name) {
+       Bindings b = (Bindings) bindings.get( th_name );
+       if ( b == null )
+           return getValue( name );
+       Element e = find( b, name );
+       return e != null? e.get() : getValue( name ) ;
+    }
+
+    /**
+     * Utility method to set data element value.
+     */
+    public Data setValue(String name,Object v) {
+       create( name ).set( v );
+       return this;
+    }
+
+    /**
+     * Utility method to set data element value relative a given
+     * "thread".
+     */
+    public Data setValue(String th_name, String name,Object v) {
+       String old = setThreadName( th_name );
+       create( name ).set( v );
+       setThreadName( old );
+       return this;
+    }
+
+    /**
+     * Utility method to delete all elements for a name along the
+     * whole binding chain.
+     */
+    public void forget(String name) {
+        for ( Bindings b = getBindings(); b != null; b = b.prior ) {
+           b.elements.remove( name );
+       }
+    }
+
+    /**
+     * Utility method to forget all but n values for an element (i.e.,
+     * its "innermost" Binding occurrence).  If n < 0, then -n top
+     * values are forgotten. If n == 0, then everyhing is forgotten,
+     * except that the element remains.
+     */
+    public void forget(String name,int n)
+    {
+       Element e = find( name );
+       if ( e != null ) {
+           e.forget( n );
+       }
+    }
+
+    /**
+     * Utility method to replace top data element value.
+     */
+    public Data replaceValue(String name,Object v)
+    {
+       Element e = create( name );
+       e.pop();
+       e.set( v );
+       return this;
+    }
+
+    /**
+     * Utility method to restore to a prior value. 
+     */
+    public Data restoreValue(String name,Object v) {
+       Element e = create( name );
+       e.pop();
+       if ( goldfish || e.size() == 0 ) {
+           e.set( v );
+       }
+       return this;
+    }
+
+    /**
+     * Find a given data element following the bindings chain of the
+     * calling thread.
+     */
+    public synchronized Element find(String name) {
+       return find( getBindings(), name );
+    }
+
+    /**
+     * Find a given data element following the bindings chain of the
+     * calling thread.
+     */
+    public Element find(Bindings b,String name) {
+       for ( ; b != null; b = b.prior ) {
+           Element e = (Element) b.elements.get( name );
+           if ( e != null )
+               return e;
+       }
+       return null;
+    }
+
+    /**
+     * Tells whether there is an element of a given name in the
+     * bindings object chain of the calling thread or not
+     */
+    public boolean hasElement(String name)
+    {
+       return find( name ) != null;
+    }
+
+    /**
+     * Utility to tell how many values a data element has.
+     */
+    public int size(String name) {
+       Element e = find( name );
+       return e != null? e.values.size() : 0;
+    }
+
+    /**
+     * Utility method to retrieve an Element instance on the calling
+     * thread's bindings chain, or create one at the head of the chain
+     * if missing.
+     */
+    public synchronized Element create(String name) {
+       Element e = find( name );
+       if ( e == null ) {
+           e = getBindings().create( name );
+       }
+       return e;
+    }
+
+    /**
+     * This method is used for querying about readiness of a data
+     * element. The method returns immediately, with true if the
+     * element is missing or not ready, and false otherwise.
+     */
+    public boolean pending(String name) {
+       Element e = find( name );
+       return e == null || ! e.ready ;
+    }
+
+    /**
+     * Marks a named element as ready. If no such element exists, then
+     * this method will create a new one.
+     */
+    public void ready(String name) {
+        create( name ).markReady();
+    }
+
+    /**
+     * Set data elements from the {@link Ref} objects of the given
+     * {@link Query}.
+     */
+    public void set(Query query) {
+       set( query.getRefs( new Vector/*<Ref>*/() ) );
+    }
+
+    /**
+     * Set data elements from the given {@link Ref} objects, using
+     * {@link #replaceValue}.
+     */
+    public void set(Vector/*<Ref>*/ refs) {
+       for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); ) {
+           Ref ref = (Ref) i.next();
+           //setValue( ref.getName(), ref.get() );
+           replaceValue( ref.getName(), ref.get() );
+       }
+    }
+
+    /**
+     * Set the {@link Ref} objects of a {@link Query} from data
+     * elements.
+     */
+    public void get(Query query,boolean keep) {
+       get( query.getRefs( new Vector/*<Ref>*/() ), keep );
+    }
+
+    /**
+     * Set the given {@link Ref} objects from data elements.
+     */
+    //@SuppressWarnings("unchecked")
+    public void get(Vector/*<Ref>*/ refs,boolean keep) {
+        for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); ) {
+            Ref ref = (Ref) i.next();
+           Element e = find( ref.getName() );
+           if ( e != null ) {
+               ref.set( keep? e.get() : e.pop() );
+           }
+        }
+    }
+
+    /**
+     * Set the given {@link Ref} object from its data element, without
+     * popping the data element value.
+     */
+    //@SuppressWarnings("unchecked")
+    public void get(Ref/*...*/ [] refs) {
+       for ( int i = 0; i < refs.length; i++ ) {
+           Element e = find( refs[ i ].getName() );
+           if ( e != null )
+               refs[ i ].set( e.get() );
+       }
+    }
+
+    //
+    // Thread control support
+    //
+
+    /**
+     * A rendezvous flag.
+     */
+    public boolean flag = true;
+
+    /**
+     * Utility method to raise the {@link #flag}.
+     */
+    public synchronized void setFlag() {
+       if ( ! flag ) {
+           flag = true;
+           notifyAll();
+       }
+    }
+    
+    public synchronized void unsetFlag() {
+       if ( flag ) {
+           flag = false;
+           notifyAll();
+       }
+    }
+
+    /**
+     * Utility method to wait for the rendezvous {@link #flag} to be
+     * set.
+     */
+    public synchronized void waitFlag() {
+        while (!flag) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+            }
+        }
+        flag = false;
+    }
+
+    /**
+     * Returns a String representation of this Data object.
+     */
+    public String toString() {
+       /************ 1.5
+       Formatter f = new Formatter( new StringBuilder() );
+       boolean nl = false;
+       HashSet/*<String>* / done = new HashSet/*<String>* /();
+       for ( Iterator/*<String>* / i =
+                 bindings.keySet().iterator(); i.hasNext(); ) {
+           String tn = (String) i.next();
+           if ( nl )
+               f.format( "\n" );
+           Bindings b = bindings.get( tn );
+           String h = "Data.Bindings " + b.name;
+           if ( ! done.contains( b.name ) )
+               h = b.toString();
+           f.format( "Intention %s --> %s", tn, h );
+           done.add( b.name );
+           nl = true;
+       }
+       return f.toString();
+       **************/
+       StringBuilder s = new StringBuilder();
+       boolean nl = false;
+       HashSet/*<String>*/ done = new HashSet/*<String>*/();
+       for ( Iterator/*<String>*/ i =
+                 bindings.keySet().iterator(); i.hasNext(); ) {
+           String tn = (String) i.next();
+           if ( nl )
+               s.append( "\n" );
+           Bindings b = (Bindings) bindings.get( tn );
+           String h = "Data.Bindings " + b.name;
+           if ( ! done.contains( b.name ) )
+               h = b.toString();
+           s.append( "Intention " );
+           s.append( tn );
+           s.append( " --> " );
+           s.append( h );
+           done.add( b.name );
+           nl = true;
+       }
+       return s.toString();
+    }
+}
diff --git a/com/intendico/gorite/DataGoal.java b/com/intendico/gorite/DataGoal.java
new file mode 100644 (file)
index 0000000..5b0e778
--- /dev/null
@@ -0,0 +1,83 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+/**
+ * A DataGoal is a goal that uses the value of the data element named
+ * by the control attribute as the actual goal to achieve.  This value
+ * is accessed and considered whenever the DataGoal is instantiated
+ * (via {@link #instantiate(String,Data)}), where it either is a
+ * {@link Goal}, a {@link Goal.Instance}, or some other non-null
+ * object.
+ * <ul>
+ *
+ * <li> If the value is null, the DataGoal will instantiate as a
+ * FailGoal, and thus fail when executed.
+ *
+ * <li> A Goal is instantiated and executed in lieu of the DataGoal,
+ * and likewise for a Goal.Instance, which is used as given and
+ * executed in lieu of the DataGoal.
+ *
+ * Note that a Goal.Instance execution may then a different Data
+ * object than for the DataGoal instantiation, and if so, there is no
+ * support for data transfer between the Data objects. Further, the
+ * Goal.Instance execution progresses from whatever execution state it
+ * has.
+ *
+ * <li> For any other type of object, the DataGoal creates a vacuous
+ * BDIGoal for the object, and that BDIGoal is instantiated and
+ * executed in lieu of the DataGoal.
+ * </ul>
+ *
+ * <p> Except for Goal.Instance values (which are already
+ * instantiated), the DataGoal goal adds the suffix "[xx]", where xx
+ * is the control data name, to the intention head, as its marker in
+ * an execution trace output.
+ */
+public class DataGoal extends Goal {
+
+    /**
+     * Constructor with goal name and control data name.
+     */
+    public DataGoal(String n,String c) {
+       super( n );
+       setGoalControl( c );
+    }
+
+    /**
+     * Overrides the base class method {@link Goal#instantiate} to
+     * provide the instance for the data goal.
+     */
+    public Instance instantiate(String h,Data d) {
+       Object g = d.getValue( getGoalControl() );
+       if ( g instanceof Instance ) {
+           return (Instance) g;
+       }
+       String hh = h + "[" + getGoalControl() + "]";
+       if ( g instanceof Goal ) {
+           return ((Goal) g).instantiate( hh, d );
+       }
+       if ( g != null ) {
+           return new BDIGoal( g ).instantiate( hh, d );
+       }
+       return new FailGoal( "Data getGoalControl() is null" ).
+           instantiate( hh, d );
+    }
+}
diff --git a/com/intendico/gorite/EndGoal.java b/com/intendico/gorite/EndGoal.java
new file mode 100644 (file)
index 0000000..891de31
--- /dev/null
@@ -0,0 +1,127 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * An EndGoal is achieved by means of achieving its sub goals in
+ * sequence, and it will then cause an enclosing {@link LoopGoal} goal
+ * to be achieved. If any sub goal fails, the EndGoal will just
+ * succeed without causing the enclosing {@link LoopGoal} to
+ * succeed. An EndGoal never fails; either it succeeds or it causes
+ * the enclosing {@link LoopGoal} to succeed.
+ *
+ * The usage pattern for {@link LoopGoal} and EndGoal is as follows:
+ *
+ * <pre>
+ * new LoopGoal( "loop", new Goal [] {
+ *     .. // steps in the loop
+ *     .. new EndGoal( "break if", new Goal [] {
+ *            // optional sub goals for ending the loop if they succeed
+ *        } )
+ *     .. // more steps in the loop
+ * } )
+ * </pre>
+ *
+ * <p> There may be any number of {@link EndGoal} to end the loop
+ * although of course only one will take effect. The {@link EndGoal}
+ * may be at any level within the LoopGoal goal hierarchy, except
+ * nested within another LoopGoal.
+ *
+ * <p> The EndLoop effect may be thought of as throwing an {@link
+ * Exception} that the nearest enclosing {@link LoopGoal} catches,
+ * interrupting the progress of any intermediate composite goal,
+ * including {@link ParallelGoal} and {@link RepeatGoal}.  Ongoing
+ * branches of intermediate parallel goals will be cancelled.
+ *
+ * <p> The EndLoop effect does not propagate up through a {@link
+ * BDIGoal} execution of a {@link Plan} with non-null {@link
+ * Context#context(Data)} {@link Query}. I.e., the EndGoal must
+ * typically be explicitely within the sub goal hierarchy of the
+ * {@link LoopGoal} of concern.
+ *
+ * <p> However, the EndGoal effect does propagate up through a {@link
+ * BDIGoal} execution if the {@link Plan} has a null {@link
+ * Context#context(Data)}, or is an implementing {@link Goal}
+ * hierarchy without {@link Context}.
+ * 
+ * @see LoopGoal
+ * @see SequenceGoal
+ * @see ParallelGoal
+ * @see RepeatGoal
+ */
+public class EndGoal extends SequenceGoal {
+
+    /**
+     * Constructor.
+     */
+    public EndGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public EndGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a EndGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new EndInstance( head );
+    }
+
+    /**
+     * Implements a loop-end action ("break")
+     */
+    public class EndInstance extends SequenceInstance {
+
+       /**
+        * Constructor.
+        */
+        public EndInstance(String h) {
+            super( h );
+        }
+
+        /**
+         * Process all subgoals in sequence, then throw a
+        * LoopEndException if success. If subgoals fail, then succeed
+        * without throwing exception.
+         */
+        public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           States s = super.action( head, d );
+           if ( s == States.PASSED )
+               throw new LoopEndException( head );
+           if ( s == States.FAILED )
+               return States.PASSED;
+           return s;
+       }
+    }
+}
diff --git a/com/intendico/gorite/Executor.java b/com/intendico/gorite/Executor.java
new file mode 100644 (file)
index 0000000..b625089
--- /dev/null
@@ -0,0 +1,440 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import java.util.Vector;
+import java.util.Iterator;
+import com.intendico.data.Ref;
+
+/**
+ * The Executor class implements functions for managing goal execution
+ * for a collection of performers. More precisely, it implements the
+ * dispatch thread control to the {@link Performer.TodoGroup} goal
+ * executions.
+ *
+ * <p> System execution may be controlled by invoking the {@link
+ * Performer#performGoal} method for the top level team. This will
+ * lead the execution thread into the Executor for progressing all
+ * {@link Performer.TodoGroup} executions.
+ *
+ * <p> The Executor class also includes a control loop, the {@link
+ * #run} method, for using a separate thread to run performers. This
+ * is an alternative execution principle to the use of {@link
+ * Performer#performGoal}. It's not a good idea to use both.
+ *
+ * <p> Ordinarily the use of the Executor class is transparent to an
+ * application developer. A single Executor object is created so as to
+ * provide thread control dispatch to all performers, and the top
+ * level {@link Performer#performGoal} utilizes this as needed.
+ *
+ * <p> An application may split the performers amongst several
+ * executors, and create background threads for {@link
+ * Performer.TodoGroup} executions. In that case, the developer must
+ * take precaution against multi-threading interferences, and in
+ * particular refrain from using {@link Performer#performGoal}.
+ *
+ * <p> The transfer of control between goal executions uses the {@link
+ * #steps} variable, which tells how many goal steps an execution may
+ * advance before being stopped to allow the thread to pursue another
+ * goal execution.
+ */
+public class Executor implements Runnable {
+
+    /**
+     * This is a callback interface for monitoring the executor.
+     */
+    public interface Listener {
+       /**
+        * This method is invoked when the Executor is going idle.
+        */
+       public void goingIdle(Executor e);
+       /**
+        * This method is invoked when the Executor is waking up.
+        */
+       public void wakingUp(Executor e);
+    }
+
+
+    /**
+     * Holds the current default executor that performers add
+     * themselves to by default. Initially <em>null</em>, but one is
+     * set up on demand if needed by {@link #getDefaultExecutor()}.
+     */
+    public static Executor default_executor = null;
+
+    /**
+     * Returns the default executor, and creates one if needed.
+     */
+    public static Executor getDefaultExecutor() {
+       if ( default_executor == null )
+           default_executor = new Executor( "[gorite]" );
+       return default_executor;
+    }
+
+    /**
+     * A name for the executor. This is also used as thread name for
+     * the executor's dedicated thread.
+     */
+    public String name;
+
+    /**
+     * Configuration value for how many {@link #changeFocus()}
+     * invocations to count between it returning <em>true</em>.
+     */
+    public int steps = 40;
+
+    /**
+     * Dynamic counter of how many {@link #changeFocus()} invocations
+     * remain until it will return <em>true</em>.
+     */
+    public int count = steps;
+
+    /**
+     * The {@link Performer} objects managed by this executor.
+     */
+    public Vector/*<Performer>*/ performers = new Vector/*<Performer>*/();
+
+    /**
+     * A dynamic state variable.
+     */
+    public boolean running = true;
+
+    /**
+     * A dynamic state variable. Used by the {link #stop} method for
+     * telling the {@link #run} method to stop and exit when
+     * convenient. Value 1 is a "hard stop", that takes effect even
+     * while performers have things to do, and value 2 is a "soft
+     * stop" that takes effect when all performers are blocking.
+     */
+    public int stopping = 0;
+
+    /**
+     * A dynamic state variable. Index of the performer to re-run
+     * next.
+     */
+    public int index = 0;
+
+    /**
+     * The collection of listeners, which get notify about the
+     * thread going to sleep and waking up.
+     */
+    public Vector/*<Listener>*/ listeners = new Vector/*<Listener>*/();
+
+    /**
+     * Utility method to notify all listeners
+     */
+    public void notifyGoingIdle() {
+       Vector/*<Listener>*/ handlers = new Vector/*<Listener>*/();
+       synchronized ( this ) {
+           handlers.addAll( listeners );
+       }
+       for ( Iterator/*<Listener>*/ i = handlers.iterator();
+             i.hasNext(); ) {
+           Listener x = (Listener) i.next();
+           x.goingIdle( this );
+       }
+    }
+
+    /**
+     * Utility method to notify all listeners
+     */
+    public void notifyWakingUp() {
+       Vector/*<Listener>*/ handlers = new Vector/*<Listener>*/();
+       synchronized ( this ) {
+           handlers.addAll( listeners );
+       }
+       for ( Iterator/*<Listener>*/ i = handlers.iterator();
+             i.hasNext(); ) {
+           Listener x = (Listener) i.next();
+           x.wakingUp( this );
+       }
+    }
+
+    /**
+     * Adds a listener to the Executor to be notified when the
+     * Executor goes idle and wakes up from idle
+     * @param listener
+     */
+    synchronized public void addListener(Listener listener) {
+       listeners.add( listener );
+    }
+
+    /**
+     * Removes a listener from the executor.
+     * @param listener
+     */
+    synchronized public void removeListener(Listener listener) {
+       listeners.remove( listener );
+    }
+
+    /**
+     * Constructor. The given name is also used as thread name for the
+     * thread that is started.
+     */
+    public Executor(String n) {
+       name = n;
+    }
+
+    /**
+     * Returns the executor's name.
+     */
+    public String toString() {
+       return name;
+    }
+
+    /**
+     * Tells whether it is time to change focus or not. This is used
+     * by the {@link Goal.Instance#perform} method to possibly stop
+     * the current computation in order to let the executor shift
+     * focus to another computation. Whenever the {@link #count}
+     * arrives at 0, then it it is time to shift focus, and to reset
+     * the count from {@link #steps}. Though, if the latter is 0 or
+     * negative, then a change of focus will never be signalled from
+     * here.
+     */
+    public boolean changeFocus() {
+       if ( steps <= 0 )
+           return false;
+       if ( count-- > 0 )
+           return false;
+       if ( Goal.isTracing() )
+           System.err.println( "CHANGE FOCUS" );
+       count = steps;
+       return true;
+    }
+
+    /**
+     * Utility method to "preempt" current intention by forcing {@link
+     * #count} to zero. This only has effect if {@link #steps} is
+     * non-zero, i.e., when using intention interleaving.
+     */
+    public void preempt() {
+       count = 0;
+    }
+
+    /**
+     * Utility method to add a {@link Performer} to be managed by this
+     * executor. If the performer already belongs to an executor, it
+     * is first removed from that one.
+     */
+    synchronized public void addPerformer(Performer performer) {
+       if ( performer.executor != null ) {
+           // The performer already belongs to an executor
+           if ( performer.executor != this )
+               performer.executor.removePerformer( performer );
+       }
+       if ( ! performers.contains( performer ) )
+           performers.add( performer );
+       performer.executor = this;
+    }
+
+    /**
+     * Utility method to remove a {@link Performer} from the
+     * collection of performers managed by this executor. This does
+     * not interrupt an ongoing computation, but means that the
+     * executor will not re-run the performer.
+     */
+    synchronized public void removePerformer(Performer performer) {
+       if ( performer.executor == this ) {
+           performers.remove( performer );
+           performer.executor = null;
+       }
+    }
+
+    /**
+     * Utility method to localize the first {@link Performer} by its
+     * name.
+     */
+    synchronized public Performer findPerformer(String name)
+    {
+       for ( Iterator/*<Performer>*/ i =
+                 performers.iterator(); i.hasNext(); ) {
+           Performer performer = (Performer) i.next();
+           if ( performer.name.equals( name ) )
+               return performer;
+       }
+       return null;
+    }
+
+    /**
+     * Utility method to "tickle" the executor with, just in case it
+     * is idle.
+     */
+    synchronized public void signal() {
+       running = true;
+       notifyAll();
+    }
+
+    /**
+     * A utility method for obtaining the next performer to re-run, or
+     * <em>null</em>, in between having told about them all.
+     */
+    synchronized public Performer getNext() {
+       if ( index < performers.size() )
+           return (Performer) performers.get( index++ );
+       index = 0;
+       return null;
+    }
+
+    /**
+     * A utility method for going idle when there doesn't seem to be
+     * anything to do.
+     */
+    public void waitRunning() {
+       boolean idle = false;
+       synchronized ( this ) {
+            if ( ! running && stopping == 0 )
+               idle = true;
+       }
+       if ( idle )
+           notifyGoingIdle();
+       synchronized ( this ) {
+           if ( idle ) {
+               while ( ! running && stopping == 0 ) {
+                   if ( Goal.isTracing() )
+                       System.err.println(
+                           "** executor " + this + " idle..." );
+                   try {
+                       wait();
+                   } catch (InterruptedException e) {
+                   }
+               }
+           }
+           running = false;
+       }
+       if ( idle ) {
+           if ( Goal.isTracing() )
+               System.err.println( "** executor " + this + " active..." );
+           notifyWakingUp();
+       }
+    }
+
+    /**
+     * Utility method to run all {@link Performer Performers} once,
+     * and then report if they all reported blocking or not.
+     */
+    public Goal.States runPerformersOnce() {
+       if ( Goal.isTracing() )
+           System.err.println( "** runPerformersOnce" );
+       boolean stopped = false;
+       Performer performer = getNext();
+       while ( performer != null ) {
+           if ( performer.runPerformer() != Goal.States.BLOCKED )
+               stopped = true;
+           performer = getNext();
+       }
+       if ( Goal.isTracing() )
+           System.err.println( "** runPerformersOnce stopped " + stopped );
+       return stopped? Goal.States.STOPPED : Goal.States.BLOCKED ;
+    }
+
+    /**
+     * Utility method to keep running {@link Performer Performers}
+     * while not all are blocking. This method invokes {@link
+     * #runPerformersOnce} repeatedly until it reports that all
+     * performers are blocked.
+     */
+    public boolean runPerformersBlocked() {
+        if ( Goal.isTracing() )
+            System.err.println( "** runPerformersBlocked" );
+       boolean done_something = false;
+       while ( runPerformersOnce() != Goal.States.BLOCKED ) {
+           done_something = true;
+           if ( stopping > 0 )
+               break;
+       }
+        if ( Goal.isTracing() )
+            System.err.println(
+               "** runPerformersBlocked done something " + done_something );
+       return done_something;
+    }
+
+    /**
+     * This is the main execution loop of the executor. It keeps
+     * running its performers until they are all blocked, in which
+     * case it waits for a signal to re-run them.
+     */
+    public void run() {
+       if ( Goal.isTracing() )
+           System.err.println( "** starting executor " + this );
+       try {
+           index = 0;
+            for (;;) {
+                if (runPerformersBlocked()) {
+                    running = true;
+                }
+                if (running) {
+                    if (stopping == 1) {
+                        break;
+                    }
+                    running = false;
+                } else {
+                    waitRunning();
+                    if (stopping > 0) {
+                        break;
+                    }
+                }
+            }
+           
+       } catch (Throwable t) {
+           t.printStackTrace();
+           System.err.println( "** terminating executor " + this );
+       }
+       stopping = 0;
+    }
+
+    /**
+     * Tell the {@link #run} method to stop.
+     */
+    public void stop(boolean hard) {
+       stopping = hard? 1 : 2;
+       signal();
+    }
+
+    /**
+     * Service interface to request a goal to be performed by a named
+     * performer, with thread synchronisation on this Executor.
+     */
+    synchronized public boolean performGoal(
+       String performer,Goal goal,String head,Data d) {
+       return findPerformer( performer ).performGoal( goal, head, d );
+    }
+    
+    /**
+     * Service interface to request a goal to be performed by a named
+     * performer, with thread synchronisation on this Executor.
+     */
+    synchronized public boolean performGoal(
+       String performer,Goal goal,String head,
+       Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
+       return findPerformer( performer ).performGoal( goal, head, ins, outs );
+    }
+
+    /**
+     * Service interface to request a goal to be performed by a named
+     * performer.
+     */
+    public boolean performGoal(
+       String performer,String goal,String head,
+       Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
+       return findPerformer( performer ).performGoal( goal, head, ins, outs );
+    }
+}
+
diff --git a/com/intendico/gorite/FailGoal.java b/com/intendico/gorite/FailGoal.java
new file mode 100644 (file)
index 0000000..5f368bf
--- /dev/null
@@ -0,0 +1,90 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+import com.intendico.gorite.Goal.States;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A FailGoal is achieved by means of failing any sub goal, which are
+ * attempted in sequence. The goal fails when and if all sub goals
+ * have been attempted and succeeded. In particular, a fail goal
+ * without sub goals always fail immediately, and is a way to induce
+ * failure. Failure may also be induced by tasks, which can succeed or
+ * fail.
+ *
+ * @see ConditionGoal
+ */
+public class FailGoal extends SequenceGoal {
+
+    /**
+     * Constructor.
+     */
+    public FailGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public FailGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a FailGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new FailInstance( head );
+    }
+
+    /**
+     * Represents the process of achieving a FailGoal
+     */
+    public class FailInstance extends SequenceInstance {
+
+       /**
+        * Constructor.
+        */
+       public FailInstance(String h) {
+           super( h );
+       }
+
+       /**
+        * Process all sub goals in sequence, then reverse {@link
+        * States#PASSED} and {@link States#FAILED}.
+        */
+       public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           States s = super.action( head, d );
+           if ( s == States.PASSED )
+               return States.FAILED;
+           if ( s == States.FAILED )
+               return States.PASSED;
+           return s;
+       }
+    }
+}
diff --git a/com/intendico/gorite/Goal.java b/com/intendico/gorite/Goal.java
new file mode 100644 (file)
index 0000000..eaae20c
--- /dev/null
@@ -0,0 +1,876 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * The Goal class is base class for a goal oriented process modelling
+ * through a hierarchy that reflects the decomposition of a goal as an
+ * abstraction of sub goals.  A process model is a hierarchy with a
+ * top level goal that is sucessively decomposed as particular
+ * combinations of sub goals. The top level goal is achieved by means
+ * of processing its sub goals according to the goal type.
+ *
+ * <p> A goal is processed by instantiating it via its {@link
+ * #instantiate} method, which creates the {@link Instance} object
+ * that represents an actual intention to achieve the goal. The
+ * intention is then pursued via the {@link Instance#perform} method,
+ * which eventually results in invoking one of the particular {@link
+ * Instance#action} methods that implement the particular execution
+ * semantics.
+ * 
+ * <p> The base class is also used for task goals, where the {@link
+ * #execute} method is overridden to perform the intended task. In
+ * many cases, the task execution is a matter of processing one or
+ * more input data elements to proved one or more result data
+ * elements, as illustrated by the following example:
+ * <pre>
+ * public States execute(Data data) {
+ *     data.setValue( "result", process( data.getValue( "input" ) ) );
+ *     return PASSED;
+ * }
+ * </pre>
+ *
+ * Of course, the data element names need to be designed and defined
+ * in the full application context, since all goal executions of the
+ * same intention use the same {@link Data} object. Typically you
+ * would employ some data naming scheme to ensure appropriate name
+ * separation.
+ *
+ * <p> Note that an Goal may be augmented with a reimplemented {@link
+ * Goal#instantiate(String,Data)} method as way of defining data
+ * values, a reimplemented {@link Goal#execute(Data,Goal.Instance)}
+ * method as way of defining post processing, and a reimplemented
+ * {@link Goal#cancelled(Goal.Instance)} method as way of restoring
+ * state if the goal execution is cancelled.
+ *
+ * In particular, if the implementation requires a dynamic lexical
+ * environment for each execution, then the {@link
+ * Goal#instantiate(String,Data)} needs to create a vacuous Goal
+ * extension with that environment and ultimately containing the
+ * associated {@link Goal#execute(Data,Goal.Instance)} method. The
+ * following is an illustration example of this.
+ * <pre>
+ * public Goal.Instance instantiate(String head,Data data) {
+ *     return new Goal( "achieving " + getGoalName() ) {
+ *         int x;
+ *         public States execute(Data data) {
+ *             // Sees a dynamically instantiated x
+ *             while ( x++ < 10 ) {
+ *                 return BLOCKED;
+ *             }
+ *             return PASSED;
+ *         }
+ *     }.instantiate( head, goal );
+ * }
+ * </pre>
+ *
+ * Using that approach, a vacuous Goal object, with the "int x"
+ * member, is created for each instantation of the surrounding goal,
+ * and then that goal is instantiated for providing the actual
+ * Instance object.
+ */
+public class Goal {
+
+    /**
+     * Interface for intrusive tracing of goal execution.
+     * @see #tracer
+     */
+    public interface Tracer {
+
+       /**
+        * Invoked before performing an instance for given data. This
+        * method is invoked by the gorite executor thread prior to
+        * entering or re-entering the goal instance execution. A
+        * Tracer implementation may, by hogging the thread, hold back
+        * the execution while inspecting the model state. The Data is
+        * the instance data to be; this data object becomes the
+        * {@link Instance#data} upon the first entry (after the call
+        * to this method).
+        */
+       public void enterPerform(Instance instance,Data d);
+
+       /**
+        * Invoked after performing instance returns. This method is
+        * invoked when the execution thread stops performing the
+        * instance, which is either because it is finished, or
+        * because it is blocked or stopped. The given {@link States}
+        * is the execution return value, which in many cases is also
+        * cached in {@link Instance#state}.
+        */
+       public void exitPerform(Instance instance,States s);
+
+       /**
+        * Invoked after performing instance throws exception. This
+        * method is invoked when the execution thread stops
+        * performing the instance due to it throwing an exception or
+        * ({@link Throwable}).  If it happens to be a {@link
+        * LoopEndException} or {@link ParallelEndException}, then the
+        * exception is passed in on the call, otherwise null is
+        * passed in. Note that this invocation happens in the
+        * "finally block" for the pending exception propagation,
+        * which thus gets propagated when the method returns.
+        */
+       public void exceptionPerform(Instance instance,Exception e);
+
+    }
+
+    /**
+     * The global intrusive goal execution tracer, if any. This may be
+     * set via the command line property <tt>-Dgorite.tracer=XX</tt>
+     * where <tt>XX</tt> is the class name for the Tracer
+     * implementation to instantiate. It may also be set in code.
+     */
+    public static Tracer tracer = null;
+
+    /**
+     * These are the return values of the {@link #execute(Data)}
+     * method, to report on the state of a goal instance execution.
+     */
+    public /*enum*/ static class States {
+
+       /**
+        * A "name" of a States object.
+        */
+       protected String name;
+
+       /**
+        * The PASSED state is returned to indicate that the goal
+        * execution has completed successfully.
+        */
+       public final static States PASSED =
+           new States() {{ name = "PASSED"; }};
+
+       /**
+        * The FAILED state is returned to indicate that the goal
+        * execution has completed without success.
+        */
+       public final static States FAILED =
+           new States() {{ name = "FAILED"; }};
+
+       /**
+        * The CANCEL state is not returned by goal instance
+        * execution, but is used to mark an {@link Instance} when the
+        * execution has been cancelled.
+        */
+       public final static States CANCEL =
+           new States() {{ name = "CANCEL"; }};
+
+       /**
+        * The BLOCKED state is returned to indicate that the goal
+        * execution has not completed, and that it cannot progress
+        * until a blocking condition has been removed. This return
+        * code is further a promise that something will notify the
+        * execution system via {@link Data#setFlag} call when the
+        * blocking condition possibly has changed. Having returned
+        * this code, the goal instance execution may be re-entered
+        * whenever the execution system so wishes, regardless of the
+        * blocking condition, and the execution method needs to
+        * confirm the condition or return BLOCKED again.
+        * @see Data#setTrigger(Observable)
+        * @see Data#setTrigger(Query)
+        */
+       public final static States BLOCKED =
+           new States() {{ name = "BLOCKED"; }};
+
+       /**
+        * The STOPPED state is returned to indicate that the goal
+        * execution has not completed, but is interrupted for some
+        * reason other than a blocking condition. The goal instance
+        * execution is thus expecting re-entry as soon as possible.
+        */
+       public final static States STOPPED =
+           new States() {{ name = "STOPPED"; }};
+
+       /**
+        * Returns the name.
+        */
+       public String toString() {
+           return name;
+       }
+    }
+
+    /**
+     * The property name for tracing goal execution.
+     */
+    public static final String TRACE = "gorite.goal.trace";
+
+    /**
+     * The name of the data element that identifies the executing
+     * performer.
+     */
+    public static final String PERFORMER = "this performer";
+
+    /**
+     * The name of this goal. There are no restrictions on which names
+     * goals have, but it is the name that identifies the goal when
+     * resolving a {@link BDIGoal} or a {@link TeamGoal} into some
+     * plan to achieve it. Conceptually it is the name that *is* the
+     * goal, wheras a Goal object rather is a node in a goal hierarchy
+     * to represent the idea of achieving the named goal.
+     *
+     * <p> An actual attempt to achieve the named goal is also
+     * separate both from the goal (name) itself, and from the Goal
+     * object (the idea of achieving it). Such an attempt is
+     * understood as an "intention", which in GORITE gets represented
+     * by a {@link Goal.Instance}.
+     */
+    private String name = null;
+
+    /**
+     * The control data, if any. This is only used by certain kinds of
+     * goals, to carry some certain definition detail separate from
+     * the goal type.
+     *
+     * <p> It is for instance used by {@link ParallelGoal} and {@link
+     * RepeatGoal} goals to name the data element that identifies each
+     * branch.
+     * @see Data#fork(String,int,String)
+     * @see Data#join(String,HashSet,String)
+     */
+    private String control = null;
+
+    /**
+     * The execution group to be used for pursuing the goal. If set,
+     * it is the name of the {@link Performer.TodoGroup} to use for
+     * goal instance execution. Goals of the same group are then
+     * managed by the execution management associated with the {@link
+     * Performer.TodoGroup}.
+     */
+    private String group = null;
+
+    /**
+     * The sub goals of this goal.
+     */
+    private Goal [] subgoals;
+
+    /**
+     * Where is this created?
+     */
+    final public StackTraceElement created;
+
+    /**
+     * Constructor.
+     */
+    public Goal(String n,Goal [] sg) {
+       name = n;
+       subgoals = sg;
+       StackTraceElement [] where = new Error().getStackTrace();
+       for ( int i = 0; i < where.length; i++ ) {
+           String c = where[i].getClassName();
+           if ( ! c.startsWith( "com.intendico.gorite." ) ) {
+               created = where[i];
+               return;
+           }
+       }
+       created = null;
+    }
+
+    /**
+     * Convenience constructor without sub goals. The sub goals may be
+     * set explicitly as value to the {@link #subgoals} member.
+     */
+    public Goal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * This flag captures whether or not the {@link #TRACE} property
+     * set (to any value) upon class initialisation, and it may be set
+     * or reset subsequently for controlling the amount of goal
+     * execution logging to generate.
+     * 
+     * <p> Ordinarily one will turn on standard goal execution tracing
+     * by using the command line argument:
+     * <pre>
+     * -Dgorite.goal.trace=yes
+     * </pre>
+     */
+    public static boolean tracing = System.getProperty( TRACE, null ) != null;
+
+    // Set up a tracer if declared
+    static {
+       String c = System.getProperty( "gorite.tracer", null );
+       if ( c != null ) {
+           try {
+               tracer = (Tracer) Class.forName( c ).newInstance();
+           } catch (Exception e) {
+               e.printStackTrace();
+           }
+       }
+    }
+    /**
+     * This is a utility method to discover whether tracing has been
+     * requested by defining the {@link #TRACE} property, or not.
+     */
+    public static boolean isTracing() {
+       return tracing;
+    }
+
+    //
+    // Execution support
+    //
+
+    /**
+     * This is the primary execution method for a goal. It is invoked
+     * from the {@link Instance} object for a goal so as to achieve
+     * the goal, and it is told about both the calling goal {@link
+     * Instance} and {@link Data}. This base implementation invokes
+     * {@link Instance#action(String,Data)} for actual progress of the
+     * intention.
+     *
+     * <p> This method may be overridden by a user method when
+     * defining a task goal that needs access to the {@link Instance}
+     * object. See {@link #execute(Data)} for details.
+     */
+    public States execute(Data d,Instance i)
+        throws LoopEndException, ParallelEndException {
+        return i.action( i.head, d );
+    }
+
+    /**
+     * This is the simplified execution method for task goal. It is
+     * invoked from the {@link Instance} object for a task goal so as
+     * to achieve the goal by means of Java code, and it is told only
+     * about the intention {@link Data}. This base implementation
+     * merely returns {@link States#PASSED}, and it should be
+     * overridden by a user method when defining an actual task goal.
+     *
+     * <p> The method returns either {@link States#PASSED} or {@link
+     * States#FAILED} to indicate that the execution has terminated
+     * and the goal is achieved or not, or it returns {@link
+     * States#STOPPED} or {@link States#BLOCKED} to indicate that the
+     * attempt of achieving the goal is still going on, but execution
+     * control is relinquished temporarily (so as to allow other
+     * intentions to progress). By returning {@link States#BLOCKED},
+     * it also indicates that this code need not be invoked unless
+     * some other intention has progressed, but the {@link Executor}
+     * may choose to go idle. This is typically combined with the
+     * setting of an execution trigger, which is done via {@link
+     * Data#setTrigger(Query)} or {@link Data#setTrigger(Observable)},
+     * to ensure that the {@link Executor} wakes up when something
+     * interesting has happened.
+     */
+    public States execute(Data d)
+       throws InterruptedException, LoopEndException, ParallelEndException {
+       return States.PASSED;
+    }
+
+    /**
+     * Utility method for instantiating a goal according to its
+     * type. Each goal type has its own instance class extension,
+     * which implement the ways in which a goal is achieved through
+     * its sub goals.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new Instance( head );
+    }
+
+    /**
+     * Control callback whereby the goal gets notified that an (its)
+     * intention is cancelled.
+     */
+    public void cancelled(Instance which) {
+    }
+
+    /**
+     * Base class for goal instance execution procedures.
+     */
+    public class Instance {
+
+       /**
+        * The trace name of the execution point. This defines where
+        * in the abstract goal execution tree this instance appears.
+        * The head is a kind of cursor into the abstract tree, set up
+        * as a sequence of numbers that tell which branch to follow
+        * so as to eventually arrive at this node. Branches are
+        * numbered from 0 and up, i.e. branch #3 is the fourth
+        * branch; for some goal types the branch number is also an
+        * index into the {@link #subgoals} array for the goal the
+        * execution is an instantiation of.
+        * The numbers are separated by characters that mark which
+        * kind of expansion the abstract tree has at the point.
+        * <ul>
+        * <li> ".n" marks insantiation of subgoal n
+        * <li> ":n" marks repetition n or branch n, for {@link
+        * LoopGoal}, {@link ParallelGoal}, {@link RepeatGoal} and
+        * {@link TeamGoal}.
+        * <li> "*n" marks attempt n for a {@link BDIGoal}
+        * </ul>
+        */
+       final public String head;
+
+       /**
+        * A more verbose intention name, which consists of the head
+        * and the goal name. This is used as key for the {@link
+        * Data.Bindings} of this intention.
+        */
+       final public String thread_name;
+
+       /**
+        * The Data object for this instance.
+        */
+       public Data data = null;
+
+       /**
+        * The monitoring state. This is the state an intention
+        * monitor should return; it starts off as BLOCKED, and turns
+        * into PASSED, FAILED or STOPPED (upon exception).
+        */
+       public States state = States.BLOCKED;
+
+       /**
+        * Returns the goal of this instance.
+        */
+       public Goal getGoal() {
+           return Goal.this;
+       }
+
+       /**
+        * Returns the plan argument object, if any, for this
+        * instance.
+        */
+       public Object getArgs() {
+           return data.getArgs( head );
+       }
+
+       /**
+        * Determine the plan-local variable name for the given short
+        * name.
+        */
+       public String local(String name) {
+           int i = head.lastIndexOf( "*" );
+           if ( i < 0 )
+               i = head.length();
+           return head.substring( 0, i ) + "-" + name;
+       }
+
+       /**
+        * Returns the value of a data element as seen by this
+        * intention.
+        */
+       public Object getValue(String name) {
+           if ( data == null )
+               return null;
+           return data.getValue( name, thread_name );
+       }
+
+       /**
+        * Sets a data value onto the innermost lookup path as seen by
+        * this intention. If an element of the name is found, then
+        * that element gains a new value, otherwise a new element is
+        * created for the most local binding of this intention.
+        */
+       public void setValue(String name,Object value) {
+           data.setValue( thread_name, name, value );
+       }
+
+       /**
+        * Utility method for updating the monitoring state, which
+        * also notifies any waiting thread.
+        */
+       synchronized public void setMonitoring(States s) {
+           if ( isTracing() )
+               trace( "-- noting", " (" + s + ")" );
+           state = s;
+           if ( state == States.BLOCKED )
+               state = s;
+           else
+               data.setFlag();
+       }
+
+       /**
+        * Utility method to cancel the execution of this instance.
+        * This method is typically overridden by extension classes in
+        * order to propagate cancellation.
+        */
+       public void cancel() {
+           if ( isTracing() )
+               System.err.println( "** cancel " + head + " " + this );
+           if ( state == States.BLOCKED || state == States.STOPPED )
+               state = States.CANCEL;
+           // Notify Goal about cancelling this intention.
+           cancelled( this );
+           if ( data != null )
+               data.dropBindings( thread_name );
+       }
+
+       /**
+        * Utility thread control method to wait until this instance
+        * is done.
+        */
+       synchronized public States waitDone()
+           throws LoopEndException, ParallelEndException {
+           if ( state == States.BLOCKED ) {
+               trace( "-- waiting on ", "" );
+               data.waitFlag();
+           }
+           throwSavedException();
+           return state;
+       }
+
+       /**
+        * Utility funtion that throws the particular exception, if
+        * any, that has been saved for this instance, or just
+        * returns.
+        */
+       public void throwSavedException()
+           throws LoopEndException, ParallelEndException {
+           if ( exception instanceof LoopEndException )
+               throw (LoopEndException) exception;
+           if ( exception instanceof ParallelEndException )
+               throw (ParallelEndException) exception;
+       }
+           
+       /**
+        * Keeps any LoopEndException or ParallelEndException thrown
+        * by a sub goal.
+        */
+       public Exception exception = null;
+
+       /**
+        * The instance performer.
+        */
+       public Performer performer = null;
+
+       /**
+        * Constructor, taking the execution point name as argument.
+        */
+       public Instance(String h) {
+           head = h.replace( '\"', ':' );
+           thread_name = head + " " + nameString( name );
+       }
+
+       /**
+        * Utility method to issue a standard trace message.
+        */
+       public void trace(String prefix,String suffix) {
+           if ( isTracing() ) {
+               System.err.println(
+                   prefix + " " +
+                   ( performer == null? "" : ( performer.name + ":" ) ) +
+                   thread_name + suffix );
+           }
+       }
+
+       /**
+        * This is the primary entry point for executing this goal
+        * instance. It is invoked repeatedly by the calling goal
+        * instance for the purpose of progressing this goal instance
+        * via its {@link Goal#execute(Data,Instance)} method. On the
+        * first invokation, which is detected by virtue of {@link
+        * #data} being unset, this method registers the instance to
+        * the intention data and sets up the {@link #PERFORMER} data
+        * element.
+        *
+        * <p> This method also invokes {@link
+        * Performer#propagateBeliefs()} before progressing the
+        * intention.
+        *
+        * <p> If the enclosing goal's {@link Goal#group} attribute is
+        * non-null, then it is understood to name a {@link
+        * Performer.TodoGroup} for coordinating instances of this
+        * goal. Therefore, instead of actually progressing the
+        * intention, this instance gets added to the named {@link
+        * Performer.TodoGroup} (by {@link Performer#execute}), and
+        * subsequently all progress will occur "in parallel" via
+        * calls to the {@link #action()} method, while this method
+        * henceforth results in merely polling the instance state
+        * (which happens indirectly via the subsequent calls to
+        * {@link Performer#execute}).
+        */
+       public States perform(Data d)
+           throws LoopEndException, ParallelEndException {
+           Tracer t = tracer;
+           if ( t == null ) {
+               try {
+                   return performIt( d );
+               } catch(LoopEndException x) {
+                   throw x;
+               } catch(ParallelEndException x) {
+                   throw x;
+               } catch (Throwable x) {
+                   throw new GoriteError( this, x );
+               }
+           }
+           t.enterPerform( this, d );
+           States b = States.STOPPED;
+           boolean returns = false;
+           Exception e = null;
+           try {
+               b = performIt( d );
+               returns = true;
+               return b;
+           } catch (LoopEndException x) {
+               e = x;
+               throw x;
+           } catch (ParallelEndException x) {
+               e = x;
+               throw x;
+           } catch (Throwable x) {
+               throw new GoriteError( this, x );
+           } finally {
+               if ( returns ) {
+                   t.exitPerform( this, b );
+               } else {
+                   t.exceptionPerform( this, e );
+               }
+           }
+       }
+       
+       /**
+        * The working method for performing this Instance. It used to
+        * be {@link #perform}, but nowadays that method only deals
+        * with the {@link #tracer}, while invoking this method for
+        * the actual work.
+        * @see #perform
+        */
+       private States performIt(Data d)
+           throws LoopEndException, ParallelEndException {
+           try {
+               if ( state == States.CANCEL ) {
+                   trace( "--", " (cancel)" );
+                   cancel();
+                   return States.CANCEL;
+               }
+
+               boolean reentry = true;
+               if ( data == null ) {
+                   trace( "=>", " (group=" + group + ")" );
+                   data = d;
+                   data.link( thread_name );
+                   reentry = false;
+                   performer = (Performer) data.getValue( PERFORMER );
+                   if ( performer == null ) {
+                       System.err.println(
+                           "** Missing performer for goal \"" +
+                           name + "\" at " + head );
+                       System.err.println( "---------------------\n" + data );
+                       //System.exit( 1 );
+                   }
+               } else {
+                   if ( state == States.BLOCKED &&
+                        ! data.isRunning( thread_name ) )
+                       return States.BLOCKED;
+                   trace( "=>", " (resume)" );
+               }
+
+               if ( ! reentry && performer.changeFocus() ) {
+                   if ( group == null )
+                       return States.STOPPED ;
+               }
+
+               performer.propagateBeliefs();
+
+               States b;
+               if ( group == null ) {
+
+                   b = execute( data, this );
+                   if ( b == States.PASSED || b == States.FAILED ) {
+                       data.dropBindings( thread_name );
+                   }
+               } else {
+                   b = performer.execute( reentry, this, group );
+               }
+               setMonitoring( b );
+               
+               if ( isTracing() ) {
+                   trace( "<=", " (" + b + ")" );
+               }
+
+               return b;
+           } catch (LoopEndException e) {
+               if ( data != null )
+                   data.dropBindings( thread_name );
+               throw e;
+           } catch (ParallelEndException e) {
+               if ( data != null )
+                   data.dropBindings( thread_name );
+               throw e;
+           }
+       }
+
+       /**
+        * Utility method that runs this instance's
+        * action(String,Data). This is used for resuming the instance
+        * from a {@link Performer.TodoGroup}.
+        */
+       public States action() {
+           if ( state == States.CANCEL ) {
+               trace( "--*", " (cancel)" );
+               cancel();
+               return States.CANCEL;
+           }
+           state = States.BLOCKED;
+           try {
+               data.setThreadName( thread_name );
+               if ( performer == null ) {
+                   throw new Error(
+                       "** Missing performer for goal \"" +
+                       name + "\" at " + head );
+               }
+               data.replaceValue( Goal.PERFORMER, performer );
+               trace( "=>*", " (resume)" );
+               performer.propagateBeliefs();
+               return execute( data, this );
+           } catch (LoopEndException e) {
+               exception = e;
+               return States.STOPPED;
+           } catch (ParallelEndException e) {
+               exception = e;
+               return States.STOPPED;
+           }
+       }
+
+       /**
+        * Utility method to access a data element relative to this
+        * thread.
+        */
+       public Data.Element getDataElement(String name) {
+           return data.getLocalElement( thread_name, name );
+       }
+
+       /**
+        * Implements how a goal is achieved by means of its sub
+        * goals.  This base implementation invokes the goal's {@link
+        * #execute(Data)}.
+        */
+       public States action(String head,Data d)
+           throws LoopEndException, ParallelEndException {
+           try {
+               return execute( d );
+           } catch (InterruptedException e) {
+               e.printStackTrace();
+               return States.STOPPED;
+           }
+       }
+    }
+
+    /**
+     * Utility method to quote a string.
+     */
+    public static String nameString(String n)
+    {
+       if ( n == null )
+           return "null";
+       return "\"" + n + "\"";
+    }
+
+    /**
+     * Returns a {@link java.lang.String} identifying the type of the
+     * goal.
+     */
+    public String getType()
+    {
+       String s = getClass().getName();
+       int i = s.lastIndexOf( "." );
+       return s.substring( i + 1 );
+    }
+
+    /**
+     * Makes a deep-structure string representation of this goal and
+     * all its sub goals, using a given depth prefix. It does not
+     * check for cyclic goal structure.
+     */
+    public String toString(String counter) {
+       StringBuffer s = new StringBuffer();
+       String x = "";
+       if ( counter != null ) {
+           s.append( counter );
+           s.append( " " );
+           x = counter + ".";
+       }
+       s.append( nameString( name ) );
+       s.append( " (" );
+       s.append( getType() );
+       s.append( ")" );
+       if ( subgoals != null ) {
+           for ( int i = 0; i < subgoals.length; i++ ) {
+               s.append( "\n" );
+               s.append( subgoals[ i ].toString( x + (i+1) ) );
+           }
+           //s.append( " // end " + counter + "\n" );
+       }
+       return s.toString();
+    }
+
+    /**
+     * Makes a deep-structure string representation of this goal and
+     * all its sub goals.
+     */
+    public String toString() {
+       return toString( null );
+    }
+
+    /**
+     * Returns the goal {@link #name} attribute.
+     */
+    public String getGoalName() {
+       return name;
+    }
+
+    /**
+     * Returns the goal {@link #control} attribute.
+     */
+    public String getGoalControl() {
+       return control;
+    }
+
+    /**
+     * Returns the {@link #subgoals} attribute.
+     */
+    public Goal [] getGoalSubgoals() {
+       return subgoals;
+    }
+
+    /**
+     * Returns the {@link #group} attribute.
+     */
+    public String getGoalGroup() {
+       return group;
+    }
+
+    /**
+     * Assigns the {@link #control} attribute.
+     */
+    public void setGoalControl(String v) {
+       control = v;
+    }
+
+    /**
+     * Assigns the {@link #subgoals} attribute.
+     */
+    public void setGoalSubgoals(Goal [] s) {
+       subgoals = s;
+    }
+
+    /**
+     * Assigns the {@link #group} attribute.
+     */
+    public void setGoalGroup(String v) {
+       group = v;
+    }
+}
diff --git a/com/intendico/gorite/GoriteError.java b/com/intendico/gorite/GoriteError.java
new file mode 100644 (file)
index 0000000..aa42fa5
--- /dev/null
@@ -0,0 +1,35 @@
+package com.intendico.gorite;
+
+/**
+ * This is a specialozed {@link Error} that used for wrapping
+ * exceptions in a gorite intention chain, so as to give better
+ * guidance about where in the source execution the error occurs.
+ */
+public class GoriteError extends Error {
+
+    /**
+     * Constructor.
+     */
+    public GoriteError(Goal.Instance instance,Throwable cause) {
+       super( "Uncaught Throwable" );
+       if ( cause instanceof GoriteError ) {
+           cause = cause.getCause();
+       }
+
+       // Reduce the stack trace for the cause in a good way...
+       StackTraceElement [] stack = getStackTrace();
+       StackTraceElement [] inner = cause.getStackTrace();
+       
+       int at = inner.length - stack.length;
+       StackTraceElement created = instance.getGoal().created;
+       inner[ at ] = new StackTraceElement(
+           created.getClassName(),
+           created.getMethodName() +
+           "|" + instance.getGoal().getGoalName() + "|" + instance.head,
+           created.getFileName(),
+           created.getLineNumber() );
+       cause.setStackTrace( inner );
+       initCause( cause );
+    }
+
+}
diff --git a/com/intendico/gorite/LoopEndException.java b/com/intendico/gorite/LoopEndException.java
new file mode 100644 (file)
index 0000000..6a8a021
--- /dev/null
@@ -0,0 +1,39 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+/**
+ * This exception is thrown by an {@link EndGoal} goal, to be caught
+ * by an enclosing {@link LoopGoal} goal, which then terminates
+ * successfully.
+ */
+public class LoopEndException extends Exception {
+    /**
+     * Version identity required for serialization.
+     */
+    public static final long serialVersionUID = 1L;
+
+    /**
+     * Cosntructor.
+     */
+    public LoopEndException(String s) {
+       super( s );
+    }
+}
diff --git a/com/intendico/gorite/LoopGoal.java b/com/intendico/gorite/LoopGoal.java
new file mode 100644 (file)
index 0000000..6e7e7fa
--- /dev/null
@@ -0,0 +1,111 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A LoopGoal is achieved by means of repeatedly achieving its sub
+ * goals in sequence, until eventually an embedded {@link EndGoal} is
+ * achieved. The usage pattern for this is as follows:
+ *
+ * <pre>
+ * new LoopGoal( "loop", new Goal [] {
+ *     .. // steps in the loop
+ *     .. new EndGoal( "break if", new Goal [] {
+ *            // optional sub goals for ending the loop if they succeed
+ *        } )
+ *     .. // more steps in the loop
+ * } )
+ * </pre>
+ *
+ * There may be any number of {@link EndGoal} to end the loop although
+ * of course only one will take effect. The {@link EndGoal} may be at
+ * any level within the LoopGoal goal hierarchy, except nested within
+ * another LoopGoal.
+ *
+ * @see EndGoal
+ * @see SequenceGoal
+ */
+public class LoopGoal extends SequenceGoal {
+
+    /**
+     * Constructor.
+     */
+    public LoopGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public LoopGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a LoopGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new LoopInstance( head );
+    }
+
+    /**
+     * Implements repeated, sequential exception.
+     */
+    public class LoopInstance extends SequenceInstance {
+
+       /**
+        * Constructor.
+        */
+        public LoopInstance(String h) {
+            super( h );
+        }
+
+       public int count = 0;
+
+        /**
+         * Repeatedly process all subgoals in sequence, until a
+        * thrown LoopEndException is caught.
+         */
+        public States action(String head,Data d)
+            throws LoopEndException, ParallelEndException {
+           for ( ;; count++ ) {
+               States s;
+               try {
+                   s = super.action( head + ":" + count, d );
+               } catch (LoopEndException e) {
+                   // End this loop
+                   break;
+               }
+               if ( s != States.PASSED )
+                   return s;
+               index = 0;
+           }
+           return States.PASSED;
+       }
+    }
+}
diff --git a/com/intendico/gorite/ParallelEndException.java b/com/intendico/gorite/ParallelEndException.java
new file mode 100644 (file)
index 0000000..74bd87a
--- /dev/null
@@ -0,0 +1,44 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+/**
+ * This exception is thrown by an {@link ControlGoal} goal, to be
+ * caught by an enclosing parallel execution, which then cancels all
+ * parallel branches and terminates successfully. The parallel
+ * executions concerned include both {@link ParallelGoal} and {@link
+ * RepeatGoal} goals, as well as {@link TeamGoal} goals directed to
+ * a multi-filled {@link Team.Role}.
+ */
+public class ParallelEndException extends Exception {
+    
+    /**
+     * Version identity required for serialization.
+     */
+    public static final long serialVersionUID = 1L;
+
+    /**
+     * Constructor.
+     * @param s
+     */
+    public ParallelEndException(String s) {
+       super( s );
+    }
+}
diff --git a/com/intendico/gorite/ParallelGoal.java b/com/intendico/gorite/ParallelGoal.java
new file mode 100644 (file)
index 0000000..038738e
--- /dev/null
@@ -0,0 +1,113 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A ParallelGoal is achieved by means of achieving all its sub goals
+ * in parallel. If any sub goal execution fails, then all other sub
+ * goal executions are interrupted by means of throwing an {@link
+ * java.lang.InterruptedException InterruptedException}, and
+ * thereafter the parallel goal execution fails (when all the sub goal
+ * executions have terminated).
+ *
+ * <p> A ParallelGoal execution can also be interrupted to end in
+ * success, by means of throwing a {@link ParallelEndException} within
+ * one of its branches. In this case all other sub goal executions are
+ * interrupted by means of throwing an {@link
+ * java.lang.InterruptedException InterruptedException}, and
+ * thereafter the parallel goal execution succeeds (when all the sub
+ * goal executions have terminated).
+ *
+ * <p> See also {@link ControlGoal}, which throws a {@link
+ * ParallelEndException} if its sub goals succeed. The following is a
+ * code snippet to illustrate the use of an embedded {@link
+ * ControlGoal} for an optional premature interupt of an indefinite
+ * intention.
+ * 
+ * <pre>
+ * new ParallelGoal( ..., new Goal [] {
+ *     new BDIGoal( "do something indefinitely" ),
+ *     new FailGoal( "not interrupting is fine", new Goal [] {
+ *         new ControlGoal( "interrupt if desired", new Goal [] {
+ *             new BDIGoal( "decide if and when to interrupt" )
+ *         }
+ *     }
+ * } )
+ * </pre>
+ */
+public class ParallelGoal extends BranchingGoal {
+
+    /**
+     * Constructor.
+     */
+    public ParallelGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public ParallelGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a ParallelGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new ParallelInstance( head );
+    }
+
+    /**
+     * Implements parallel execution of sub goals.
+     */
+    public class ParallelInstance extends MultiInstance {
+
+       /**
+        * Constructor.
+        */
+        public ParallelInstance(String h) {
+            super( h );
+        }
+
+       /**
+        * There is one branch for each sub goal.
+        */
+       public boolean more(int i,Data d) {
+           return getGoalSubgoals() != null && i < getGoalSubgoals().length;
+       }
+
+       /**
+        * The sub goal branch is obtained by instantiating the sub
+        * goal.
+        */
+       public Instance getBranch(int i,String head,Data d) {
+           return getGoalSubgoals()[ i ].instantiate( head, d );
+       }
+    }
+}
diff --git a/com/intendico/gorite/Performer.java b/com/intendico/gorite/Performer.java
new file mode 100644 (file)
index 0000000..95dceec
--- /dev/null
@@ -0,0 +1,834 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Observable;
+import java.util.Observer;
+import com.intendico.data.Ref;
+import com.intendico.data.RuleSet;
+import com.intendico.data.Rule;
+import com.intendico.data.Query;
+import com.intendico.gorite.Goal.Instance;
+import com.intendico.gorite.addon.TodoGroupSkipBlocked;
+import com.intendico.gorite.addon.TodoGroupRoundRobin;
+import com.intendico.gorite.addon.TodoGroupParallel;
+
+/**
+ * The Performer class is base class for {@link Team} member
+ * implementations. Conceptually a performer is considered to be the
+ * agent for {@link Goal} executions, and it contains the belief
+ * structures, if any, that agent reasoning may refer to.
+ *
+ * <p> A Performer is a {@link Capability} in that it has {@link Goal}
+ * hierarchies, and inner capabilities that define its reasoning
+ * processes.
+ *
+ * <p> The goals of performer may be grouped to belong to named "to-do
+ * groups", which are represented by the inner class {@link
+ * Performer.TodoGroup}. The goals of a TodoGroup are managed such
+ * that only one at a time is performed, and the group is associated
+ * with meta-level reasoning for deciding which goal to perform next.
+ */
+public class Performer extends Capability {
+
+    /**
+     * The {@link Data.Element} name of the Todogroup object concerned
+     * when performing TodoGroups meta-level reasoning.
+     */
+    public static final String TODOGROUP = "todogroup";
+
+    /**
+     * Holds the Performer's name.
+     */
+    public String name;
+
+    /**
+     * Default constructor. When this is used, the {@link #name} field
+     * should be assigned before the performer is made to fill a role.
+     */
+    public Performer() {
+       this( "nobody" );
+    }
+
+    /**
+     * Constructor with {@link #name} given.
+     */
+    public Performer(String n) {
+       name = n;
+       performer = this;
+    }
+
+    /**
+     * Returns a text representation of this performer.
+     */
+    public String toString() {
+       return getClass().getName() + ": " + name;
+    }
+
+    /**
+     * Overrides {@link Capability#addCapability} so as to link up
+     * added capabilities with this performer.
+     */
+    public void addCapability(Capability c) {
+       c.setPerformer( this );
+       super.addCapability( c );
+    }
+
+    //
+    // The performer's rule set, if any.
+    //
+
+    /**
+     * The Performer's rule set.
+     */
+    public RuleSet rule_set = new RuleSet() {{
+       addObserver( new Observer() {
+               public void update(Observable x,Object y) {
+                   signalExecutor();
+               }
+           } );
+    }};
+
+    /**
+     * Utility method to apply all rules exhaustively.
+     */
+    public void propagateBeliefs() {
+       rule_set.run();
+    }
+
+    //
+    // TodoGroup based execution support
+    //
+
+    /**
+     * A constant goal name for a generic TodoGroup meta goal.
+     * Presently "todogroup meta goal".
+     */
+    public final static String TODOGROUP_META_GOAL =
+       TODOGROUP + " meta goal";
+
+    /**
+     * This performer's {link TodoGroup} objects.
+     */
+    public Hashtable/*<String,TodoGroup>*/ todogroups =
+        new Hashtable/*<String,TodoGroup>*/();
+
+    /**
+     * Utility method to find a named {@link TodoGroup}. This method
+     * will create a new {@link TodoGroup} rather than return
+     * <tt>null</tt>, and the new {@link TodoGroup} will have its
+     * {@link TodoGroup#meta_goal} string set to <tt>null</tt> by
+     * default (meaning no meta goal), unless the system property
+     * <tt>gorite.todo.classic</tt> is set, in which case {@link
+     * #TODOGROUP_META_GOAL} is used as meta goal name.
+     * @param rn the {@link TodoGroup} name
+     * @return the {@link TodoGroup} object of that name among this
+     * performer's {@link #todogroups}.
+     */
+    public TodoGroup getTodoGroup(String rn) {
+       if ( rn == null ) 
+           return null;
+       TodoGroup r = (TodoGroup) todogroups.get( rn );
+       if ( r == null ) {
+            if ( Goal.isTracing() ) {
+               System.err.println( "-- " + name + " adds TodoGroup " + rn );
+           }
+           String mg = null;
+           if ( System.getProperty( "gorite.todo.classic" ) != null )
+               mg = TODOGROUP_META_GOAL;
+           r = new TodoGroup( rn, mg );
+           addTodoGroup( r );
+       }
+       return r;
+    }
+
+    /**
+     * Utility method to add a TodoGroup with given name and meta goal.
+     */
+    public void addTodoGroup(String n,String mg) {
+       addTodoGroup( new TodoGroup( n, mg ) );
+    }
+    
+    /**
+     * Utility method to add a given TodoGroup.
+     */
+    public void addTodoGroup(TodoGroup r) {
+       if ( todogroups == null )
+           todogroups = new Hashtable/*<String,TodoGroup>*/();
+       todogroups.put( r.name, r );
+    }
+
+    /**
+     * Control flag for TodoGroup execution.
+     */
+    private long todo_added = 0;
+
+    /**
+     * Utility method to add an instance to its todogroup.
+     */
+    public Goal.States execute(
+       boolean reentry,Goal.Instance instance,String group)
+       throws LoopEndException, ParallelEndException
+    {
+       if ( Goal.isTracing() )
+           System.err.println( "-- execute: " + group + " " + reentry );
+       //todo_added += 1;
+       return getTodoGroup( group ).execute( reentry, instance );
+    }
+
+    /**
+     * Index of TodoGroup to execute next.
+     */
+    private int todo_index = 0;
+
+    /**
+     * Control method to run all todogroups until stopped or blocked.
+     */
+    synchronized public Goal.States runPerformer() {
+       if ( Goal.isTracing() )
+           System.err.println(
+               "-- " + name + " running " + 
+               ( todogroups == null? 0 : todogroups.size() ) );
+       propagateBeliefs();
+       if ( todogroups == null || todogroups.size() == 0 )
+           return Goal.States.BLOCKED;
+       boolean running = true;
+       Goal.States s = Goal.States.BLOCKED;
+       while ( running ) {
+           long todo_added_cache = todo_added;
+           TodoGroup [] groups = (TodoGroup []) todogroups.values().toArray(
+               new TodoGroup [ todogroups.size() ] );
+           running = false;
+           propagateBeliefs();
+           for ( int i = 0; i < groups.length; i++ ) {
+               if ( todo_index >= groups.length )
+                   todo_index = 0;
+               s = groups[ todo_index++ ].runGroup();
+               if ( Goal.isTracing() )
+                   System.err.println( "-- runGroup = " + s );
+               propagateBeliefs();
+               running = todo_added != todo_added_cache;
+               if ( s == Goal.States.BLOCKED ) {
+                   continue;
+               }
+               if ( s == Goal.States.STOPPED )
+                   break;
+               running = true;
+               s = Goal.States.BLOCKED;
+           }
+       }
+       if ( Goal.isTracing() )
+           System.err.println( "-- " + name + " (" + s + ")" );
+       return s;
+    }
+
+    /**
+     * The TodoGroup class provides a meta-level goal execution
+     * facility, allowing multiple parallel executions be
+     * synchronised.
+     *
+     * <p> A Goal that has its {@link Goal#group} attribute set will
+     * be executed by adding its execution instance to the nominated
+     * TodoGroup. The instance execution will be progressed as a
+     * TodoGroup execution task, when the execution thread gets around
+     * to it.
+     *
+     * <p> The TodoGroup is associated with a meta goal {@link
+     * #meta_goal}, which is performed by the execution machinery when
+     * the TodoGroup state changes: when a goal instance is added,
+     * when its execution gets blocked, and when it completes. The
+     * meta goal execution may inspect the TodoGroup and promote any
+     * of the pending goal instance executions to be the one to
+     * progress next.
+      */
+    public class TodoGroup implements Observer {
+
+       /**
+        * A name attribute for the group. There is no particular
+        * significance in this name except that it uniquely
+        * identifies a TodoGroup object for a {@link Performer}.
+        */
+       public String name = null;
+
+       /**
+        * This names the {@link BDIGoal} to be invoked when {@link
+        * Goal.Instance} objects are added to, or removed from the
+        * TodoGroup, as well as when the intention execution returns
+        * blocked. The meta goal is invoked for the purpose of
+        * deciding which intention to pursue next.
+        * 
+        * <p> The default name is {@link
+        * Performer#TODOGROUP_META_GOAL}.
+        *
+        * <p> When the meta goal is invoked, it is provided with the
+        * following data elements:
+        * <dl>
+        * <dt>named by {@link Goal#PERFORMER}</dt><dd>this {@link
+        * Performer} object</dd>
+        * <dt>named by {@link #TODOGROUP}</dt><dd>this TodoGroup
+        * object</dd>
+        * <dt>"added"</dt><dd>{@link Vector} of the {@link
+        * Goal.Instance} objects just added to the group, if any.</dd>
+        * <dt>"removed"</dt><dd>{@link Vector} of the {@link
+        * Goal.Instance} objects just removed from the group, if any.</dd>
+        * <dt>"top"</dt><dd>the current top-of-stack {@link
+        * Goal.Instance} object. When an instance terminates, it is
+        * added to the "removed" set, then removed from the stack
+        * before the meta goal gets invoked.</dd>
+        * </dl>
+        *
+        * <p> Note that the "added" and "removed" collections have
+        * been processed, and their contents have been added and
+        * removed respectively before the meta goal is performed.
+        *
+        * <p> A meta goal plan typically uses {@link #promote} to
+        * change the {@link #stack}. Still, no other code is supposed
+        * to operate on the stack concurrently, so the meta goal plan
+        * may change the stack in more complex ways.
+        *
+        * @see TodoGroupSkipBlocked
+        * @see TodoGroupRoundRobin
+        * @see TodoGroupParallel
+        */
+       public String meta_goal = null;
+
+       /**
+        * Constructor given name and meta goal name. If the meta goal
+        * name is null, then the default meta goal name ({@link
+         * Performer#TODOGROUP_META_GOAL}) is used.
+        */
+       public TodoGroup(String n,String mg) {
+           name = n;
+           meta_goal = mg;
+       }
+
+       /**
+        * This is the stack of executions in this TodoGroup.
+        */
+       public Vector/*<Goal.Instance>*/ stack =
+           new Vector/*<Goal.Instance>*/();
+
+        public Vector/*<Goal.Instance>*/ added =
+            new Vector/*<Goal.Instance>*/();
+
+        public Vector/*<Goal.Instance>*/ removed =
+            new Vector/*<Goal.Instance>*/();
+
+       /**
+        * Keeping track of an ongoing meta goal execution. This is in
+        * particular used if a meta goal execution is blocked or
+        * stopped, in which case the pursuit of TodoGroup tasks is
+        * stopped. The ongoing meta goal execution must terminate
+        * (passed, failed or cancel) before the next TodoGroup task
+        * is pursued.
+        *
+        * <p> Note that new TodoGroup tasks can be added during an
+        * ongoing meta goal execution, and that this will not trigger
+        * any new meta goal execution. However, it will cause {@link
+        * #added_while_ongoing} to be set, which in turn results in
+        * an additional meta goal execution to follow the ongoing
+        * one.
+        */
+       public Goal.Instance ongoing_meta_goal = null;
+
+       /**
+        * A flag that indicates that a TodoGroup task was added
+        * during an ongoing meta goal execution. This will cause a
+        * subsequent meta goal execution before any TodoGroup task is
+        * progressed.
+        */
+       public boolean added_while_ongoing = false;
+
+       /**
+        * Invoked prior to pursuing the top element of the
+        * stack. This method adds the added instances onto the stack,
+        * cleares out the removed instances. It creates a meta goal
+        * instance if either forced is true, or the some instance was
+        * added or removed.
+        *
+        * <p> Note that this method will change the {@link #stack} by
+        * removing completed tasks, and actually adding tasks pending
+        * to be added. This happens before the invocation of the meta
+        * goal (if any). 
+        */
+       private Goal.Instance enterGroup(boolean forced) {
+           Vector/*<Goal.Instance>*/ coming = null;
+           Vector/*<Goal.Instance>*/ gone = null;
+
+           // pick up the added and removed instances
+           // the added vector changes by parallel threads
+           synchronized ( added ) {
+               coming = added;
+               added = new Vector/*<Goal.Instance>*/();
+               gone = removed;
+               removed = new Vector/*<Goal.Instance>*/();
+           }
+
+           // handle removed instances
+           for ( Iterator/*<Goal.Instance>*/ i = gone.iterator();
+                 i.hasNext(); ) {
+               Goal.Instance instance = (Goal.Instance) i.next();
+               instance.data.deleteObserver( instance.thread_name, this );
+           }
+
+           // handle added instances
+           for ( Iterator/*<Goal.Instance>*/ i = coming.iterator();
+                 i.hasNext(); ) {
+                Goal.Instance instance = (Goal.Instance) i.next();
+               stack.add( instance );
+               instance.data.addObserver( instance.thread_name, this );
+           }
+
+           if ( meta_goal == null )
+               return null;
+
+           if ( coming.size() + gone.size() == 0 ) {
+               if ( ! forced )
+                   return null;
+               if ( System.getProperty( "gorite.todo.classic" ) != null )
+                   return null;
+           }
+
+           if ( Goal.isTracing() ) {
+               System.err.println(
+                   "-- " + Performer.this.name + "/" + name +
+                   " meta goal " + added + " \"" + meta_goal + "\"" );
+           }
+
+           Goal.Instance mg = new BDIGoal( meta_goal )
+               .instantiate( name, null );
+           mg.performer = Performer.this;
+           mg.data = new Data()
+               .setValue( Goal.PERFORMER, Performer.this )
+               .setValue( TODOGROUP, this )
+               .setValue( "added", coming )
+               .setValue( "removed", gone )
+               .setValue( "top", stack.size() == 0? null : stack.get( 0 ) );
+           mg.data.link( mg.thread_name );
+
+           return mg;
+       }
+
+       /**
+        * A transient handle to the most recent blocking intention on
+        * the actual stack.
+        */
+       public Goal.Instance blocking = null;
+
+       /**
+        * Run the group. This means to run its top-of-stack goal
+        * instance. If that passes or fails, it is removed from the
+        * group, and the meta goal is called upon to possibly
+        * re-order remaining goal instances. If it returns STOPPED,
+        * the group execution returns STOPPED. If it returns BLOCKED,
+        * the meta goal is invoked in order to possibly re-arrange
+        * the group, and if so, the group execution continues with
+        * the new top goal instance. Otherwise the group execution
+        * returns blocked.
+        */
+       public Goal.States runGroup() {
+            if ( Goal.isTracing() ) {
+               System.err.println(
+                   "-- TodoGroup " + Performer.this.name + "/" + name +
+                   " " + stack.size() );
+           }
+           for ( ;; ) {
+               if ( ongoing_meta_goal != null ) {
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "-- TodoGroup " + Performer.this.name +
+                           "/" + name + " ongoing meta goal " +
+                           stack.size() );
+                   }
+                   Goal.States s = ongoing_meta_goal.action();
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "-- " + Performer.this.name + "/" + name +
+                           " meta goal " + " \"" + meta_goal + "\" " +
+                           s );
+                   }
+                   if ( s == Goal.States.STOPPED ) {
+                       continue; // Don't interrupt meta goal
+                   }
+                   if ( s == Goal.States.BLOCKED ) {
+                       return s;
+                   }
+               }
+               ongoing_meta_goal = enterGroup( ongoing_meta_goal == null );
+               if ( ongoing_meta_goal != null )
+                   continue;
+               if ( stack.size() == 0 )
+                   return Goal.States.BLOCKED;
+
+               Goal.Instance x = (Goal.Instance) stack.get( 0 );
+
+               if ( x == blocking ) {
+                   if ( ! x.data.isRunning( x.thread_name ) ) {
+                       return Goal.States.BLOCKED;
+                   }
+                   if ( Goal.isTracing() ) {
+                       System.err.println( "-- BLOCKING " + x.head +
+                                           " without trigger" );
+                   }
+                   blocking = null;
+                   //return Goal.States.BLOCKED;
+               }
+               blocking = null;
+               Goal.States s = x.action();
+               if ( Goal.isTracing() ) {
+                   System.err.println( "-- noting " + x.head + " " + s );
+               }
+               if ( s == Goal.States.STOPPED ) {
+                   return s;
+               }
+               if ( s == Goal.States.BLOCKED ) {
+                   blocking = x;
+                   continue;
+               }
+               // s == Goal.States.PASSED
+               // s == Goal.States.FAILED
+               // s == Goal.States.CANCEL
+               x.setMonitoring( s );
+               if ( stack.remove( x ) )
+                   removed.add( x );
+           }
+       }
+
+       /**
+        * Adds a goal for execution as part of this todo
+        * group. Returns the instance monitoring state.
+        */
+       public Goal.States execute(
+           boolean reentry,Goal.Instance instance)
+           throws LoopEndException, ParallelEndException
+       {
+           if ( ! reentry ) {
+               todo_added += 1;
+                signalExecutor();
+               synchronized ( added ) {
+                   instance.setMonitoring( Goal.States.STOPPED );
+                   added.add( instance );
+               }
+                signalExecutor();
+           } else {
+               instance.throwSavedException();
+           }
+           return instance.state;
+       }
+
+       /**
+        * Utility method to obtain stack size.
+        */
+       public int size() {
+           return stack.size();
+       }
+
+       /**
+        * Utility method to access the i:th stack element.
+        * @throws ArrayIndexOutOfBoundsException if the stack is
+        * empty, or i is negative.
+        */
+       public Instance get(int i) {
+           return (Instance) stack.get( i );
+       }
+
+       /**
+        * Utility method to remove the i:th stack element.
+        * @throws ArrayIndexOutOfBoundsException if appropriate
+        */
+       public Instance remove(int i) {
+           return (Instance) stack.remove( i );
+       }
+
+       /**
+        * Utility method to insert an element to be the i:th stack
+        * element.
+        * @throws ArrayIndexOutOfBoundsException if appropriate
+        */
+       public void add(int i,Instance x) {
+           stack.add( i, x );
+       }
+
+       /**
+        * Utility method to move a selected {@link Goal.Instance} to
+        * be the one to pursue next.
+        */
+       public void promote(int index) {
+           if ( index != 0 )
+               stack.insertElementAt( stack.remove( index ), 0 );
+       }
+
+       /**
+        * Utility method to move the top {@link Goal.Instance} to
+        * stack bottom.
+        */
+       public void rotate() {
+           if ( stack.size() > 1 )
+               stack.add( stack.remove( 0 ) );
+       }
+
+       ///
+       /// Observer interface implementation
+       ///
+
+       /**
+        * Implements {@link Observer#update} by pretending that a
+        * todo group is added, as way of signalling that the
+        * performer should process its todogroups again
+        */
+       public void update(Observable x,Object y) {
+           todo_added += 1;
+       }
+
+    } // End class TodoGroup
+
+    /**
+     * The {@link Executor} for this performer.
+     */
+    public Executor executor = null;
+
+    /**
+     * Utility method to signal the {@link #executor} that this
+     * performer wants some attention. This will use the current
+     * executor, and attach to the default executor if none is
+     * assigned.
+     */
+    public void signalExecutor() {
+       if ( executor == null ) {
+           executor = Executor.getDefaultExecutor();
+           shareInquirables( null );
+           executor.addPerformer( this );
+       }
+       executor.signal();
+    }
+
+    /**
+     * Utility method to instantiate the goal for given the head, and
+     * then keep invoking the perform method with the given data
+     * context, until it passes or fails.
+     */
+    synchronized public boolean performGoal(Goal goal,String head,Data d) {
+       signalExecutor();
+       try {
+           Goal.Instance instance = goal.instantiate( head, d );
+           Goal.States s = Goal.States.PASSED;
+           for ( ;; ) {
+               if ( Goal.isTracing() )
+                   System.err.println( "** perform " + instance.head );
+               d.replaceValue( Goal.PERFORMER, this );
+               s = instance.perform( d );
+               if ( s == Goal.States.PASSED || s == Goal.States.FAILED )
+                   break;
+               boolean idle = s == Goal.States.BLOCKED;
+               if ( executor != null ) {
+                   if ( executor.runPerformersBlocked() )
+                       idle = false;
+               }
+               if ( idle )
+                   instance.waitDone(); // ignore result
+           }
+           if ( executor != null ) {
+               while ( executor.runPerformersBlocked() )
+                   ;
+           }
+           return s == Goal.States.PASSED;
+       } catch (LoopEndException e) {
+           e.printStackTrace();
+           return false;
+       } catch (ParallelEndException e) {
+           e.printStackTrace();
+           return false;
+       }
+    }
+
+    /**
+     * Alternative performGoal method, with input and output held as
+     * Vector<Ref> collections.
+     */
+    public boolean performGoal(
+       Goal goal,String head, Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
+       Data d = new Data();
+       d.set( ins );
+       boolean b = performGoal( goal, head, d );
+       d.get( outs, true );
+       return b;
+    }
+
+    /**
+     * Alternative performGoal method, with goal given as a {@link
+     * String}, implying a BDI goal, and input and output held as
+     * Vector<Ref> collections.
+     */
+    public boolean performGoal(
+       String goal,String head, Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
+       return performGoal( new BDIGoal( goal ), head, ins, outs );
+    }
+
+    /**
+     * Returns true if the performer execution should change focus.
+     */
+    public boolean changeFocus() {
+       return executor != null && executor.changeFocus();
+    }
+
+    /**
+     * Returns a {@link RoleFilling} object for filling a given {@link
+     * Team.Role}. The object represents that this performer fills the
+     * given {@link Team.Role}. This method may be overridden in a
+     * specific performer, either as way of providing an extended
+     * {@link RoleFilling} object, or to possibly refuse the {@link
+     * Team.Role} filling, by returning <em>null</em>.
+     */
+    public RoleFilling fillRole(Team.Role r) {
+       return new RoleFilling( r );
+    }
+
+    /**
+     * This class is used for representing this performer when acting
+     * in a given {@link Team.Role}. It in particular provides a
+     * {@link #lookup(String)} method that provides a combined plan
+     * lookup in both the filled {@link Team.Role} (qua {@link
+     * Capability}) and the enclosing {@link Performer} (qua {@link
+     * Capability}), and thereby extending the performer's capability
+     * with the role plans.
+     */
+    public class RoleFilling extends Capability {
+
+       /**
+        * The {@link Team.Role} being filled.
+        */
+       public Team.Role role;
+
+       /**
+        * Constructor. The enclosing performer is set to fill the
+        * given role.
+        */
+       public RoleFilling(Team.Role r) {
+           role = r;
+       }
+
+       /**
+        * Extends the base class method {@link Capability#lookup} by
+        * wrapping the result goals into {@link TransferGoal} goals.
+        */
+       public Vector/*<Goal>*/ lookup(String name) {
+           Vector/*<Goal>*/ v = Performer.this.lookup( name );
+           Vector/*<Goal>*/ vx = role.lookup( name );
+           vx.addAll( super.lookup( name ) );
+           for ( Iterator/*<Goal>*/ gi = v.iterator(); gi.hasNext(); ) {
+               vx.add( goalForPerformer( (Goal) gi.next() ) );
+           }
+           return vx;
+       }
+
+       /**
+        * Extends the base class method {@link Capability#hasGoals} by
+        * formarding to the performer
+        */
+       public boolean hasGoal(String name) {
+           return role.hasGoal( name ) ||
+               super.hasGoal( name ) ||
+               Performer.this.hasGoal( name );
+       }
+
+       /**
+        * Returns the {@link Performer} of this RoleFilling.
+        */
+       public Performer getPerformer() {
+           return Performer.this;
+       }
+
+       /**
+        * Returns a text representation of this performer object.
+        */
+       public String toString()
+       {
+           return "role filler " + Performer.this;
+       }
+    }
+
+    /**
+     * Utility method to obtain the {@link Team} that has established
+     * a {@link Team.Role} filler under a given name in the {@link
+     * Data}.
+     */
+    static public Team team(String rolename,Data d) {
+       RoleFilling r = (RoleFilling) d.getValue( rolename );
+       return r.role.team();
+    }
+
+    /**
+     * Utility method to create a {@link TransferGoal} object aimed
+     * at this performer.
+     */
+    public Goal goalForPerformer(Goal g) {
+       return new TransferGoal( this, g );
+    }
+
+    /**
+     * A method that returns {@link #name}.
+     */
+    public String getName() {
+       return name;
+    }
+
+    ///
+    /// Plan choice support
+    ///
+
+    /**
+     * The association between (BDI) goals and their plan choice
+     * modifiers. These take effect in the BDI plan choice processing,
+     * to provide the decision for choosing a next applicable plan
+     * instance. The modifier should be either a {@link
+     * java.util.Random Random} object
+     * (for making a random draw among highest precedence plan
+     * options0, or a {@link String} (for making the choice by meta
+     * level plan processing).
+     *
+     * @see BDIGoal
+     */
+    public Hashtable/*<String,Object>*/ plan_choices = null;
+
+    /**
+     * Utility method to declare a plan choice modifier for a
+     * goal. The modifier should be either a {@link java.util.Random
+     * Random} object, or a {@link String}.
+     *
+     * @see BDIGoal
+     */
+    public void setPlanChoice(String goal,Object choice_method) {
+       if ( plan_choices == null ) {
+           plan_choices = new Hashtable/*<String,String>*/();
+       }
+       plan_choices.put( goal, choice_method );
+    }
+
+    /**
+     * Utility method to obtain the plan choice modifier, if any,
+     * associated with the given goal.
+     */
+    public Object getPlanChoice(String goal) {
+       return plan_choices == null? null : plan_choices.get( goal );
+    }
+}
diff --git a/com/intendico/gorite/Plan.java b/com/intendico/gorite/Plan.java
new file mode 100644 (file)
index 0000000..927cf55
--- /dev/null
@@ -0,0 +1,167 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+import com.intendico.data.Equals;
+
+/**
+ * The Plan class is a {@link Goal} that implements {@link Context}
+ * and {@link Precedence}.
+ *
+ * <p> The typical use of Plan object is as the top level {@link Goal}
+ * objects that get added to a {@link Capability}.
+ *
+ * @see BDIGoal
+ * @see AnyGoal
+ * @see AllGoal
+ * @see DataGoal
+ * @see TeamGoal
+ */
+public class Plan extends SequenceGoal implements Context, Precedence {
+
+    /**
+     * Holds a plan name. This name is not used for anything within
+     * GORITE, but may serve as an identifier for a plan for a
+     * developer.
+     */
+    private String plan_name;
+
+    /**
+     * Utility method that creates a context Query for defining a data
+     * element value.
+     */
+    public static Query define(String name,Object value) {
+       Ref r = new Ref( name );
+       r.set( value );
+       return new Equals( new Object [] { r } );
+    }
+
+    /**
+     * Constructor for a given plan name, goal name and sub goals. The
+     * given sub goals comprise the plan body. The goal name is that
+     * which the plan is intended to achieve. The plan name is held in
+     * {@link #plan_name}.
+     */
+    public Plan(String pn,String n,Goal [] sg) {
+       super( n, sg );
+       plan_name = pn;
+    }
+
+    /**
+     * Constructor for a given plan name, goal name and without sub
+     * goals. The goal name is that which the plan is intended to
+     * achieve. The plan name is held in {@link #plan_name}.
+     */
+    public Plan(String pn,String n) {
+       this( pn, n, null );
+    }
+
+    /**
+     * Constructor for a given goal name and sub goals. The given sub
+     * goals comprise the plan body. The goal name is that which the
+     * plan is intended to achieve.
+     */
+    public Plan(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Constructor for a given goal name without sub goals. A body may
+     * be assigned to this plan at initialisation or later, or
+     * alternatively, this plan would be a <i>task</i> by virtue of an
+     * overriding {@link Goal#execute} implementation. The goal name
+     * is that which the plan is intended to achieve.
+     */
+    public Plan(String n) {
+       this( null, n, null );
+    }
+
+    /**
+     * Construction for a goal name given as Class. Actual goal name
+     * is the name of the Class.
+     */
+    public Plan(Class goal) {
+       this( null, goal.getName(), null );
+    }
+
+    /**
+     * Construction for a goal name given as Class, with plan
+     * name. Actual goal name is the name of the Class.
+     */
+    public Plan(String name,Class goal) {
+       this( name, goal.getName(), null );
+    }
+
+    /**
+     * Construction for a goal name given as Class, with sub
+     * goals. Actual goal name is the name of the Class.
+     */
+    public Plan(Class goal,Goal [] sg) {
+       this( null, goal.getName(), sg );
+    }
+
+    /**
+     * Construction for a goal name given as Class, with plan name and
+     * sub goals. Actual goal name is the name of the Class.
+     */
+    public Plan(String name,Class goal,Goal [] sg) {
+       this( name, goal.getName(), sg );
+    }
+
+    /**
+     * The default precedence level for goals.
+     */
+    public final static int DEFAULT_PRECEDENCE = 5;
+
+    /**
+     * Default context method.
+     */
+    public Query context(Data d) throws Exception {
+       return null;
+    }
+
+    /**
+     * Default precedence method. Returns {@link
+     * #DEFAULT_PRECEDENCE}.
+     */
+    public int precedence(Data d) {
+       return DEFAULT_PRECEDENCE;
+    }
+
+    /**
+     * Returns a {@link String} representation of this object.
+     */
+    public String toString() {
+       String n = "Plan:";
+       if ( plan_name != null )
+           n += " " + plan_name;
+       return n + "\n" + super.toString();
+    }
+
+    /**
+     * Returns the {@link #plan_name} attribute.
+     */
+    public String getPlanName() {
+       return plan_name;
+    }
+
+}
diff --git a/com/intendico/gorite/PlanChoiceGoal.java b/com/intendico/gorite/PlanChoiceGoal.java
new file mode 100644 (file)
index 0000000..8b10515
--- /dev/null
@@ -0,0 +1,181 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.gorite.Goal.States;
+import com.intendico.data.*;
+import java.util.Vector;
+
+/**
+ * This is a utility class used by BDIGoal execution, to inject a plan
+ * choice goal for choosing the plan instance to pursue. It is not
+ * intended for explicit use within goal hierarchies.
+ *
+ * <p> The {@link BDIGoal} uses the {@link
+ * Performer#getPlanChoice(String)} method to determine whether or not
+ * a goal is asoicated with a plan choice goal. If this is the case,
+ * then it will create a PlanChoiceGoal instance for handling the
+ * choice of which of the applicable plan options to pursue next, and
+ * the subsequent execution of that option.
+ *
+ * <p> The choice is performed by triggering a (freschly created)
+ * {@link BDIGoal} for the plan choice goal, with a separate Data
+ * object that is initialised with "options", "failed" and "data". As
+ * the plan choice goal succeeds, the resulting "choice" data is used
+ * as being the choice of plan option, and then that plan option is
+ * executed as an attempt of achieving the original goal. If that
+ * succeeds, the original goal succeeds without further ado, and if it
+ * fails, then the BDIGoal forms a new, possibly empty collection of
+ * applicable (non-failed) plan options, and trigger a new
+ * PlanChoiceGoal for that collection.
+ *
+ * <p> If the plan choice goal fails, then all applicable options at
+ * this point are added to the failed set, the {@link BDIGoal}
+ * continues by forming a new, possibly empty collection of applicable
+ * (non-failed) plan options, and triggering a new PlanChoiceGoal for
+ * that collection.
+ *
+ * <p> If the plan choice goal fails for the final triggering, i.e,
+ * with an empty collection of applicable (non-failed) plan options,
+ * then the original goal fails. In fact, the orginal goal fails also
+ * if the final plan choice goal succeeds, except if this is also the
+ * first triggering and thus, there are no failed plan options
+ * either. In that case, the original goal will succeed if the plan
+ * choice goal succeeds.
+ *
+ * @see Performer#getPlanChoice(String)
+ * @see Performer#setPlanChoice(String,Object)
+ */
+public class PlanChoiceGoal extends Goal {
+
+    /**
+     * The invocation data.
+     */
+    public Data choice_data;
+
+    /**
+     * Status flag for plan choice processing.
+     */
+    public States done = States.STOPPED;
+
+    /**
+     * The initial first option.
+     */
+    public Goal first;
+
+    /**
+     * The choice goal. This is set by the plan choice processing.
+     */
+    public Goal choice;
+
+    /**
+     * The currently executing goal instance.
+     */
+    public Instance instance = null;
+
+    /**
+     * Sub goal head, which is set when this goal is instantiated
+     */
+    public String head;
+
+    /**
+     * Constructor for a given plan choice goal and collection of plan
+     * instances.
+     */
+    public PlanChoiceGoal(
+       Performer p,String pg,Vector/*<Goal>*/ plans,Vector/*<Goal>*/ failed) {
+       super( pg );
+       choice_data = new Data();
+       choice_data.setValue( Goal.PERFORMER, p );
+       choice_data.setValue( "options", plans );
+       choice_data.setValue( "failed", failed );
+       if ( plans.size() > 0 )
+           first = (Goal) plans.get( 0 );
+    }
+
+    /**
+     * Instantiating this goal for execution.
+     */
+    public Goal.Instance instantiate(String h,Data d) {
+       head = h;
+       choice_data.setValue( "data", d );
+       instance = new BDIGoal(
+           getGoalName() ).instantiate( h + "?", choice_data );
+       return super.instantiate( h, d );
+    }
+
+    /**
+     * Control callback whereby the goal gets notified that an (its)
+     * intention is cancelled. This will forward the cancellation to
+     * the currently progressing instance, if any.
+     */
+    public void cancelled(Instance which) {
+       if ( instance != null )
+           instance.cancel();
+    }
+
+    /**
+     * Excution of this goal. If choice is null, then the execution is
+     * like a BDIGoal of the plan choice goal, with its data holding
+     * the "options", for setting the "choice". If that execution is
+     * successful, then the choice is taken, otherwise the original
+     * first choice is taken. If choice is set, then the execution
+     * executes that goal.
+     */
+    public States execute(Data data)
+       throws ParallelEndException {
+       for ( ;; ) {
+           States s = States.FAILED;
+           try {
+               s = instance.perform( choice == null? choice_data: data );
+           } catch (LoopEndException e) {
+               s = States.FAILED;
+           } catch (ParallelEndException e) {
+               s = States.FAILED;
+               if ( done != States.STOPPED )
+                   throw e;
+           }
+           if ( done != States.STOPPED )
+               return s;
+           if ( s != States.PASSED && s != States.FAILED )
+               return s;
+           done = s;
+           if ( s == States.FAILED ) {
+               Vector/*<Goal>*/ options = (Vector/*<Goal>*/)
+                   choice_data.getValue( "options" );
+               Vector/*<Goal>*/ failed = (Vector/*<Goal>*/)
+                   choice_data.getValue( "failed" );
+               failed.addAll( options );
+               choice = first;
+               return States.FAILED;
+           }
+           choice = (Goal) instance.getValue( "choice" );
+           if ( choice == null )
+               choice = first;
+           if ( choice == null ) {
+               Vector/*<Goal>*/ failed = (Vector/*<Goal>*/)
+                   choice_data.getValue( "failed" );
+               return failed.size() == 0? States.PASSED : States.FAILED;
+           }
+           instance = choice.instantiate( head + "!", data );
+       }
+    }
+
+}
diff --git a/com/intendico/gorite/PoolExecutor.java b/com/intendico/gorite/PoolExecutor.java
new file mode 100644 (file)
index 0000000..d6d4436
--- /dev/null
@@ -0,0 +1,92 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+/**
+ * This is an Executor extension intended for applications containing
+ * multiple models (i.e., Executors), and multiple threads to execute
+ * them. The PoolExecutor is tied to a {@link
+ * java.util.concurrent.Executor} on concstruction. It adds itself for
+ * running whenever it gets runnable by signalling, or any performer
+ * ended in STOPPED rather than BLOCKED. This approach allows an
+ * execution pool of many threads, where one thread at a time is used
+ * for executing all each Executor's performers once, then
+ * interleaving the models in between these one-shot runs.
+ */
+public class PoolExecutor extends Executor {
+
+    /**
+     * Cache of the execution queue interface.
+     */
+    private java.util.concurrent.Executor runner;
+
+    /**
+     * Control flag for capturing signal calls during performer
+     * execution. This is reset by the {@link #run()} method before it
+     * executes performers, and set by the {@link #signal()} method
+     * whenever called.
+     */
+    private boolean signaled = false;
+
+    /**
+     * Constructor with a given {@link java.util.concurrent.Executor}.
+     */
+    public PoolExecutor(java.util.concurrent.Executor x) {
+       super( "PoolExecutor" );
+       runner = x;
+       x.execute( this );
+    }
+
+    /**
+     * Utility method to "tickle" the executor with, just in case it
+     * is idle. Unless marked as running, this Executor is entered
+     * into the execution queue (and marked as running).
+     */
+    synchronized public void signal() {
+       signaled = true;
+       if ( ! running ) {
+           running = true;
+           runner.execute( this );
+           notifyAll();
+       }
+    }
+
+    /**
+     * Overrides {@link Executor#run()} for a one-shot execution of
+     * all performers. If any performer ends in STOPPED rather than
+     * BLOCKED, or there is a {@link #signal()} while the execution is
+     * in progress, then this Executor is re-entered into the
+     * execution pool.
+     */
+    public void run() {
+       signaled = false;
+       if ( runPerformersOnce() != Goal.States.BLOCKED ) {
+           runner.execute( this ); // continue asap
+           return;
+       }
+       boolean b;
+       synchronized ( this ) {
+           b = running = signaled;
+       }
+       if ( b ) {
+           runner.execute( this ); // continue asap
+       }
+    }
+}
diff --git a/com/intendico/gorite/Precedence.java b/com/intendico/gorite/Precedence.java
new file mode 100644 (file)
index 0000000..cd74844
--- /dev/null
@@ -0,0 +1,49 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+
+/**
+ * The Precedence interface is intended to be implemented by a {@link
+ * Goal} class that wants to suggest its precedence among alternative
+ * {@link Plan} options for a {@link BDIGoal} goal.
+ */
+public interface Precedence {
+
+    /**
+     * The precedence method is invoked by {@link
+     * BDIGoal.BDIInstance#action} in order to sort the collection of
+     * plans such that higher precedence plans are attempted before
+     * lower precedence plans.
+     *
+     * <p> A {@link Goal} without Precedence implementation is
+     * assigned the precedence value of 5.
+     *
+     * <p> Note that this method is invoked for each of any multiple
+     * bindings defined by a {@link Plan#context} {@link Query}, with
+     * the relevant bindings assigned into the {@link Data} object
+     * provided. The same {@link Data} object will be used for the
+     * successive {@link #precedence} calls, but with the bindings
+     * successively updated to pin point the relevant plan
+     * alternative.
+     */
+    public int precedence(Data d);
+}
diff --git a/com/intendico/gorite/RepeatGoal.java b/com/intendico/gorite/RepeatGoal.java
new file mode 100644 (file)
index 0000000..5dd6da2
--- /dev/null
@@ -0,0 +1,118 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A RepeatGoal is achieved by means of achieving all its
+ * instantiations in parallel, where each instantiation is achieved by
+ * achieving the given sub goals in sequence. The goal definition
+ * includes a context specification, which enumerates the
+ * instantiations of the goal arising. Each instantiation refers to
+ * the same sequence of sub goals, but with different focus in the
+ * data.
+ *
+ * <p> The following code snippet illustrates the use of a RepeatGoal
+ * to process a multi-valued data element (named "options" in the
+ * example).
+ *
+ * <pre>
+ * new RepeatGoal( "process options", new Goal [] {
+ *     // This goal sequence has its "options" data focussed
+ *     // on one of the (previosuly multi-valued) options.
+ *     new BDIGoal( "do this with options" ),
+ *     new BDIGoal( "do that with options" ),
+ *     new BDIGoal( "do more with options" ),
+ * } ) {{ control = "options"; }} // Replicate for all the values of "options"
+ * </pre>
+ *
+ * @see LoopGoal
+ * @see BDIGoal
+ */
+public class RepeatGoal extends BranchingGoal {
+
+    /**
+     * Constructor with a given control name, goal name and sub goals.
+     */
+    public RepeatGoal(String c,String n,Goal [] sg) {
+       super( n, sg );
+       setGoalControl( c );
+    }
+
+    /**
+     * Constructor with a given goal name and sub goals.
+     */
+    public RepeatGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public RepeatGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a RepeatGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new RepeatInstance( head );
+    }
+
+    /**
+     * Implements re-instatiation of this goal relative something.
+     */
+    public class RepeatInstance extends MultiInstance {
+
+       /**
+        * Constructor.
+        */
+        public RepeatInstance(String h) {
+            super( h );
+        }
+
+       /**
+        * To decide whether yet another branch should be made.
+        */
+        public boolean more(int i,Data d) {
+           return i < d.size( getGoalControl() );
+        }
+
+       /**
+        * The branch is created be instantiating this goal as a
+        * sequence goal.
+        */
+        public Instance getBranch(int i,String head,Data d) {
+           Goal g = new SequenceGoal( getGoalName(), getGoalSubgoals() );
+           g.setGoalControl( getGoalControl() );
+           g.setGoalGroup( getGoalGroup() );
+           return g.instantiate( head, d );
+        }
+    }
+
+}
diff --git a/com/intendico/gorite/SequenceGoal.java b/com/intendico/gorite/SequenceGoal.java
new file mode 100644 (file)
index 0000000..84c7b3c
--- /dev/null
@@ -0,0 +1,111 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A SequenceGoal is achieved by means of achieving its sub goals in
+ * order. The goal fails when and if any sub goal fails.
+ */
+public class SequenceGoal extends Goal {
+
+    /**
+     * Constructor.
+     */
+    public SequenceGoal(String n,Goal [] sg) {
+       super( n, sg );
+    }
+
+    /**
+     * Convenience constructor without sub goals.
+     */
+    public SequenceGoal(String n) {
+       this( n, null );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a SequenceGoal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new SequenceInstance( head );
+    }
+
+    /**
+     * Implements sequential goal execution.
+     */
+    public class SequenceInstance extends Instance {
+
+       /**
+        * Constructor.
+        */
+       public SequenceInstance(String h) {
+           super( h );
+       }
+
+       /**
+        * Keeps the currently ongoing subgoal instance
+        */
+       public Instance ongoing = null;
+
+       /**
+        * Index of the currently ongoing sub goal.
+        */
+       public int index = 0;
+
+       /**
+        * Cancels execution.
+        */
+       public void cancel() {
+           super.cancel();
+           if ( ongoing != null )
+               ongoing.cancel();
+       }
+
+       /**
+        * Instantiates and performs sub goals in sequence.
+        */
+       public States action(String head,Data d)
+           throws LoopEndException, ParallelEndException {
+           Goal [] subgoals = getGoalSubgoals();
+           if ( subgoals == null )
+               return super.action( head, d );
+           while ( index < subgoals.length ) {
+               if ( ongoing == null ) {
+                   ongoing = subgoals[ index ].instantiate(
+                       head + "." + index, d );
+               }
+               States s = ongoing.perform( d );
+               if ( s != States.PASSED )
+                   return s;
+               index += 1;
+               ongoing = null;
+           }
+           return States.PASSED;
+       }
+    }
+
+}
diff --git a/com/intendico/gorite/Team.java b/com/intendico/gorite/Team.java
new file mode 100644 (file)
index 0000000..a5c86e7
--- /dev/null
@@ -0,0 +1,500 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+import com.intendico.data.Relation;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * The Team class is a base class for a structured task performer,
+ * i.e. a {@link Performer} that combine sub performers. A particular
+ * team type is defined by extending the Team class. The structure of
+ * an instance of a team type is defined by calls to the {@link
+ * #addPerformer} method. This provides a lookup context for inner
+ * {@link TaskTeam} objects, which are used to define particular sub
+ * team constallations for particular team tasks.
+ */
+
+public class Team extends Performer {
+
+    /**
+     * Default constructor.
+     */
+    public Team() {
+    }
+
+    /**
+     * Constructor for team of given name.
+     */
+    public Team(String name)
+    {
+       super( name );
+    }
+
+    /**
+     * This is the current role filling for this team -- it's
+     * obligation structure.
+     */
+    public Vector/*<Performer>*/ performers = new Vector/*<Performer>*/();
+
+    /**
+     * This is a table of TaskTeam objects, to support longer term
+     * constallations.
+     */
+    public Hashtable/*<String,TaskTeam>*/ groupings =
+        new Hashtable/*<String,TaskTeam>*/();
+
+    /**
+     * Adds a particular performer in a named role filling.
+     */
+    synchronized public void addPerformer(Performer performer)
+    {
+       performers.add( performer );
+       for ( Iterator/*<TaskTeam>*/ ti = groupings.values().iterator();
+             ti.hasNext(); ) {
+           ((TaskTeam) ti.next()).updateFillers( performer );
+       }
+    }
+
+    /**
+     * Adds multiple, named performers.
+     */
+    public void addPerformers(Performer [] p)
+       throws Exception
+    {
+       if ( p != null ) {
+           for ( int i = 0; i < p.length; i++ )
+               addPerformer( p[i] );
+       }
+    }
+
+    /**
+     * Utility method to pick a performer. Return null when the index
+     * is out of bounds.
+     */
+    synchronized public Performer getPerformer(int i) {
+       if ( i < 0 || i >= performers.size() )
+           return null;
+       return (Performer) performers.get( i );
+    }
+
+    /**
+     * Utility method to pick a performer by name. Returns the first
+     * Performer of this team with the given name, or null.
+     */
+    public Performer getPerformer(String name) {
+       for ( Iterator/*<Performer>*/ i =
+                 performers.iterator(); i.hasNext(); ) {
+           Performer p = (Performer) i.next();
+           if ( name.equals( p.name ) ) {
+               return p;
+           }
+       }
+       return null;
+    }
+
+     /**
+      * Utility method to remove a performer by name. Removes and
+      * returns the first Performer with the given name, or null;
+      */
+     public Performer removePerformer(String name) {
+        for ( Iterator/*<Performer>*/ i =
+                  performers.iterator(); i.hasNext(); ) {
+            Performer p = (Performer) i.next();
+            if ( name.equals( p.getName() ) ) {
+                i.remove();
+                return p;
+            }
+        }
+        return null;
+     }
+
+    /**
+     * Utility method to access a long term TaskTeam by name.
+     */
+    public TaskTeam getTaskTeam(String name) {
+       return (TaskTeam) groupings.get( name );
+    }
+
+    /**
+     * Utility method to define a long term TaskTeam by unique name.
+     */
+    public void setTaskTeam(String name,TaskTeam group) {
+       groupings.put( name, group );
+       group.updateFillers( performers );
+    }
+
+    /**
+     * A TaskTeam represents a sub-grouping of a team's performers
+     * into roles as appropriate for particular team activities. Each
+     * TaskTeam is populated to hold a selection of performers that
+     * are nominated for the task team roles according to their
+     * abilities. This role filling defines the candidates for acting
+     * in the roles when the TaskTeam is "established" in a particular
+     * intention.
+     *
+     * <p> The role filling in a TaskTeam as well as the established
+     * role filling in an intention may be changed at any time. When
+     * the TaskTeam is installed for a {@link Team}, the current
+     * {@link #performers} collection is revide and performers are
+     * nominated to roles as appropriate. Thereafter each subsequently
+     * added {@link Performer} is automatically considered, and
+     * nominated to roles of installed TaskTeams as appropriate. The
+     * detail nomination decision is made by the overridable {@link
+     * Role#canFill(Performer)} method, which by default ensures
+     * distinct, singular role filling, and that the nominated
+     * {@link Performer} has plans for all required goals.
+     *
+     * <p> The TaskTeam is deployed on demand for intentions,
+     * typically by by means of {@link #deploy(String)} goal in team
+     * plans. When deployed in this way, the TaskTeam role filling is
+     * established in the intention {@link Data} via {@link
+     * #establish(Data)}, with a further filtering through the
+     * overridable {@link Role#canAct(Data,Performer)} method, which
+     * by default is "true".
+     */
+    public class TaskTeam {
+
+       /**
+        * The roles of the task team.
+        */
+       public Vector/*<Role>*/ roles = new Vector/*<Role>*/();
+
+       /**
+        * The population of this task team, held as a {@link
+        * Relation} beteen roles and performers.
+        */
+       public Relation fillers = new Relation( "fillers", 2 );
+
+       /**
+        * Utility method to add a role to the task team.
+        */
+       public void addRole(Role r) {
+           r.group = this;
+           roles.add( r );
+       }
+
+       /**
+        * Utility method to remove a role from the task team. This
+        * also removes any population for the role.
+        */
+       public void removeRole(Role r) {
+           r.group = this;
+           roles.remove( r );
+           filling( r, new Ref( "$p" ) ).remove();
+       }
+
+       /**
+        * Utility method to return a role filling {@link Query}
+        * object for a given role and performer, either of which is
+        * given as a constant of its type, null, or a {@link Ref}
+        * object, which may be (appropriately) bound or unbound.
+        */
+       public Query filling(Object r,Object p) {
+           try {
+               return fillers.get( new Object [] { r, p } );
+           } catch (Exception e) {
+               e.printStackTrace();
+               return null;
+           }
+       }
+
+       /**
+        * Utility method to define a role filler for a role.
+        */
+       public void fillRole(Role r,Performer p) {
+           filling( r, p ).add();
+       }
+
+       /**
+        * Utility method to clear all filling.
+        */
+       public void clearFilling() {
+           filling( null, null ).remove();
+       }
+
+       /**
+        * Updates the role nominations for the given {@link Performer
+        * performers}. This method simply iterates over the
+        * collection and updates each performer one by one via {@link
+        * #updateFillers(Performer)}.
+        */
+       public void updateFillers(Vector/*<Performer>*/ performers) {
+           for ( Iterator/*<Performer>*/ pi = performers.iterator();
+                 pi.hasNext(); ) {
+               updateFillers( (Performer) pi.next() );
+           }
+       }
+
+       /**
+        * Updates the role nominations for the given {@link
+        * Performer} as appropriate for roles in this taks team.
+        * Which roles it is nominated to, if any, is determined by
+        * {@link Role#canFill(Performer)}.
+        */
+       public void updateFillers(Performer p) {
+           for ( Iterator/*<Role>*/ ri = roles.iterator(); ri.hasNext(); ) {
+               Role r = (Role) ri.next();
+               Query q = filling( r, p );
+               if ( r.canFill( p ) ) {
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "** can Fill: " + r.name + " " + p );
+                   }
+                   q.add();
+               } else {
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "** cannot Fill: " + r.name + " " + p );
+                   }
+                   q.remove();
+               }
+           }
+       }
+
+       /**
+        * Utility method to establish the role filling in the given
+        * intention data. This will establish the roles one by one in
+        * declaration order, using the current fillers as filtered by
+        * the {@link Role#canAct} method.
+        *
+        * <p> Note that role filling is set up as data elements using
+        * the role names. The value for a role name is the collection
+        * of fillers that can act in the role, qua {@link
+        * Capability}, but actually represented by {@link
+        * Performer.RoleFilling} objects as returned by the {@link
+        * Performer#fillRole(Team.Role)} method.
+        * @return true if all roles have some filling
+        */
+       public boolean establish(Data d) {
+           if ( Goal.isTracing() ) {
+               System.err.println( "** establish task team" );
+           }
+           for ( Iterator/*<Role>*/ ri = roles.iterator();
+                 ri.hasNext(); ) {
+               Role role = (Role) ri.next();
+               Ref $p = new Ref( "$p" );
+               Query q = filling( role, $p );
+               d.forget( role.name );
+               boolean filled = false;
+               HashSet actors = Team.getActors( role.name, d );
+               try {
+                   while ( q.next() ) {
+                       Performer candidate = (Performer) $p.get();
+                       if ( actors.contains( candidate ) ) {
+                           if ( Goal.isTracing() ) {
+                               System.err.println(
+                                   "** already Acting: " + role.name +
+                                   " " + candidate );
+                           }
+                       } else if ( role.canAct( d, candidate ) ) {
+                           if ( Goal.isTracing() ) {
+                               System.err.println(
+                                   "** can Act: " + role.name +
+                                   " " + candidate );
+                           }
+                           Capability c = candidate.fillRole( role );
+                           if ( c != null ) {
+                               d.setValue( role.name, c );
+                               filled = true;
+                           } else if ( Goal.isTracing() ) {
+                               System.err.println(
+                                   "** refuses to Act: " + role.name +
+                                   " " + candidate );
+                           }
+                       } else if ( Goal.isTracing() ) {
+                           System.err.println(
+                               "** cannot Act: " + role.name +
+                               " " + candidate );
+                       }
+                   }
+               } catch (Exception e) {
+                   e.printStackTrace();
+               }
+               if ( ! filled ) {
+                   if ( Goal.isTracing() ) {
+                       System.err.println(
+                           "** Role not established: " + role.name );
+                   }
+                   return false; // couldn't establish the role at
+                                 // all
+               }
+           }
+           return true;
+       }
+
+       /**
+        * Utility method that creates a {@link Goal} of establishing
+        * this task team in the intention data when the goal is to be
+        * achieved.
+        * @see #establish(Data)
+        */
+       public Goal deploy(String name) {
+           return new Goal( "deploy " + name ) {
+                   public States execute(Data d) {
+                       return establish( d )? States.PASSED : States.FAILED;
+                   }
+               };
+       }
+
+       /**
+        * Utility method to collate all actors of this TaskTeam's
+        * roles in the given Data.
+        * @return a Hashtable of {@link Performer} HashSets, keyed by
+        * the {@link Role} names of the TaskTeam's roles.
+        */
+       public Hashtable/*<String,HashSet<Performer>>*/ getActors(Data d) {
+           Hashtable result = new Hashtable();
+           for ( Iterator/*<Role>*/ ri = roles.iterator(); ri.hasNext(); ) {
+               Role r = (Role) ri.next();
+               HashSet/*<Performer>*/ actors = Team.getActors( r.name, d );
+               if ( actors != null )
+                   result.put( r.name, actors );
+           }
+           return result;
+       }
+
+       /**
+        * Utility method to return the enclosing team wherein this
+        * TaskTeam object is created.
+        */
+       public Team team() {
+           return Team.this;
+       }
+    }
+
+    /**
+     * Utility method to collate the performers defined to act in the
+     * given role in a given Data.
+     * @see TaskTeam#getActors(Data)
+     */
+    static public HashSet/*<Performer>*/ getActors(String r,Data d) {
+       HashSet/*<Performer>*/ result = new HashSet/*<Performer>*/();
+       Data.Element e = d.find( r );
+       if ( e != null ) {
+           for ( Iterator/*<Object>*/ vi = e.values.iterator();
+                 vi.hasNext(); ) {
+               result.add( ((Capability) vi.next()).getPerformer() );
+           }
+       }
+       return result;
+    }
+
+    /**
+     * The Role class is a base class for representing team members in
+     * a team. Role extends {@link Capability}, so as to hold {@link
+     * Goal} methods that are tied to the role within the context of a
+     * {@link Team}. It further contains an attribute {@link
+     * #required}, which indicate goals that are required by a {@link
+     * Performer} of the role.
+     */
+    public class Role extends Capability {
+
+       /**
+        * The reference name for this role.
+        */
+       public String name;
+
+       /**
+        * The goal names required by a role filler.
+        */
+       public String [] required;
+
+       /**
+        * The {@link TaskTeam} that this role is a role of. This is
+        * set by the {@link TaskTeam#addRole} method when the role is
+        * added.
+        */
+       public TaskTeam group;
+
+       /**
+        * Constructor that takes an array of required goals.
+        */
+       public Role(String n,String [] r) {
+           name = n;
+           required = r;
+           setPerformer( Team.this );
+       }
+
+       /**
+        * Overrides {@link Capability#addCapability} so as to link up
+        * added capabilities with this team qua performer.
+        */
+       public void addCapability(Capability c) {
+           c.setPerformer( Team.this );
+           super.addCapability( c );
+       }
+
+       /**
+        * Returns the team of the role.
+        */
+       public Team team() {
+           return Team.this;
+       }
+
+       /**
+        * Overridable method that determines if the given performer
+        * can fill this role (within its {@link TaskTeam} {@link
+        * #group}). The default decision implemented by this
+        * method ensures that: <ol>
+        *
+        * <li> the performer is not already filling another role,
+        *
+        * <li> the role is not already filled by another performer,
+        * and
+        *
+        * <li> the performer has some plans to achieve the required
+        * goals.
+        * 
+        * </ol>
+        */
+       public boolean canFill(Performer p) {
+           try {
+               if ( group.filling( null, p ).next() )
+                   return false;
+               if ( group.filling( this, null ).next() )
+                   return false;
+               return p.hasGoals( required );
+           } catch (Exception e) {
+               e.printStackTrace();
+               return false;
+           }
+       }
+
+       /**
+        * Overridable method that determines whether the given {@link
+        * Performer} can act in this role within the {@link
+        * Goal.Instance intention} represented by the given {@link
+        * Data}. The default decision is "yes".
+        * @see Team#getActors(String,Data)
+        */
+       public boolean canAct(Data d,Performer p) {
+           return true;
+       }
+
+    }
+
+}
diff --git a/com/intendico/gorite/TeamGoal.java b/com/intendico/gorite/TeamGoal.java
new file mode 100644 (file)
index 0000000..25155ac
--- /dev/null
@@ -0,0 +1,118 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A TeamGoal is a {@link Goal} that is achieved as a {@link BDIGoal}
+ * by a given {@link Team.Role}. The TeamGoal looks up {@link
+ * Team.Role} fillers in the {@link Data} under the given role name,
+ * and presents itself as a {@link BDIGoal} to be performed by the
+ * fillers.
+ *
+ * <p> Note that the TeamGoal distributes like a {@link RepeatGoal}
+ * over the role name data, requesting all fillers to achieve the goal
+ * in parallel. The TeamGoal succeeds when all the fillers succeed,
+ * and fails if any role filler fails. In the latter case, the
+ * branches for the fillers in progress are cancelled.
+ *
+ * <p> If the TeamGoal is implemented by a {@link Team.Role} plan that
+ *
+ * @see Team
+ * @see Team.TaskTeam
+ * @see Performer#fillRole
+ * @see ControlGoal
+ */
+public class TeamGoal extends BranchingGoal {
+
+    /**
+     * Constructor for TeamGoal.
+     */
+    public TeamGoal(String role,String n) {
+       super( n, null );
+       setGoalControl( role );
+    }
+
+    /**
+     * Cache of construction object (when not a String).
+     */
+    public Object specific;
+
+    /**
+     * Constructor using an object other than String. Then the class
+     * name of the object is used as goal name, and the object is held
+     * as {@link #specific}. However, if the given object is a {@link
+     * String}, then its value (rather than its type) is used as goal
+     * name anyhow.
+     * @see BDIGoal#BDIGoal(Object)
+     */
+    public TeamGoal(String role,Object n) {
+       super( ( n instanceof String )? (String) n : n.getClass().getName() );
+       specific = n;
+       setGoalControl( role );
+    }
+
+    /**
+     * Make an Instance for executing this goal.
+     */
+    public Instance instantiate(String head,Data d) {
+       return new TeamInstance( head );
+    }
+
+    /**
+     * Creates and returns an instance object for achieving
+     * a TeamGoal.
+     */
+    public class TeamInstance extends MultiInstance {
+
+       /**
+        * Constructor.
+        */
+        public TeamInstance(String h) {
+            super( h );
+        }
+
+       /**
+        * To decide whether yet another branch should be made.
+        */
+        public boolean more(int i,Data d) {
+           return i < Math.max( 1, d.size( getGoalControl() ) );
+        }
+
+       /**
+        * The branch is created be instantiating a {@link BDIGoal}.
+        */
+        public Instance getBranch(int i,String head,Data d) {
+           Goal g = specific != null?
+               new BDIGoal( specific ) : new BDIGoal( getGoalName() );
+           g.setGoalGroup( getGoalGroup() );
+           g.setGoalControl( getGoalControl() );
+            return g.instantiate( head, d );
+        }
+
+    }
+
+}
diff --git a/com/intendico/gorite/TransferGoal.java b/com/intendico/gorite/TransferGoal.java
new file mode 100644 (file)
index 0000000..a014239
--- /dev/null
@@ -0,0 +1,198 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite;
+
+import com.intendico.data.Query;
+
+/**
+ * The TransferGoal class is utility class for wrapping a goal that is
+ * transferred into a different target performer context.
+ */
+public class TransferGoal extends Plan {
+
+    /**
+     * Holds the target performer.
+     */
+    public Performer target;
+
+    /**
+     * Holds the transferred goal.
+     */
+    public Goal goal;
+
+    /**
+     * Constructor.
+     */
+    public TransferGoal(Performer to,Goal g) {
+       super( g.getGoalName() );
+       target = to;
+       goal = g;
+    }
+
+    /**
+     * Constructor without Performer, which instead is looked up
+     * at instantiation using the control string.
+     */
+    public TransferGoal(Goal g) {
+       super( g.getGoalName() );
+       target = null;
+       goal = g;
+    }
+
+    /**
+     * Overrides the base class method {@link Goal#instantiate} to
+     * provide a wrapper instance for the transferred goal.
+     */
+    public Instance instantiate(String h,Data d) {
+       return new TransferInstance( h, d );
+    }
+
+    /**
+     * Transfer goals are equals by the goals they transfer.
+     */
+    public boolean equals(Object x) {
+       return x instanceof TransferGoal && equals( (TransferGoal) x );
+    }
+
+    /**
+     * Transfer goals are equals by the goals they transfer.
+     */
+    public boolean equals(TransferGoal x) {
+       return goal.equals( x.goal );
+    }
+
+    /**
+     * Wrapper class for transfer goal instances.
+     */
+    public class TransferInstance extends Instance {
+
+       /**
+        * Holds the instance of the transferred goal.
+        */
+       public Instance instance;
+
+       public Performer into;
+       /**
+        * Constructor. Constructs an instance of the transferred
+        * goal while temporarily shifting into the target
+        * performer. 
+        */
+       public TransferInstance(String h,Data d) {
+           super( h );
+           instance = goal.instantiate( h + "|", d );
+       }
+
+       /**
+        * Control callback whereby the intention gets notified that
+        * it is cancelled. This will forward the cancellation to the
+        * currently progressing instance, if any.
+        */
+       public void cancel() {
+           if ( instance != null )
+               instance.cancel();
+       }
+
+       /**
+        * Implements the instance action, which sets the target
+        * performer as {@link Goal#PERFORMER}, then invokes the
+        * target instance, and thereafter restores the performer.
+        */
+       public States action(String h,Data d)
+           throws LoopEndException, ParallelEndException {
+           Performer from = (Performer) d.getValue( PERFORMER );
+           try {
+               if ( target == null ) {
+                   into = (Performer) d.getValue( getGoalControl() );
+               } else {
+                   into = target;
+               }
+               // Note the use of setValue() rather than
+               // replaceValue() below. This is done to ensure
+               // that PERFORMER is set in the local data
+               // context, without replacing PERFORMER in the
+               // parent context.
+               //
+               // Also, the new PERFORMER is pushed on top of
+               // any existing value, showing the chain of
+               // performers involved in the computation.  Unless
+               // the data context is in "goldfish" mode of
+               // course.
+               d.setValue( PERFORMER, into );
+               if ( isTracing() )
+                   System.err.println( ">> " + into );
+               return instance.perform( d );
+           } finally {
+               // Note that restoreValue only affects the local
+               // data context.
+               d.restoreValue( PERFORMER, from );
+               if ( isTracing() )
+                   System.err.println( "<< " + into );
+           }
+       }
+    }
+
+    /**
+     * Implements the {@link Context#context} method by forwarding to
+     * the wrapped goal.
+     */
+    public Query context(Data d) throws Exception {
+       if ( goal instanceof Context ) {
+           Capability from = (Capability) d.getValue( PERFORMER );
+           try {
+               if ( isTracing() ) {
+                   System.err.println( "~(context)> (" + target + ")" );
+               }
+               d.setValue( PERFORMER, target );
+               return ((Context) goal).context( d ) ;
+           } finally {
+               if ( isTracing() ) {
+                   System.err.println( "<(context)~ (" + target + ")" );
+               }
+               d.restoreValue( PERFORMER, from );
+           }
+       } else {
+           return null ;
+       }
+    }
+
+    /**
+     * Implements the {@link Precedence#precedence} method by
+     * forwarding to the wrapped goal.
+     */
+    public int precedence(Data d) {
+       if ( goal instanceof Precedence ) {
+           Capability from = (Capability) d.getValue( PERFORMER );
+           try {
+               if ( isTracing() ) {
+                   System.err.println( "~(precedence)> (" + target + ")" );
+               }
+               d.setValue( PERFORMER, target );
+               return ((Precedence) goal).precedence( d );
+           } finally {
+               if ( isTracing() ) {
+                   System.err.println( "<(precedence)~ (" + target + ")" );
+               }
+               d.replaceValue( PERFORMER, from );
+           }
+       } else {
+           return DEFAULT_PRECEDENCE ;
+       }
+    }
+}
diff --git a/com/intendico/gorite/addon/.cvsignore b/com/intendico/gorite/addon/.cvsignore
new file mode 100644 (file)
index 0000000..6b468b6
--- /dev/null
@@ -0,0 +1 @@
+*.class
diff --git a/com/intendico/gorite/addon/BellBoy.java b/com/intendico/gorite/addon/BellBoy.java
new file mode 100644 (file)
index 0000000..4fcb45d
--- /dev/null
@@ -0,0 +1,242 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+import java.util.Hashtable;
+import java.util.Set;
+
+/**
+ * This is a {@link Capability} (type) that provides a simple
+ * inter-model messaging service within a single JVM. It uses the
+ * {@link Performer#getName()} method of the containing {@link
+ * Performer} as messaging end point identity. The idea is that all
+ * communicating performers are set up to have their own instance of
+ * this capability type. The following is an example of use, with a
+ * receive plan outline:
+ *
+ * <pre>
+ * new Performer( "morgan" ) {{
+ *     addCapability( new BellBoy() );
+ *     addPlan( new Plan( BellBoy.RECV, new Goal [] {
+ *         .... deal with received message in "percept"
+ *     } );
+ * }};
+ * </pre>
+ *
+ * <p><i>Sending Messages</i>
+ *
+ * <p> The capability handles a {@link #SEND "Send BellBoy message"}
+ * {@link Goal goal}, with {@link #MSG "bellboy message"} holding a
+ * {@link BellBoy.Envelope message envelope}, by delivering that
+ * message to the {@link Envelope#getWhom() indicated recipient}.
+ * 
+ * <p> Sending can also be done directly in Java code by using the
+ * static {@link BellBoy#send(Envelope)} or {@link
+ * BellBoy#send(String,Object)} methods.
+ *
+ * <p><i>Receiving Messages</i>
+ *
+ * <p> Upon receiving a message, the capability triggers a {@link
+ * #RECV "Got BellBoy message"} {@link BDIGoal} goal, on the {@link
+ * #TODO "bellboy"} {@link com.intendico.gorite.Performer.TodoGroup
+ * TodoGroup} and with {@link Perceptor#data_name "percept"} holding
+ * the message,
+ *
+ * <p> Reception can also be triggered in Java code by using the
+ * {@link #receive(Object)} method.
+ *
+ * <p> <b>NOTE</b>
+ *
+ * <p>Messages are delivered through in-core sharing, and they are not
+ * copied! <b>Not copied!</b> This means that the sender and receiver
+ * end up sharing the one message object. In proper use, the sender
+ * should release all its handles to the message object after having
+ * sent it. The capability supports this by {@link
+ * Data#forget(String) forgetting} {@link #MSG "bellboy message"}
+ * after having sent the message.
+ */
+public class BellBoy extends Capability {
+
+    /**
+     * The interface for standard BellBoy messages.
+     */
+    public interface Envelope {
+       /**
+        * Returns message recipient identity.
+        */
+       public String getWhom();
+
+       /**
+        * Returns the message payload, i.e., the "actual" message.
+        */
+       public Object getPayload();
+    }
+
+    /**
+     * Convenience constant for the send goal.
+     */
+    public final static String SEND = "Send BellBoy message";
+
+    /**
+     * Convenience constant for the receive goal.
+     */
+    public final static String RECV = "Got BellBoy message";
+
+    /**
+     * Convenience constant for the send goal data element.
+     */
+    public final static String MSG = "bellboy message";
+
+    /**
+     * Convenience constant for BellBoy messaging {@link
+     * com.intendico.gorite.Performer.TodoGroup TodoGroup}.
+     */
+    public final static String TODO = "bellboy";
+
+    /**
+     * The table of communicating BellBoy instances within this JVM.
+     */
+    public static Hashtable/*<String,BellBoy>*/ club =
+       new Hashtable/*<String,BellBoy>*/();
+
+    /**
+     * The {@link Perceptor} for receiving BellBoy messages. This is
+     * set up by the {@link #initialize()}, which is invoked
+     * automatically by GORITE. The installed {@link Perceptor}
+     * triggers {@link #RECV "Got BellBoy message"} goals on the
+     * {@link #TODO "bellboy"} {@link
+     * com.intendico.gorite.Performer.TodoGroup TodoGroup} with the
+     * received messages as the {@link Perceptor#data_name "percept"}
+     * data.
+     */
+    public Perceptor recv;
+
+    /**
+     * Binds a BellBoy to be a {@link #club} member under a given
+     * name. This replaces any previous binding, and binding
+     * <tt>null</tt> removes the previous binding.
+     */
+    public static void bind(String name,BellBoy b) {
+       if ( b == null ) {
+           synchronized ( club ) {
+               club.remove( name );
+           }
+       } else {
+           synchronized ( club ) {
+               club.put( name, b );
+           }
+       }
+    }
+
+    /**
+     * Returns the BellBoy bound to a given name.
+     */
+    public static BellBoy find(String name) {
+       synchronized ( club ) {
+            return (BellBoy) club.get( name );
+       }
+    }
+
+    /**
+     * Returns the names of the current {@link BellBoy#club} members.
+     */
+    public static Set/*<String>*/ getMembers() {
+       synchronized ( club ) {
+           return club.keySet();
+       }
+    }
+
+    /**
+     * Creates a standard BellBoy {@link Envelope}.
+     */
+    public static Envelope pack(final String whom,final Object what) {
+       return new Envelope() {
+           public String getWhom() {
+               return whom;
+           }
+           public Object getPayload() {
+               return what;
+           }
+       };
+    }
+
+    /**
+     * Receives a message. This causes a percept on the {@link #recv}
+     * {@link Perceptor}. The method returns false if {@link #recv} is
+     * unassigned, otherwise it returns true.
+     */
+    public boolean receive(Object m) {
+       if ( recv == null )
+           return false;
+       recv.perceive( m );
+       return true;
+    }
+
+    /**
+     * Sends a named recipient a message.
+     */
+    public static boolean send(String to,Object message) {
+       BellBoy whom = find( to );
+       return ( whom == null )? false : whom.receive( message );
+    }
+
+    /**
+     * Sends an envelope.
+     */
+    public static boolean send(Envelope m) {
+       return ( m == null )? false : send( m.getWhom(), m.getPayload() );
+    }
+
+    /**
+     * Initializes the capability with the abilities to send and
+     * receive messages, and then it joins the BellBoy club.
+     *
+     * <p> The capability is set up to handle a {@link #SEND "Send
+     * BellBoy message"} {@link Goal goal}, with {@link #MSG "bellboy
+     * message"} holding a {@link BellBoy.Envelope message envelope},
+     * by delivering that message to the {@link Envelope#getWhom()
+     * indicated recipient}.
+     *
+     * The goal fails if {@link #MSG "bellboy message"} is null. If
+     * it's non-null but not a {@link BellBoy.Envelope}, then a {@link
+     * ClassCastException} is thrown.
+     *
+     * The goal also fails if the {@link BellBoy#club} doesn't include
+     * the desired recipient.
+     */
+    public void initialize() {
+       // Prepare to receive messages
+       recv = new Perceptor( getPerformer(), RECV, TODO );
+       // Define ability to send message as goal
+       addGoal( new Goal( SEND ) {
+           public States execute(Data data) {
+               if ( send( (Envelope) data.getValue( MSG ) ) ) {
+                   data.forget( MSG );
+                   return States.PASSED;
+               }
+               return States.FAILED;
+           }
+       } );
+       // Join the club
+       bind( getPerformer().getName(), this );
+    }
+
+}
diff --git a/com/intendico/gorite/addon/Handshake.java b/com/intendico/gorite/addon/Handshake.java
new file mode 100644 (file)
index 0000000..eeea2fb
--- /dev/null
@@ -0,0 +1,101 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.Goal;
+import com.intendico.gorite.Data;
+import java.util.Observable;
+
+/**
+ * Utility class to deal with request-response protocols between
+ * in-core performers with BellBoy capability.  The requester would
+ * include a {@link Handshake#create()} step in the requesting plan,
+ * with data elements "responder" and "request" set up, and the
+ * responder, who gets a Handshake object as BellBoy percept, uses the
+ * {@link Handshake#reply(Object)} method to issue their reply.
+ *
+ * <p> Note: a null response is not a response, but will make the
+ * requester keep waiting.
+ */
+public class Handshake extends Observable {
+
+    /**
+     * The request message of this Handshake.
+     */
+    public Object message;
+
+    /**
+     * The response message of this Handshake.
+     */
+    public Object response;
+
+    /**
+     * Java method to set the reply and notify to the requester
+     * intention.
+     */
+    public void reply(Object r) {
+        response = r;
+        setChanged();
+        notifyObservers();
+    }
+
+    /**
+     * Returns a Goal object for performing a Handshake. It uses the
+     * data elements "responder" and "request" for input details, the
+     * data element "pending handshake" transiently, and the data
+     * element "response" for output, i.e. the reply object.
+     *
+     * Note that all incoming "pending handshake" values are
+     * forgotten, but not upon a completed handshake.
+     */
+    public static Goal create() {
+       return new Goal( "Perform handshake" ) {
+           /**
+            * Issue the request when the goal is instantiated.
+            */
+           public Instance instantiate(String head,Data data) {
+               String who = (String) data.getValue( "responder" );
+               Handshake h = new Handshake();
+               h.message = data.getValue( "request" );
+               data.forget( "pending handshake" );
+               if ( BellBoy.send( who, h ) ) {
+                   data.setValue( "pending handshake", h );
+               }
+               return super.instantiate( head, data );
+           }
+
+           /**
+            * Keep blocking until there is a non-null reply
+            */
+           public States execute(Data data) {
+               Handshake h = (Handshake) data.getValue( "pending handshake");
+               if ( h == null ) {
+                   return States.FAILED;
+               }
+               if ( h.response == null ) {
+                   data.setTrigger( h );
+                   return States.BLOCKED;
+               }
+               data.setValue( "response", h.response );
+               return States.PASSED;
+           }
+       };
+    }
+}
diff --git a/com/intendico/gorite/addon/Perceptor.java b/com/intendico/gorite/addon/Perceptor.java
new file mode 100644 (file)
index 0000000..76e0a8d
--- /dev/null
@@ -0,0 +1,199 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+
+/**
+ * This is a class for implementing a performer percept channel, where
+ * each percept triggers an intention for handling it, which gets
+ * added to a {@link com.intendico.gorite.Performer.TodoGroup} for
+ * eventual processing.
+ *
+ * <p> The Perceptor class is made separate from Performer so as to
+ * allow multiple, different perceptors and to simplify its use at the
+ * perception source end.
+ *
+ * <p> A "pure" event driven system would be run via the {@link
+ * Executor#run} method (rather than using a top level {@link
+ * Performer#performGoal(Goal,String,Data)} call), and have Perceptor
+ * objects to cause executions.
+ */
+public class Perceptor {
+
+    /**
+     * Keeps track of which {@link Performer} this is a perceptor
+     * for. This is given as constructor argument, and it typically
+     * remains unchanged. Though there is no harm in changing it and
+     * thereby move the Perceptor to another Performer.
+     */
+    public Performer performer;
+
+    /**
+     * Keeps track of which {@link Goal} hierarchy to use for handling
+     * a percept. It must not be null, and is given as optional
+     * constructor argment. The default goal hierarchy is
+     *
+     * <pre>
+     *     new BDIGoal( "handle percept" )
+     * </pre>
+     *
+     * which thus allows the actual goal hierarchy be defined among
+     * the performers capabilities, with possibly many alternative
+     * handler plans depending on the kinds of percepts.
+     */
+    public Goal handler;
+
+    /**
+     * Keeps track of which {@link com.intendico.gorite.Data.Element}
+     * name to use for the percept object. The default name is
+     * "percept".
+     */
+    public String data_name = "percept";
+
+    /**
+     * Keeps track of which {@link
+     * com.intendico.gorite.Performer.TodoGroup} to use for the
+     * percept handling intentions. The default group is "percepts".
+     */
+    public String todo_group = "percepts";
+
+    /**
+     * Constructor with performer, goal and todo group name.
+     */
+    public Perceptor(Performer p,Goal g,String todo)
+    {
+       performer = p;
+       handler = g;
+       todo_group = todo;
+       g.setGoalGroup( todo );
+    }
+
+    /**
+     * Constructor with performer and goal, using default todo group.
+     */
+    public Perceptor(Performer p,Goal g)
+    {
+       performer = p;
+       handler = g;
+       g.setGoalGroup( todo_group );
+    }
+
+    /**
+     * Alternative constructor with goal name and todo group name.
+     */
+    public Perceptor(Performer p,String goal,String todo) {
+       this( p, new BDIGoal( goal ), todo );
+    }
+
+    /**
+     * Alternative constructor with performer and name of a BDI goal
+     * to be created here.
+     */
+    public Perceptor(Performer p,String goal)
+    {
+       this( p, new BDIGoal( goal ) );
+    }
+
+    /**
+     * Alternative constructor with performer only, and using the
+     * default percept handling goal <tt>"handle percept"</tt> and
+     * default todo group <tt>"percepts"</tt>.
+     */
+    public Perceptor(Performer p)
+    {
+       this( p, "handle percept" );
+    }
+
+    /**
+     * Utility method to reset the perceptor set up. The new settings
+     * apply for subsequent perceive calls.
+     */
+    public void setup(String goal,String data,String todo) {
+       handler = new BDIGoal( goal );
+       handler.setGoalGroup( todo );
+       data_name = data;
+       todo_group = todo;
+    }
+
+    /**
+     * The method to use for making the performer perceive
+     * something. The given object is set up as value of a named data
+     * element, and percept handling goal is instantiated for handling
+     * it.
+     */
+    public void perceive(Object percept)
+    {
+       perceive( percept, new Data(), handler );
+    }
+
+    /**
+     * Alternative perception method with a given a Data object.
+     */
+    public void perceive(Object percept,Data data) {
+       perceive( percept, data, handler );
+    }
+
+    /**
+     * Alternative perception method with a given a Data object and
+     * special perception goal name.
+     */
+    public void perceive(Object percept,Data data,String goal) {
+       perceive( percept, data, goal != null? new BDIGoal( goal ) : handler );
+    }
+
+    /**
+     * Alternative perception method with a given a Data object and
+     * special perception goal name.
+     */
+    public void perceive(Object percept,Data data,Goal goal) {
+
+       if ( ! "X".equals( data.thread_name ) ) {
+           // This indicates that the Data object is already used or
+           // in use, which can cause all sorts of funny things
+           throw new Error( "Schizogenic Data object!!" );
+       }
+
+       data.setValue( data_name, percept );
+       data.setValue( Goal.PERFORMER, performer );
+       
+       if ( goal == null )
+           goal = handler;
+       Goal.Instance gi = goal.instantiate( todo_group, data );
+
+       // The following set up is essential to make it appear that
+       // the goal instace has been performed. (c.f {@link
+       // Goal.Instance#perform})
+       gi.data = data;
+       gi.performer = performer;
+       data.link( gi.thread_name );
+
+       //System.err.println( "Data = " + data );
+       //System.err.println( "Data:" + data_name + " = " + percept );
+
+       try {
+           performer.execute( false, gi, todo_group);
+       } catch (Exception e) {
+           // Not good :-(
+           e.printStackTrace();
+       }
+       performer.signalExecutor();
+    }
+}
diff --git a/com/intendico/gorite/addon/Reflector.java b/com/intendico/gorite/addon/Reflector.java
new file mode 100644 (file)
index 0000000..f84d3d7
--- /dev/null
@@ -0,0 +1,259 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.Performer;
+import com.intendico.gorite.Data;
+import com.intendico.gorite.Goal;
+import com.intendico.gorite.BDIGoal;
+import com.intendico.data.Query;
+import com.intendico.data.Ref;
+import com.intendico.data.Snapshot;
+
+import java.util.IdentityHashMap;
+import java.util.Observer;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.Observable;
+
+/**
+ * A Reflector is a reasoning faculty to translate situation changes
+ * into percepts. The Reflector is populated with some number of
+ * {@link Reflection} objects, which each combines a {@link Query}
+ * with a {@link Goal}, so as to intend the {@link Goal} as a percept
+ * when the {@link Query} changes state, from false to true or from
+ * true to false, for particular bindings.
+ *
+ * <p> For example, consider the following code snippet:
+ * <pre>
+ *     new Reflector( performer, "grandparent", "inference" ) {{
+ *         Ref p = new Ref( "parent" );
+ *         addQuery( new And( parent.get( new Ref( "gp"), p ),
+ *                            parent.get( p, new Ref( "ch") ) ) );
+ *     }};
+ * </pre>
+ *
+ * The example code would keep observing the "parent" relation, and
+ * when it is updated to present a different grandparent-parent-child
+ * linkage from before, the goal "grandparent" is intended on the
+ * "inference" {@link com.intendico.gorite.Performer.TodoGroup
+ * Performer.TodoGroup}, with the data elements "gp", "parent" and
+ * "ch" set. Further, the data element "reflection event" is set to
+ * "added" or "lost" to indicate whether the change is of adding or
+ * losing a matching binding for the query.
+ *
+ *
+ * <p> Technicallly each added {@link Query} gets wrapped into a
+ * {@link Snapshot} in a {@link Reflector.Reflection} object, which is
+ * set up as {@link Observer} to observe any changes to the {@link
+ * Query} sources. The {@link Snapshot} wrapping acts as a filter to
+ * ensure that only changes cause percepts. The percept will hold the
+ * input query as "percept" data element, and all {@link Ref} object
+ * values of the query as corresponding data elemens.
+ *
+ */
+public class Reflector extends Perceptor {
+
+    /**
+     * The name of the data element to indicate whether its an
+     * added or lost event
+     */
+    public String REFLECTION_EVENT = "reflection event";
+
+    /**
+     * Constructor that ties the Reflector to the given {@link
+     * Performer}, causing goals as named in the {@link
+     * com.intendico.gorite.Performer.TodoGroup Performer.TodoGroup}
+     * as named.
+     */
+    public Reflector(Performer p,String goal,String todo) {
+       super( p, goal, todo );
+    }
+
+    /**
+     * Utility class for observing the query sources, and trigger
+     * goals when bindings change state.
+     */
+    public class Reflection implements Observer {
+
+       /**
+        * Holds the query wrapped into a {@link Snapshot}
+        */
+       public Snapshot snap;
+
+       /**
+        * Holds the perception goal caused by this reflection.
+        */
+       public Goal goal;
+
+       /**
+        * Holds the reflection mode; postive for reacting to added
+        * bindings, negative for reacting to lost bindings and zero
+        * for both.
+        */
+       public int mode = 1;
+
+       /**
+        * Alternative constructor, with a goal name.
+        */
+       public Reflection(Query q,String g) {
+           this( q, g != null? new BDIGoal( g ) : null );
+       }
+
+       /**
+        * Constructor, which applies the {@link Snapshot} wrapping.
+        */
+       public Reflection(Query q,Goal g) {
+           snap = new Snapshot( q );
+           snap.addObserver( this );
+           goal = g;
+       }
+
+       /**
+        * Implements {@link Observer#update} by reviewing the query
+        * and generating percepts for new bindings.
+        */
+       synchronized public void update(Observable x,Object y) {
+           try {
+               snap.reset();
+               Vector/*<Ref>*/ refs = snap.getRefs( new Vector/*<Ref>*/() );
+               if ( mode >= 0 ) {
+                   // Process added bindings
+                   while ( snap.next() ) {
+                       Data d = new Data();
+                       d.set( refs );
+                       d.setValue( REFLECTION_EVENT, "added" );
+                       perceive( snap.query, d, goal );
+                   }
+               }
+               if ( mode <= 0 ) {
+                   // Process lost bindings
+                   for ( Iterator/*<Vector>*/ i =
+                             snap.lost.iterator(); i.hasNext(); ) {
+                       Vector v = (Vector) i.next();
+                       Data d = new Data();
+                       Ref.bind( refs, v );
+                       d.set( refs );
+                       d.setValue( REFLECTION_EVENT, "lost" );
+                       perceive( snap.query, d, goal );
+                   }
+               }
+           } catch (Exception e) {
+               e.printStackTrace();
+           }
+       }
+
+       /**
+        * Cancels the observation of query sources.
+        */
+       public void cancel() {
+           snap.deleteObserver( this );
+       }
+    }
+
+    /**
+     * Keeps the current collection of queries, which are contained in
+     * {@link Reflection} objects
+     */
+    public IdentityHashMap/*<Query,Reflection>*/ reflections =
+        new IdentityHashMap/*<Query,Reflection>*/();
+
+    /**
+     * Adds a {@link Reflection} that uses the given {@link Query}.
+     */
+    public void addQuery(Query q) {
+       addQuery( q, (String) null );
+    }
+
+    /**
+     * Adds a {@link Reflection} query using the given {@link
+     * Goal#name}.
+     */
+    public void addQuery(Query q,String goal) {
+       addQuery( q, goal, 1 );
+    }
+
+    /**
+     * Adds a {@link Reflection} query using the given {@link
+     * Goal#name}.
+     */
+    public void addQuery(Query q,String goal,int m) {
+       Reflection r = (Reflection) reflections.get( q );
+       if ( r == null ) {
+           r = new Reflection( q, goal );
+           r.mode = m;
+           reflections.put( q, r );
+           r.update( null, null );
+       } else if ( r.mode != m ) {
+           r.mode = 0;
+           r.update( null, null );
+       }
+    }
+
+    /**
+     * Adds a reflection query using the given Reflection goal.
+     */
+    public void addQuery(Query q,Goal goal) {
+       addQuery( q, goal, 1 );
+    }
+
+    /**
+     * Adds a reflection query using the given Reflection goal.
+     */
+    public void addQuery(Query q,Goal goal,int m) {
+       Reflection r = (Reflection) reflections.get( q );
+       if ( r == null ) {
+           r = new Reflection( q, goal );
+           r.mode = m;
+           reflections.put( q, r );
+           r.update( null, null );
+       } else if ( r.mode != m ) {
+           r.mode = 0;
+           r.update( null, null );
+       }
+    }
+
+    /**
+     * Removes the {@link Reflection} that uses the given {@link
+     * Query}.
+     */
+    public void removeQuery(Query q) {
+       Reflection r = (Reflection) reflections.remove( q );
+       if ( r != null ) {
+           r.cancel();
+       }
+    }
+
+    /**
+     * Returns a String representation of this object.
+     */
+    public String toString() {
+       StringBuilder s = new StringBuilder();
+       s.append( "Reflector(" + performer + "," +
+                 handler + ",\"" + todo_group + "\"):" );
+       for ( Iterator/*<Query>*/ ri = reflections.keySet().iterator();
+             ri.hasNext(); ) {
+           Query q = (Query) ri.next();
+           s.append( "\n  query: " );
+           s.append( q.toString() );
+       }
+       return s.toString();
+    }
+}
diff --git a/com/intendico/gorite/addon/RemotePerforming.java b/com/intendico/gorite/addon/RemotePerforming.java
new file mode 100644 (file)
index 0000000..8d6888e
--- /dev/null
@@ -0,0 +1,198 @@
+/*********************************************************************
+Copyright 2013, Ralph Ronnquist.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.data.Ref;
+import com.intendico.gorite.Capability;
+import com.intendico.gorite.Data;
+import com.intendico.gorite.Goal.States;
+import com.intendico.gorite.Goal;
+import java.util.Vector;
+
+/**
+ * This capability wraps goals to be performed by a remoteHCyl2 performer,
+ * i.e., one that exists in a different process. A RemotePerforming
+ * instance is created towards a remoteHCyl2 performer, and then it manages
+ * goal transfer to that performer. To this end, the capability is set
+ * up with a model of the remoteHCyl2 goal handling. This is used for
+ * filtering which goals to forward, and what to do with data to and
+ * from those goals. The following is an illustration:
+ * <pre>
+ *     addCapability( new RemotePerforming( my_remote_connector ) {{
+ *         addGoal( new RemoteGoal(
+ *            "review paper",
+ *            String [] { "submission" },
+ *            String [] { "review" },
+ *         ) );
+ *     }} );
+ * 
+ * }
+ * </pre>
+ * Note that my_remote_connector is an implementation of the {@link
+ * RemotePerforming.Connector} interface, to represent the "logical
+ * connection" to the remoteHCyl2 performer. When performing a goal, the
+ * connector is expected to create a {@link
+ * RemotePerforming.Connection} object to represent a particular
+ * realisation of the logical connection for the particular goal
+ * execution.
+ * <p>
+ * Further, we note that RemotePerforming is a {@link Capability}, and
+ * as such, it may have other goals and sub capabilities than the
+ * {@link RemotePerforming.RemoteGoal}. Also, once a RemoteGoal is
+ * created within a RemotePerforming capability, it may in fact be added
+ * to another capability than the one it is created within. However,
+ * doing so usually causes more grief than benefits.
+ */
+public class RemotePerforming extends Capability {
+
+    /**
+     * This interface is to be implemented by the remoteHCyl2 channel
+     * management sub system.
+     */
+    public interface Connector {
+       /**
+        * This method should trigger remoteHCyl2 goal execution.  It
+        * creates a {@link Connection} object to represent the
+        * particular goal execution. Thereafter goal execution will
+        * be monitored via the {@link Connection} methods.
+        */
+       public Connection perform(
+           String goal,String head,Vector<Ref> ins,Vector<Ref> outs);
+    }
+
+    /**
+     * This interface is implemented by the connectivity sub system,
+     * to represent a particular goal execution connection.
+     */
+    public interface Connection {
+       /**
+        * This method is used repeatedly in order to discover the
+        * status of the remote goal execution.
+        */
+       public States check() throws Throwable;
+
+       /**
+        * This method will be called if the goal execution is to be
+        * cancelled form the triggering side.
+        */
+       public void cancel();
+        
+        public Vector<Ref> results();
+    }
+
+    /**
+     * Holds the remote end connector.
+     */
+    public Connector connector;
+
+    /**
+     * Constructor with a {@link Connector} implementation to be used
+     * for interacting with the remote end.
+     */
+    public RemotePerforming(Connector c) {
+       connector = c;
+    }
+    
+    public RemoteGoal create( String name,String [] i,String [] o ) {
+        return new RemoteGoal( name, i, o );
+    }
+
+    /**
+     * This class is a wrapper for goals that are to be performed by
+     * the remote side.
+     */
+    public class RemoteGoal extends Goal {
+
+       /**
+        * Keeps track of names of goal inputs.
+        */
+       public String [] ins;
+
+       /**
+        * Keeps track of goal outputs.
+        */
+       public String [] outs;
+
+       /**
+        * Constructor with goal name, input names and output names.
+        */
+       public RemoteGoal(String name,String [] i,String [] o)
+       {
+           super( name );
+           ins = i;
+           outs = o;
+       }
+
+       /**
+        * Overrides {@link Goal#instantiate} to create a {@link
+        * RemoteInstance} to manage remote goal execution.
+        */
+       public Instance instantiate(String h,Data d)
+       {
+           return new RemoteInstance( h, d );
+       }
+
+
+       public class RemoteInstance extends Instance {
+
+           public RemoteInstance(String h,Data d)
+           {
+               super( h );
+           }
+
+           /**
+            * The identification of the remote end connection.
+            */
+           public Connection connection = null;
+
+           /**
+            * Keep track of output Ref objects
+            */
+           public Vector<Ref> output;
+
+           /**
+            * Manages remote goal execution. On first call, the
+            * remote goal is triggered, while STOPPED is returned,
+            * and on subsequent calls, the {@link Connection} is
+            * queried about completion.
+            */
+           public States action(String head,Data d) {
+
+               if ( connection == null ) {
+                   Vector<Ref> input = Ref.create( ins );
+                   d.get( input, true );
+                   output = Ref.create( outs );
+                   connection = connector.perform(
+                       head, getGoalName(), input, output );
+                   return States.STOPPED;
+               }
+               try {
+                   States s = connection.check();
+                   if ( s == States.PASSED || s == States.FAILED ) { 
+                        output = connection.results();
+                        //System.err.println( output );
+                       d.set( output );
+                    }
+                   return s;
+               } catch (Throwable t) {
+                   t.printStackTrace();
+                   cancel();
+                   return States.CANCEL;
+               }
+           }
+
+           /**
+            * Overrides {@link Instance#cancel} to forward
+            * cancellation to the connection (if any)
+            */
+           public void cancel()
+           {
+               if ( connection != null )
+                   connection.cancel();
+           }
+       }
+    }
+}
+
diff --git a/com/intendico/gorite/addon/SubGoal.java b/com/intendico/gorite/addon/SubGoal.java
new file mode 100644 (file)
index 0000000..79062da
--- /dev/null
@@ -0,0 +1,289 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+
+/**
+ * This is a utility class for managing the dynamic state of a sub
+ * goal execution within a (task) Goal's execute method. The execute
+ * method that needs to perform sub goals (typically BDIGoal goals)
+ * will need to obtain its SubGoal object on entry, and then dispatch
+ * to the execution point appropriate for the SubGoal progress count.
+ *
+ * <p> Note that when performing sub goals, it is also appropriate to
+ * re-implement the {@link Goal#cancelled} method for propagating
+ * intention branch cancellation.
+ *
+ * <p> The following is an illustration of using SubGoal.
+ * <pre>
+ * static Goal goal1 = new BDIGoal( "the first sub goal" );
+ * static Goal goal2 = new BDIGoal( "the second sub goal" );
+ * static Goal goal3 = new BDIGoal( "the third sub goal" );
+ *
+ * addPlan( new Plan( "use SubGoal for something" ) {
+ *
+ *     // Propagate intention branch cancellation to sub goal, if any
+ *     public void cancelled(Goal.Instance which) {
+ *         SubGoal.cancel( which );
+ *     }
+ *
+ *     // Plan body in Java, with sub goals:
+ *     //
+ *     // + Perform goal1 in state 0 with goal1;
+ *     //
+ *     // + Perform goal2 repeatedly through states 1-11, but break
+ *     //   the loop and warp to state 12 on a LoopEndException;
+ *     //   (with a trace output to stderr)
+ *     //
+ *     // + Perform goal3 in state 12, and swap its PASSED and FAILED
+ *     //   result to be our result
+ *
+ *     public States execute(Data data) {
+ *         SubGoal sub = SubGoal.get( data );
+ *         if ( sub.inProgress( goal1, 0 ) )
+ *             return sub.state();
+ *         while ( sub.progress() < 12 ) {
+ *             try {
+ *                 if ( sub.inProgress( goal2 ) )
+ *                     return sub.state();
+ *             } catch (LoopEndException e) {
+ *                 System.err.println( "END at state " + sub.progress() );
+ *                 sub.count = 12;
+ *                 break;
+ *             }
+ *         }
+ *         boolean pass = false;
+ *         if ( sub.inProgress( goal3, 12 ) ) {
+ *             if ( sub.state() != FAILED ) {
+ *                 return sub.state();
+ *             }
+ *             pass = true;
+ *         }
+ *         sub.done(); 
+ *         return pass? PASSED : FAILED;
+ *     }
+ * } );
+ * </pre>
+ *
+ * <p> Note that an intention branch may only have one active SubGoal
+ * object at a time, and you have to go out of your way in coding to
+ * juggle multiple concurrently active SubGoal objects. If you want
+ * parallel execution, then you should execute an approprate
+ * ParallelGoal rather than trying to implement your own parallel goal
+ * execution management.
+ *
+ * <p> The sub goal execution may throw an exception, where it in
+ * particular may be a {@link LoopEndException} or a {@link
+ * ParallelEndException}. In most cases, the execute method would
+ * ignore this, and thus allow them to be propagated up through the
+ * invocation. In the example above, the goal2 invocation, being a
+ * loop, catch {@link LoopEndException}, and let that break the loop
+ * sucessfully.
+ */
+public class SubGoal {
+
+    /**
+     * This is a convenience field that an execution method may use
+     * for marking its progress through its sub goal invocations. Its
+     * value is automatically appended to the trace head for the next
+     * sub goal, and it is incremented when a sub goal execution
+     * terminates.
+     * @see #progress()
+     */
+    public int count;
+
+    /**
+     * This is an internal cache of the {@link
+     * com.intendico.gorite.Goal.Instance Goal.Instance} in progress,
+     * if any. It is publically accessible, but the execution method
+     * should refrain from changing it.
+     * @see #inProgress(Goal,int)
+     * @see #inProgress(Goal)
+     */
+    public Goal.Instance current;
+
+    /**
+     * This is an internal cache of the most recent return value of
+     * the most recent sub goal invocation.  It is publically
+     * accessible, but the execution method should refrain from
+     * changing it. When the sub goal invocation is incomplete
+     * (i.e. either BLOCKED or STOPPED), the execution method should
+     * return this value.
+     * @see #inProgress(Goal,int)
+     * @see #inProgress(Goal)
+     */
+    public Goal.States result;
+
+    /**
+     * This is a handle to the Data object for the executing
+     * intention.  It is publically accessible, but the execution
+     * method should refrain from changing it.
+     */
+    public Data data;
+
+    /**
+     * Utility method that looks up a current SubGoal object in the
+     * given Data, or creates a new one associated with the current
+     * Data. To do so, it uses the Data element named by {@link
+     * Data#thread_name}, which is unique within the intention for the
+     * intention branch.
+     */
+    public static SubGoal get(Data data) {
+       SubGoal sub = tryGet( data );
+       return ( sub == null )? new SubGoal( data ) : sub;
+    }
+
+    /**
+     * Utility method that looks up a current SubGoal object in the
+     * given Data and returns it, or null, if none exists. You'd use
+     * this in combination with a SubGoal extension class, e.g. for
+     * carrying your own progress details. Thus, instead of
+     * using {@link #get} to find the sub goal manager, you would have
+     * a code snippet like the following:
+     * <pre>
+     * SubGoal sub = SubGoal.tryGet( data );
+     * if ( sub == null ) {
+     *     sub = new MySubGoal( data );
+     * }
+     * </pre>
+     */
+    public static SubGoal tryGet(Data data) {
+       return (SubGoal) data.getValue( data.thread_name );
+    }
+
+    /**
+     * Constructor. Normally SubGoal objects are only created
+     * indirectly, either via the {@link #get} method, or via
+     * <tt>super</tt> calls in extension classes. It always end up in
+     * this constructor, which associates the newly created object
+     * with the given {@link Data}, and puts itself as the Data
+     * element named by the current {@link Data#thread_name}, which is
+     * unique for the intention branch.
+     */
+    public SubGoal(Data d) {
+       data = d;
+       data.setValue( data.thread_name, this );
+    }
+
+    /**
+     * Clean up by forgetting the data element named by the current
+     * {@link Data#thread_name}.
+     */
+    public void done() {
+       data.forget( data.thread_name );
+    }
+
+    /**
+     * This method is used by the execution method to trigger and
+     * pursue a sub goal execution. The method returns true if the
+     * sub goal invocation returns without passing (i.e., returns
+     * STOPPED, BLOCKED or FAILED), and it returns false when the sub
+     * goal invocation returns PASSED. Also, when the sub goal returns
+     * PASSED or FAILED, the count gets incremented before this method
+     * returns.
+     */
+    public boolean inProgress(Goal goal)
+       throws LoopEndException, ParallelEndException {
+       if ( current == null ) {
+           String head = data.thread_name;
+           int i = head.lastIndexOf( "\"" );
+           if ( i > 0 )
+               i = head.lastIndexOf( "\"", i - 1 );
+           i -= 1;
+           if ( i > 0 )
+               head = head.substring( 0, i );
+           current = goal.instantiate( head + ":" + count, data );
+       }
+       result = current.perform( data );
+       if ( result == Goal.States.PASSED || result == Goal.States.FAILED ) {
+           current = null;
+           count += 1;
+       }
+       return result != Goal.States.PASSED;
+    }
+
+    /**
+     * This method verifies that the progress count is as given,
+     * before pursuing sub goal execution as per {@link
+     * #inProgress(Goal)}. Otherwise the method returns false;
+     */
+    public boolean inProgress(Goal goal,int at)
+       throws LoopEndException, ParallelEndException {
+       return count == at? inProgress( goal ) : false;
+    }
+
+    /**
+     * Utility method to cancel any ongoing sub goal execution, and
+     * force the count to a given state. If there is no sub goal in
+     * progress, the method returns false. Otherwise, that sub goal
+     * execution is cancelled and forgotten, {@link #result} is set to
+     * CANCEL, and true is returned.
+     */
+    public boolean cancel(int next) {
+       count = next;
+       if ( current == null )
+           return false;
+       current.cancel();
+       current = null;
+       result = Goal.States.CANCEL;
+       return true;
+    }
+
+    /**
+     * This method cancels and forgets any sub goal execution in
+     * progress for the given {@link
+     * com.intendico.gorite.Goal.Instance Goal.Instance}.
+     */
+    public static void cancel(Goal.Instance which) {
+       Data data = which.data;
+       String old = data.setThreadName( which.thread_name );
+       SubGoal sub = (SubGoal) data.getValue( which.thread_name );
+       if ( sub != null ) {
+           sub.cancel( sub.count + 1 );
+           sub.done();
+       }
+       data.setThreadName( old );
+    }
+
+    /**
+     * Utility method that returns the current progress count.
+     */
+    public int progress() {
+       return count;
+    }
+
+    /**
+     * Utility method that returns the cached sub intention state.
+     */
+    public Goal.States state() {
+       return result;
+    }
+
+    /**
+     * Utility method that tells whether the current state has been
+     * activated or not; i.e., whether the sub intention for the
+     * current state has been created or not.
+     */
+    public boolean isActive() {
+       return current != null;
+    }
+
+}
diff --git a/com/intendico/gorite/addon/TimeTrigger.java b/com/intendico/gorite/addon/TimeTrigger.java
new file mode 100644 (file)
index 0000000..b0b5612
--- /dev/null
@@ -0,0 +1,98 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.Data;
+import java.util.Observable;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class defines an Observable that schedules itself for issuing
+ * a notfication to Observers at a future time.
+ */
+public class TimeTrigger extends Observable {
+
+    /**
+     * Utility method to establish a TimeTrigger as a {@link Data}
+     * element value, and use as execution trigger after the given
+     * delay. The indicated {@link com.intendico.gorite.Data.Element
+     * Data.Element} should be unset initially, to be set by this
+     * method to a TimeTrigger for the given delay. Upon subsequent
+     * calls, the TimeTrigger is rescheduled until its deadline has
+     * passed, at which time the given {@link
+     * com.intendico.gorite.Data.Element Data.Element} is forgotten.
+     * @return true if a trigger has been set and false otherwise
+     */
+    public static boolean isPending(Data data,String element,long delay) {
+       TimeTrigger tt = (TimeTrigger) data.getValue( element );
+       if ( tt == null ) {
+           tt = new TimeTrigger( delay );
+           data.setValue( element, tt );
+           data.setTrigger( tt );
+       }
+       if ( tt.reschedule() )
+           return true;
+       data.forget( element );
+       return false;
+    }
+
+    /**
+     * A global {@link Timer} for scheduling future events on.
+     */
+    public static Timer timer = new Timer( true );
+
+    /**
+     * The deadline time for this TimeTrigger.
+     */
+    public long deadline;
+
+    /**
+     * Constructor for a given future time.
+     */
+    public TimeTrigger(long delay) {
+       deadline = System.currentTimeMillis() + delay;
+    }
+
+    /**
+     * Reset for triggering at a new future time.
+     */
+    public void reset(long delay) {
+       deadline = System.currentTimeMillis() + delay;
+    }
+
+    /**
+     * This method tests whether the trigger time is reached or not,
+     * and if not, it sets up a {@link TimerTask} for notifying
+     * observers when the trigger time is reached.
+     */
+    public boolean reschedule() {
+       long delay = deadline - System.currentTimeMillis();
+       if ( delay <= 0 )
+           return false;
+       timer.schedule( new TimerTask() {
+           public void run() {
+               setChanged();
+               notifyObservers( this );
+           }
+       }, delay );
+       return true;
+    }
+}
diff --git a/com/intendico/gorite/addon/TodoGroupParallel.java b/com/intendico/gorite/addon/TodoGroupParallel.java
new file mode 100644 (file)
index 0000000..497f406
--- /dev/null
@@ -0,0 +1,44 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+import com.intendico.gorite.Performer.TodoGroup;
+import java.util.Vector;
+
+/**
+ * This plan is a {@link TodoGroup} meta goal handler that combines
+ * the "round-robin" with "skip blocked" management functions.
+ * @see TodoGroupRoundRobin
+ * @see TodoGroupSkipBlocked
+ */
+public class TodoGroupParallel extends Goal {
+
+    /**
+     * Constructor for providing {@link TodoGroup} management through
+     * the named meta goal.
+     */
+    public TodoGroupParallel(String name) {
+       super( name, new Goal [] {
+               new TodoGroupRoundRobin( "round robin" ),
+               new TodoGroupSkipBlocked( "skip blocked" )
+           } );
+    }
+}
diff --git a/com/intendico/gorite/addon/TodoGroupRoundRobin.java b/com/intendico/gorite/addon/TodoGroupRoundRobin.java
new file mode 100644 (file)
index 0000000..647dd41
--- /dev/null
@@ -0,0 +1,75 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+import com.intendico.gorite.Performer.TodoGroup;
+import java.util.Vector;
+
+/**
+ * This plan is a {@link TodoGroup} meta goal handler to cycle through
+ * the stacked intentions, by putting the top one last, except when it
+ * is newly added. The actual goal name for the meta goal is given at
+ * construction time, and unless it is the default {@link
+ * Performer#TODOGROUP_META_GOAL}, the actual {@link TodoGroup}
+ * instances to be managed by this goal need to be set up explicitly
+ * using {@link Performer#addTodoGroup}.
+ *
+ * <p> Usage example:
+ * <pre>
+ * new Performer( "example" ) {
+ *     addGoal( new TodoGroupRoundRobin( "round robin todogroup meta goal" ) );
+ *     addTodoGroup( "example", "round robin todogroup meta goal" );
+ * }
+ * </pre>
+ *
+ * <p> Another usage example, to make it the default todogroup meta goal:
+ * <pre>
+ * new Performer( "example" ) {
+ *     addGoal( new TodoGroupRoundRobin( TODOGROUP_META_GOAL ) );
+ * }
+ * </pre>
+ */
+public class TodoGroupRoundRobin extends Goal {
+
+    /**
+     * Constructor for providing {@link TodoGroup} management through
+     * the named meta goal.
+     */
+    public TodoGroupRoundRobin(String name) {
+       super( name );
+    }
+
+    /**
+     * Overrides {@link Goal#execute} to provide the meta goal
+     * implementation. This expects a {@link Data} object with
+     * elements named by {@link Performer#TODOGROUP}, "coming" and
+     * "top". The implementation cycles the todogroup unless the "top"
+     * intention is in the "coming" vector.
+     */
+    public States execute(Data d) {
+       TodoGroup todogroup = (TodoGroup) d.getValue( Performer.TODOGROUP );
+       Vector coming = (Vector) d.getValue( "coming" );
+       Goal.Instance top = (Goal.Instance) d.getValue( "top" );
+       if ( ! coming.contains( top ) )
+           todogroup.rotate();
+       return States.PASSED;
+    }
+}
diff --git a/com/intendico/gorite/addon/TodoGroupSkipBlocked.java b/com/intendico/gorite/addon/TodoGroupSkipBlocked.java
new file mode 100644 (file)
index 0000000..520aa86
--- /dev/null
@@ -0,0 +1,84 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.gorite.addon;
+
+import com.intendico.gorite.*;
+import com.intendico.gorite.Performer.TodoGroup;
+import java.util.Vector;
+
+/**
+ * This plan is a {@link TodoGroup} meta goal handler to maintain
+ * focus to "the first" running intention, i.e., to avoid that the
+ * whole group is blocked when the top intention gets
+ * blocked. Instead, when the top intention gets blocked, the
+ * TodoGroup stack is scanned for its top-most non-blocked intention,
+ * which then is promoted.  The actual goal name for the meta goal is
+ * given at construction time, and unless it is the default {@link
+ * Performer#TODOGROUP_META_GOAL}, the actual {@link TodoGroup}
+ * instances to be managed by this goal need to be set up explicitly
+ * using {@link Performer#addTodoGroup}.
+ *
+ * <p> Usage example:
+ * <pre>
+ * new Performer( "example" ) {
+ *     addGoal( new TodoGroupSkipBlocked( "skip blocked todogroup meta goal" ) );
+ *     addTodoGroup( "example", "skip blocked todogroup meta goal" );
+ * }
+ * </pre>
+ *
+ * <p> Another usage example, to make it the default todogroup meta
+ * goal:
+ * <pre>
+ * new Performer( "example" ) {
+ *     addGoal( new TodoGroupSkipBlocked( TODOGROUP_META_GOAL ) );
+ * }
+ * </pre>
+ */
+public class TodoGroupSkipBlocked extends Goal {
+
+    /**
+     * Constructor for providing {@link TodoGroup} management through
+     * the named meta goal.
+     */
+    public TodoGroupSkipBlocked(String name) {
+       super( name );
+    }
+
+    /**
+     * Overrides {@link Goal#execute} to provide the meta goal
+     * implementation. This expects a {@link Data} object with the
+     * {@link TodoGroup} concerned as the element named by {@link
+     * Performer#TODOGROUP}.  The implementation promotes the first
+     * non-blocked intention, if any. Returns PASSED if there is a
+     * non-blocked intention, an FAILED otherwise.
+     */
+    public States execute(Data d) {
+       TodoGroup tg = (TodoGroup) d.getValue( Performer.TODOGROUP );
+       Vector/*<Goal.Instance>*/ s = tg.stack;
+       for ( int i = 0; i < s.size(); i++ ) {
+           if ( ((Instance) s.get( i )).state == States.STOPPED ) {
+               tg.promote( i );
+               return States.PASSED;
+           }
+       }
+       return States.FAILED;
+    }
+
+}
diff --git a/com/intendico/gorite/addon/remote/Connection.java b/com/intendico/gorite/addon/remote/Connection.java
new file mode 100644 (file)
index 0000000..afc60e7
--- /dev/null
@@ -0,0 +1,33 @@
+/*********************************************************************
+Copyright 2013, Ralph Ronnquist.
+**********************************************************************/
+
+package com.intendico.gorite.addon.remote;
+
+import com.intendico.gorite.addon.*;
+import com.intendico.data.Ref;
+import com.intendico.gorite.Capability;
+import com.intendico.gorite.Data;
+import com.intendico.gorite.Goal.States;
+import com.intendico.gorite.Goal;
+import java.util.Vector;
+
+    /**
+     * This interface is implemented by the connectivity sub system,
+     * to represent a particular goal execution connection.
+     */
+    public interface Connection {
+       /**
+        * This method is used repeatedly in order to discover the
+        * status of the remote goal execution.
+        */
+       public States check() throws Throwable;
+
+       /**
+        * This method will be called if the goal execution is to be
+        * cancelled form the triggering side.
+        */
+       public void cancel();
+        
+        public Vector<Ref> results();
+    }
diff --git a/com/intendico/gorite/addon/remote/Connector.java b/com/intendico/gorite/addon/remote/Connector.java
new file mode 100644 (file)
index 0000000..62016a8
--- /dev/null
@@ -0,0 +1,28 @@
+/*********************************************************************
+Copyright 2013, Ralph Ronnquist.
+**********************************************************************/
+
+package com.intendico.gorite.addon.remote;
+
+import com.intendico.gorite.addon.*;
+import com.intendico.data.Ref;
+import com.intendico.gorite.Capability;
+import com.intendico.gorite.Data;
+import com.intendico.gorite.Goal.States;
+import com.intendico.gorite.Goal;
+import java.util.Vector;
+
+    /**
+     * This interface is to be implemented by the remoteHCyl2 channel
+     * management sub system.
+     */
+    public interface Connector {
+       /**
+        * This method should trigger remoteHCyl2 goal execution.  It
+        * creates a {@link Connection} object to represent the
+        * particular goal execution. Thereafter goal execution will
+        * be monitored via the {@link Connection} methods.
+        */
+       public Connection perform(
+           String goal,String head,Vector<Ref> ins,Vector<Ref> outs);
+    }
\ No newline at end of file
diff --git a/com/intendico/gorite/addon/remote/RemotePerforming.java b/com/intendico/gorite/addon/remote/RemotePerforming.java
new file mode 100644 (file)
index 0000000..f9b54c2
--- /dev/null
@@ -0,0 +1,164 @@
+/*********************************************************************
+Copyright 2013, Ralph Ronnquist.
+**********************************************************************/
+
+package com.intendico.gorite.addon.remote;
+
+import com.intendico.data.Ref;
+import com.intendico.gorite.Capability;
+import com.intendico.gorite.Data;
+import com.intendico.gorite.Goal.States;
+import com.intendico.gorite.Goal;
+import java.util.Vector;
+
+/**
+ * This capability wraps goals to be performed by a remote performer,
+ * i.e., one that exists in a different process. A RemotePerforming
+ * instance is created towards a remote performer, and then it manages
+ * goal transfer to that performer. To this end, the capability is set
+ * up with a model of the remote goal handling. This is used for
+ * filtering which goals to forward, and what to do with data to and
+ * from those goals. The following is an illustration:
+ * <pre>
+ *     addCapability( new RemotePerforming( my_remote_connector ) {{
+ *         addGoal( new RemoteGoal(
+ *            "review paper",
+ *            String [] { "submission" },
+ *            String [] { "review" },
+ *         ) );
+ *     }} );
+ * 
+ * }
+ * </pre>
+ * Note that my_remote_connector is an implementation of the {@link
+ * RemotePerforming.Connector} interface, to represent the "logical
+ * connection" to the remote performer. When performing a goal, the
+ * connector is expected to create a {@link
+ * RemotePerforming.Connection} object to represent a particular
+ * realisation of the logical connection for the particular goal
+ * execution.
+ * <p>
+ * Further, we note that RemotePerforming is a {@link Capability}, and
+ * as such, it may have other goals and sub capabilities than the
+ * {@link RemotePerforming.RemoteGoal}. Also, once a RemoteGoal is
+ * created within a RemotePerforming capability, it may in fact be added
+ * to another capability than the one it is created within. However,
+ * doing so usually causes more grief than benefits.
+ */
+public class RemotePerforming extends Capability {
+
+    /**
+     * Holds the remote end connector.
+     */
+    public Connector connector;
+
+    /**
+     * Constructor with a {@link Connector} implementation to be used
+     * for interacting with the remote end.
+     */
+    public RemotePerforming(Connector c) {
+       connector = c;
+    }
+    
+    // added so that a remote goal could be created non-anonymously
+    public RemoteGoal create( String name,String [] i,String [] o ) {
+        return new RemoteGoal( name, i, o );
+    }
+
+    /**
+     * This class is a wrapper for goals that are to be performed by
+     * the remote side.
+     */
+    public class RemoteGoal extends Goal {
+
+       /**
+        * Keeps track of names of goal inputs.
+        */
+       public String [] ins;
+
+       /**
+        * Keeps track of goal outputs.
+        */
+       public String [] outs;
+
+       /**
+        * Constructor with goal name, input names and output names.
+        */
+       public RemoteGoal(String name,String [] i,String [] o)
+       {
+           super( name );
+           ins = i;
+           outs = o;
+       }
+
+       /**
+        * Overrides {@link Goal#instantiate} to create a {@link
+        * RemoteInstance} to manage remote goal execution.
+        */
+       public Instance instantiate(String h,Data d)
+       {
+           return new RemoteInstance( h, d );
+       }
+
+
+       public class RemoteInstance extends Instance {
+
+           public RemoteInstance(String h,Data d)
+           {
+               super( h );
+           }
+
+           /**
+            * The identification of the remote end connection.
+            */
+           public Connection connection = null;
+
+           /**
+            * Keep track of output Ref objects
+            */
+           public Vector<Ref> output;
+
+           /**
+            * Manages remote goal execution. On first call, the
+            * remote goal is triggered, while STOPPED is returned,
+            * and on subsequent calls, the {@link Connection} is
+            * queried about completion.
+            */
+           public States action(String head,Data d) {
+
+               if ( connection == null ) {
+                   Vector<Ref> input = Ref.create( ins );
+                   d.get( input, true );
+                   output = Ref.create( outs );
+                   connection = connector.perform(
+                       head, getGoalName(), input, output );
+                   return States.STOPPED;
+               }
+               try {
+                   States s = connection.check();
+                   if ( s == States.PASSED || s == States.FAILED ) { 
+                        output = connection.results();
+                        //System.err.println( output );
+                       d.set( output );
+                    }
+                   return s;
+               } catch (Throwable t) {
+                   t.printStackTrace();
+                   cancel();
+                   return States.CANCEL;
+               }
+           }
+
+           /**
+            * Overrides {@link Instance#cancel} to forward
+            * cancellation to the connection (if any)
+            */
+           public void cancel()
+           {
+               if ( connection != null )
+                   connection.cancel();
+           }
+       }
+    }
+}
+
diff --git a/com/intendico/sdp/BNFGrammar.java b/com/intendico/sdp/BNFGrammar.java
new file mode 100644 (file)
index 0000000..32e618b
--- /dev/null
@@ -0,0 +1,738 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.sdp;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * The BNFGrammar class is a base class for grammars that make use of
+ * the LLBNF translator for defining their syntax.
+ */
+public class BNFGrammar extends Grammar {
+
+    /**
+     * Utility method to scan for blank or comment.
+     */
+    public int findBlankOrComment(CharSequence s,int from) {
+       int end = s.length();
+       for ( ; from < end; from++ ) {
+           if ( Character.isWhitespace( s.charAt( from ) ) )
+               return from;
+           if ( s.charAt( from ) != '/' )
+               continue;
+           if ( from + 1 >= end )
+               return end;
+           if ( s.charAt( from + 1 ) == '*' )
+               return from;
+           if ( s.charAt( from + 1 ) == '/' )
+               return from;
+       }
+       return end;
+    }
+
+    //
+    // Predefined lexical scanners.
+    //
+
+    /**
+     * A Nothing is a lexical token that accepts.
+     */
+    public class Nothing extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public Nothing(String r) {
+           super( r );
+       }
+
+       /**
+        * Scanning nothing.
+        * Requires nothing.
+        */
+       public int scan(CharSequence s,int from) {
+           return from;
+       }
+    }
+
+    /**
+     * An AnySymbol is a lexical token between blanks or comments.
+     */
+    public class AnySymbol extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public AnySymbol(String r) {
+           super( r );
+       }
+
+       /**
+        * Scanning anything unto next blank or comment or end.
+        * Requires something.
+        */
+       public int scan(CharSequence s,int from) {
+           int to = findBlankOrComment( s, from );
+           return from == to? -1 : to ;
+       }
+    }
+
+    /**
+     * An End is a lexical token for nothing after blanks or comments.
+     */
+    public class End extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public End(String r) {
+           super( r );
+       }
+
+        /**
+         * Requires there to remain nothing.
+         */
+        public int scan(CharSequence s,int from) {
+           return from == s.length()? from : -1 ;
+       }
+    }
+
+    /**
+     * An Identifier is a lexical token of Java identifier characters.
+     */
+    public class Identifier extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public Identifier(String r) {
+           super( r );
+       }
+
+       /**
+        * Scanning a Java identifier.
+        */
+        public int scan(CharSequence s,int from) {
+           int end = s.length();
+           if ( from == end )
+               return -1;
+
+           if ( ! Character.isJavaIdentifierStart( s.charAt( from++ ) ) )
+               return -1;
+
+           for ( ; from < end; from++ ) {
+               if ( ! Character.isJavaIdentifierPart( s.charAt( from ) ) )
+                   return from;
+           }
+           return end;
+       }
+    }
+
+    /**
+     * A PackedChars is a lexical token consisting of a string of
+     * characters of a given collection, in any order.
+     */
+    public class PackedChars extends Lexical {
+
+       /**
+        * The accepted characters.
+        */
+       public String characters;
+
+       /**
+        * Constructor.
+        */
+       public PackedChars(String r,String set) {
+           super( r );
+           characters = set;
+       }
+
+       /**
+        * Scanning a PackedChars.
+        */
+        public int scan(CharSequence s,int from) {
+           int end = s.length();
+           if ( from == end )
+               return -1;
+
+           if ( characters.indexOf( s.charAt( from++ ) ) < 0 )
+               return -1;
+
+           for ( ; from < end; from++ ) {
+               if ( characters.indexOf( s.charAt( from ) ) < 0 )
+                   return from;
+           }
+           return end;
+       }
+    }
+
+    /**
+     * A Number is a lexical token of digits. Optionally preceded by a
+     * single "-".
+     */
+    public class Number extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public Number(String r) {
+           super( r );
+       }
+
+       /**
+        * Scanning an integer, with an optional preceding "-".
+        */
+        public int scan(CharSequence s,int from) {
+            int end = s.length();
+
+            if ( from == end )
+                return -1;
+
+           if ( inSet( s, from, end, "+-" ) )
+               from++;
+
+           int h = scanSet( s, from, end, "0123456789" );
+           return h == 0? -1 : from + h;
+        }
+    }
+
+    /**
+     * A Number is a lexical token of digits. Optionally preceded by a
+     * single "-".
+     */
+    public class RealNumber extends Lexical {
+
+       /**
+        * Constructor.
+        */
+       public RealNumber(String r) {
+           super( r );
+       }
+
+       /**
+        * Scanning a real number, with an optional preceding "-" or '+'.
+        */
+        public int scan(CharSequence s,int from) {
+            int end = s.length();
+           int h = 0;
+           int f = 0;
+
+            if ( from == end )
+               return -1;
+
+           if ( inSet( s, from, end, "+-" ) ) {
+               from++;
+               if ( from == end )
+                   return -1;
+               h = scanSet( s, from, end, "0123456789" );
+           } else {
+               if ( s.charAt( from++ ) == '0' ) {
+                   // possible lead-in  for 0xh* or otherwise octal?
+                   if ( from == end )
+                       return end;
+                   if ( s.charAt( from++ ) == 'x' ) {
+                       // found 0x lead-in
+                       h = scanSet( s, from+2, end, "01234567abcdefABCDEF" );
+                       if ( h == 0 )
+                           return -1;
+                       return from + h;
+                   }
+                   from--;
+                   return from + scanSet( s, from, end, "01234567" );
+               }
+               from--; // 
+               h = scanSet( s, from, end, "0123456789" );
+               if ( h == 0 )
+                   return -1;
+           }
+
+           from += h;
+           if ( from == end )
+               return h == 0? -1 : end;
+
+           if ( s.charAt( from++ ) == '.' ) {
+               // there's a fraction part
+               f = scanSet( s, from, end, "0123456789" );
+               if ( h == 0 && f == 0 )
+                   return -1;
+               from += f;
+               if ( from >= end )
+                   return end;
+               if ( inSet( s, from, end, "fF" ) )
+                   return from + 1;
+           } else {
+               // There's no fractional part
+               from--;
+               if ( h == 0 )
+                   return -1;
+               if ( inSet( s, from, end, "lL" ) )
+                   return from + 1;
+           }
+
+           if ( ! inSet( s, from++, end, "eE" ) )
+               return from - 1;
+
+           // scan exponent
+           if ( inSet( s, from, end, "+-" ) )
+               from++;
+           f = scanSet( s, from, end, "0123456789" );
+           return f == 0? -1 : from + f;
+        }
+    }
+
+    /**
+     * Scans past the given set of characters, and tells how far it
+     * has scanned.
+     */
+    static public int scanSet(CharSequence s,int from,int end,String set) {
+       int start = from;
+       for ( ; from < end; from++ )
+           if ( set.indexOf( s.charAt( from ) ) < 0 )
+               return from - start ;
+       return from - start;
+    }
+
+    /**
+     * Tells if the character in s at from is one of those in the set.
+     */
+    static public boolean inSet(CharSequence s,int from,int end,String set) {
+       return from < end? set.indexOf( s.charAt( from ) ) >= 0 : false;
+    }
+
+    /**
+     * Scan a compound character after the '\':
+     * \ a    - letter or any non-digit
+     * \ ooo   - octal
+     * \ x hh   - hexadecimal
+     * \ u nnnn - digits
+     */
+    static public int compound(CharSequence s,int from,int end) {
+       if ( from >= end )
+           return 0;
+       switch ( s.charAt( from ) ) {
+       case 'x':
+           return 3;
+       case 'u':
+           return 5;
+       case '0': case '1': case '2': case '3':
+       case '4': case '5': case '6': case '7':
+           return 3;
+       default:
+           return 1;
+       }
+    }
+
+    /**
+     * A String is a lexical token between a mark characters
+     * (double-quotes by default), but without a new-line in it.
+     */
+    public class QuotedString extends Lexical {
+
+       public char mark;
+
+        /**
+         * Constructor.
+         */
+        public QuotedString(String r,char m) {
+            super( r );
+           mark = m;
+        }
+
+        /**
+         * Utility constructor to use double-quote as mark character.
+         */
+        public QuotedString(String r) {
+            this( r, '"' );
+        }
+
+        /**
+         * Scanning a String
+         */
+        public int scan(CharSequence s,int from) {
+            int end = s.length();
+
+            if ( from == end || s.charAt( from++ ) != mark )
+                return -1;
+
+           tryToken( this, from ); // Special marker
+
+            for ( ; from < end; ) {
+               char c = s.charAt( from++ );
+               if ( c == mark )
+                   return from;
+               if ( c == '\n' )
+                   return -1; // bad string
+               if ( c == '\\' ) {
+                   // lead-in for compound characters.
+                   from += compound( s, from, end );
+                   if ( from > end )
+                       return -1;
+               }
+            }
+           // Bad string 2
+            return -1;
+        }
+
+       /**
+        * The error name for a buggy string.
+        */
+       public String errorName() {
+           return "terminated and well-formed <" + rule + ">" ;
+       }
+    }
+
+    /**
+     * A QuotedCharacter is a lexical token between a pair of
+     * single-quotes, with restrictions.
+     */
+    public class QuotedCharacter extends Lexical {
+
+        /**
+         * Constructor.
+         */
+        public QuotedCharacter(String r) {
+            super( r );
+        }
+
+        /**
+         * Scanning a String
+         */
+        public int scan(CharSequence s,int from) {
+            int end = s.length();
+
+            if ( from == end || s.charAt( from++ ) != '\'' )
+                return -1;
+
+           tryToken( this, from ); // Special marker
+
+           if ( from == end )
+               return -1;
+
+           switch ( s.charAt( from++ ) ) {
+           case '\'':
+               return -1;
+           case '\\':
+               from += compound( s, from, end );
+               break;
+           default:
+               from += 1;
+               break;
+           }
+           if ( from >= end )
+               return -1;
+           if ( s.charAt( from++ ) != '\'' )
+               return -1;
+            return from;
+        }
+
+       /**
+        * The error name for a buggy string.
+        */
+       public String errorName() {
+           return "terminated and well-formed <" + rule + ">" ;
+       }
+    }
+
+
+    /**
+     * A lexical scanner to match multi-word phrases from a given
+     * collection. It greedily consumes its longest match.
+     */
+    public class PhraseSet extends Lexical {
+
+       /**
+        * Matching phrases. This contains the successive heads of the
+        * matching phrases, mapped to 0, 1, or 2. 0 means that the
+        * phrase ends there, 1 means it must continue, and 2 means it
+        * may end or continue. The scanner always consumes as much as
+        * possible.
+        */
+       public TreeSet/*<String>*/ phrases = new TreeSet/*<String>*/();
+
+       /**
+        * Cache of defining phrases.
+        */
+       public Vector/*<String>*/ definition = new Vector/*<String>*/();
+
+       /**
+        * Constructor for a given name and collection of phrases.
+        */
+       public PhraseSet(String name,Vector/*<String>*/ set) {
+           super( name );
+           addSet( set );
+       }
+
+       /**
+        * Utility method to add a collection of phrases.
+        */
+       public void addSet(Vector/*<String>*/ set) {
+           for ( Iterator si = set.iterator(); si.hasNext(); ) {
+               String phrase = (String) si.next();
+               definition.add( phrase );
+               phrases.add( phrase.toLowerCase() );
+           }
+       }
+
+       /**
+        * Returns index to first character following a matching
+        * token.
+        */
+       public int scan(CharSequence input,int from) {
+           CharSequence phrase = input.subSequence( from, input.length() );
+           if ( from >= input.length() )
+               return -1;
+           char first = phrase.charAt( 0 );
+           for ( Iterator/*<String>*/ pi =
+                     phrases.headSet( phrase, true ).descendingIterator();
+                 pi.hasNext(); ) {
+               String key = (String) pi.next();
+               if ( key.charAt( 0 ) != first ) {
+                   System.err.println( "failed: " + key );
+                   return -1;
+               }
+               if ( startsWith( phrase, 0, key ) ) {
+                   return from + key.length();
+               }
+           }
+           return -1;
+       }
+
+       /**
+        * Returns this collection as a defintion clause:
+        * name = { .... }
+        */
+       public String toString() {
+           StringBuilder s = new StringBuilder();
+           s.append( rule );
+           s.append( " = {" );
+           String separator = "\n  ";
+           //for ( String phrase : definition ) {
+           for ( Iterator pi = phrases.iterator(); pi.hasNext(); ) {
+               String phrase = (String) pi.next();
+               s.append( separator );
+               s.append( phrase );
+               separator = ",\n  ";
+           }
+           s.append( "\n}\n" );
+           return s.toString();
+       }
+    }
+
+    //
+    // Predefined utility actions
+    //
+
+    /**
+     * Utility method to add elements individually from a Vector
+     * operand, if it begins with the operator.
+     */
+    static public void addArguments(Vector args,Object op,Object opnd) {
+       if ( opnd instanceof Vector ) {
+           Vector v = (Vector) opnd;
+           if ( v.size() > 0 && op.equals( v.get( 0 ) ) ) {
+               v.remove( 0 );
+               args.addAll( v );
+               return;
+           }
+       }
+       args.add( opnd );
+    }
+
+    /**
+     * Helper class for infix syntax
+     */
+    public class Infix extends Production {
+
+       /**
+        * Default Infix action is to move the middle element to
+        * beginning, if there are three arguments, and otherwise
+        * re-use the Grammar.Production action.
+        */
+       public Object action(Vector v) throws Exception {
+           if ( v.size() != 3 )
+               return super.action( v );
+
+           v.add( 0, v.remove( 1 ) );
+           return v;
+       }
+    }
+
+    /**
+     * Helper class for postfix syntax
+     */
+    public class Postfix extends Production {
+
+       /**
+        * Default Postfix action is to move the last element to
+        * beginning, if there are more than one argument, and otherwise
+        * re-use the Grammar.Production action.
+        */
+       public Object action(Vector v) throws Exception {
+           if ( v.size() <= 1 )
+               return super.action( v );
+
+           v.add( 0, v.remove( v.size() - 1 ) );
+           return v;
+       }
+    }
+
+    /**
+     * Helper class for associative infix syntax
+     */
+    public class Associative extends Production {
+
+       /**
+        * Default Associative action is to move the middle element to
+        * beginning, if there are three arguments, and then flatten
+        * out the first and last argument of they are vectors with
+        * the same operator. If there are not three arguments, then 
+        * re-use the Grammar.Production action.
+        */
+       public Object action(Vector v) throws Exception {
+           if ( v.size() != 3 )
+               return super.action( v );
+
+           Object op = v.get( 1 );
+           Vector args = new Vector();
+           args.add( op );
+           addArguments( args, op, v.get( 0 ) );
+           addArguments( args, op, v.get( 2 ) );
+           return args;
+       }
+    }
+
+    /**
+     * Helper class for lists
+     */
+    public class Enlist extends Production {
+
+       /**
+        * Default Enlist action is coalesc arguments into a Vector.
+        */
+       public Object action(Vector v) {
+           if ( v.size() <= 1 )
+               return v;
+
+           Object last = v.remove( v.size() - 1 );
+           if ( last instanceof Vector ) {
+               v.addAll( (Vector) last );
+           } else {
+               v.add( last );
+           }
+           return v;
+       }
+    }
+
+    /**
+     * Helper class for left-associative lists
+     */
+    public class Leftlist extends Production {
+
+       /**
+        * Default Enlist action is coalesc arguments into a ".."
+        * headed Vector.
+        */
+       public Object action(Vector v) {
+           if ( v.size() <= 1 )
+               return v;
+
+           Object first = v.remove( 0 );
+
+           if ( first instanceof Vector ) {
+               ((Vector) first).addAll( v );
+               return first;
+           }
+
+           v.add( 0, first );
+           return v;
+       }
+    }
+
+    /**
+     * Installs a grammar from string rules using LLBNF.
+     */
+    public void install(String rules) {
+       try {
+           new LLBNF().addRules( this, rules );
+       } catch (Throwable t) {
+           t.printStackTrace();
+           throw new Error( "Grammar failure" );
+       }
+    }
+
+    /**
+     * Utility method to clip a string after some length
+     */
+    public String clip(String input,int clip) {
+       int x = input.indexOf( "\n", 0 );
+       if ( x > 0 && x < clip )
+           return input.substring( 0, x ) + " ..." ;
+       if ( clip < input.length() )
+           return input.substring( 0, clip ) + "...";
+       return input;
+    }
+
+    /**
+     * Utility method to time parse and process.
+     */
+    public boolean timedProcess(int i,String goal,String input) {
+       try {
+           System.out.println(
+               "(" + i + ") " + goal + ": " + clip( input, 65 ) );
+           long time = System.currentTimeMillis();
+           ParseTree pr = parse( goal, input );
+           long snap = System.currentTimeMillis() - time;
+           if ( pr != null ) {
+               //System.out.println( "--> " + pr );
+               time = System.currentTimeMillis();
+               Object result = process( pr );
+               time = System.currentTimeMillis() - time;
+               System.out.println( "==> " + result );
+               System.out.println(
+                   "--------- time = " + snap + "/" + time + " ms" );
+               return true;
+           } else {
+               System.out.println( lastError( input ) );
+               System.out.println(
+                   "--------- time = " + snap + "/-- ms" );
+               return false;
+           }
+       } catch (Throwable t) {
+           t.printStackTrace();
+       }
+       return false;
+    }
+
+    /**
+     * Utility method to parse and process several tests.
+     */
+    public boolean processAll(String [][] args) {
+       
+       for ( int i=0; i<args.length; i++ ) {
+           if ( ! timedProcess( i, args[i][0], args[i][1] ) )
+               return false;
+       }
+       return true;
+    }
+
+}
diff --git a/com/intendico/sdp/Grammar.java b/com/intendico/sdp/Grammar.java
new file mode 100644 (file)
index 0000000..327bbdc
--- /dev/null
@@ -0,0 +1,1971 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.sdp;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.text.ParseException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * The Grammar class is used for defining a grammar.
+ */
+public class Grammar {
+
+    //
+    // Support methods for text processing.
+    //
+
+    /**
+     * Utility method to make a string from a named file.
+     */
+    static public String loadFile(String filename)
+       throws IOException {
+       FileReader f = new FileReader( filename );
+       char [] buffer = new char [ 1024 ];
+       int length;
+       StringBuffer s = new StringBuffer();
+       while ( ( length = f.read( buffer ) ) >= 0 ) {
+           if ( length > 0 )
+               s.append( buffer, 0, length );
+       }
+       f.close();
+       return s.toString();
+    }
+
+    private String strip(String a) {
+       Class [] c = { BNFGrammar.class, getClass() };
+       for ( int i=0; i < c.length; i++ ) {
+           if ( a.startsWith( c[i].getName() + "$" ) )
+               return a.substring( c[i].getName().length() + 1 );
+       }
+       return a;
+    }
+
+    /**
+     * Utility method to map a string index into a line number, by
+     * counting new-lines.
+     */
+    static public int lineNumber(CharSequence s,int index) {
+       int lineno = 1;
+       for ( int i = 0; i < index; i++ ) {
+           if ( s.charAt( i ) == '\n' ) {
+               lineno++;
+           }
+       }
+       return lineno;
+    }
+
+    /**
+     * Utility method that forms a string to mark out the indexed
+     * position in the input string.
+     */
+    static public String inputPosition(CharSequence input,int index) {
+       return inputPosition( input, index, 0 );
+    }
+    
+    /**
+     * Utility method that forms a string to mark out the indexed
+     * position and span in the input string.
+     */
+    static public String inputPosition(CharSequence input,int index,int span) {
+       if ( index < 0 )
+           return "" ;
+
+       int from = lastIndexOf( input, "\n", index - 1 ) + 1;
+       int to = indexOf( input, "\n", index );
+       boolean spandots = false;
+
+       if ( to < from )
+           to = input.length();
+
+       int lineno = lineNumber( input, from );
+       int at = index - from;
+
+       if ( span < 1 )
+           span = 1;
+
+       String line = lineno + ": ";
+
+       if ( span > 43 ) {
+           span = 40;
+           spandots = true;
+       }
+
+       if ( at + span > 60 ) {
+           // Adjust to fit on a line
+           int clip = at + span - 55;
+           at -= clip;
+           if ( at < 0 ) {
+               clip -= at;
+               at = 0;
+           }
+           from += clip;
+           line += "...";
+       }
+
+       if ( to - from > 70 )
+           to = from + 70;
+
+       at  += line.length();
+       line += input.subSequence( from, to ).toString();
+       if ( at + span > to )
+           span = to - at;
+
+       if ( span < 1 )
+            span = 1;
+
+       StringBuffer out = new StringBuffer( line );
+       out.append( "\n" );
+
+       for (int i=0; i<at; i++ ) {
+           if ( Character.isWhitespace( line.charAt( i ) ) ) {
+               out.append( line.charAt( i ) );
+           } else {
+               out.append( " " );
+           }
+       }
+
+       for (int i=0; i<span; i++ ) {
+           out.append( "^" );
+       }
+
+       if ( spandots )
+           out.append( "..." );
+
+       return out.toString();
+    }
+
+    /**
+     * Utility method to determine if an CharSequence starts with a given
+     * string at a given position.
+     */
+    static public boolean startsWith(CharSequence input,int from,String what) {
+       int end = from + what.length();
+       if ( end > input.length() )
+           return false;
+       return what.contentEquals( input.subSequence( from, end ) );
+    }
+
+    /**
+     * Returns the last occurence of a given string before a given index.
+     */
+    static public int lastIndexOf(CharSequence s,String what,int from) {
+       int endw = what.length();
+       if ( endw == 0 )
+           return from;
+       int end = s.length();
+       int end1 = end - endw;
+       char first = what.charAt( endw - 1  );
+       for ( from = from - 1; from >= endw; from-- ) {
+           if ( s.charAt( from ) != first )
+               continue;
+           for ( int p = from - 1, k = endw - 2; p >= 0; p--, k-- ) {
+               if ( k < 0 )
+                   return from;
+               if ( s.charAt( p ) != what.charAt( k ) )
+                   break;
+           }
+       }
+       return -1;
+    }
+
+    /**
+     * Returns the first occurence of a given string.
+     */
+    static public int indexOf(CharSequence s,String what,int from) {
+       int endw = what.length();
+       if ( endw == 0 )
+           return from;
+       int end = s.length();
+       int end1 = end - endw;
+       char first = what.charAt( 0 );
+       for ( ; from < end1; from++ ) {
+           if ( s.charAt( from ) != first )
+               continue;
+           for ( int p = from + 1, k = 1;; p++, k++ ) {
+               if ( k >= endw )
+                   return from;
+               if ( s.charAt( p ) != what.charAt( k ) )
+                   break;
+           }
+       }
+       return -1;
+    }
+
+    /**
+     * Utility method to scan past blank or comment.
+     */
+    static public int skipBlanksAndComments(CharSequence s,int from) {
+       int end = s.length();
+        for ( ; from < end; from++ ) {
+            if ( Character.isWhitespace( s.charAt( from ) ) )
+               continue;
+           if ( s.charAt( from ) != '/' )
+               return from;
+           if ( s.length() < 2 )
+               return from;
+           if ( s.charAt( from + 1 ) == '*' ) {
+               int x = indexOf( s, "*/", from );
+               if ( x < 0 ) {
+                   // Unterminated comment
+                   return end;
+               }
+               from = x + 1;
+               continue;
+           }
+           if ( s.charAt( from + 1 ) == '/' ) {
+               int x = indexOf( s, "\n", from );
+               if ( x < 0 ) {
+                   // Unterminated comment
+                   return end;
+               }
+               from = x;
+               continue;
+           }
+           return from;
+        }
+        return end;
+    }
+
+    /**
+     * The Parse interface represents the result from parsing, prior
+     * to semantic processing.
+     */
+    public interface ParseTree {
+
+       /**
+        * Returns the first character position spanned by this parse
+        * tree.
+        */
+       public int getFrom();
+
+       /**
+        * Returns the first significant character position within the
+        * region spanned by this parse tree.
+        */
+       public int getFirst();
+
+       /**
+        * Returns the first character position after the region
+        * spanned by this parse tree.
+        */
+       public int getTo();
+
+       /**
+        * Returns the grammar rule index for this result.
+        */
+       public int getIndex();
+
+       /**
+        * Returns the grammar rule for this result, if any.
+        */
+        public String getRule();
+
+       /**
+        * Returns the 'substance' portion of the span.
+        */
+       public String getText();
+
+       /**
+        * Returns the parsetree's generic syntax name, which is the
+        * rule name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName();
+
+       /**
+        * Utility method to form a string representation of this
+        * parse tree element, with an indentation argument.
+        */
+       public String toString(String head);
+
+       /**
+        * Forms a string representation of two lines to indicate the
+        * span of this result on the input line.
+        */
+       public String inputSpan();
+
+       /**
+        * Forms a string representation of the span region.
+        */
+       public String spanString();
+
+       /**
+        * Processing method for this parse tree.
+        */
+       public Object process() throws Throwable;
+
+       /**
+        * Processing method for the children of this parse tree.
+        */
+       public Vector processChildren() throws Throwable;
+    }
+
+    /**
+     * The Result class is for carrying parsing result.
+     */
+    public class Result implements ParseTree {
+
+       /**
+        * Refers to the input being parsed.
+        */
+       public final CharSequence input;
+
+       /**
+        * The character position in the input from where this Result
+        * object spans.
+        */
+       public final int from;
+
+       /**
+        * The first character position recognised as not whitespace
+        * or comment; the beginning of 'substance'.
+        */
+       public final int first;
+
+       /**
+        * The character position in the input to where this Result
+        * object spans. Thus, the span is input.substring(from,to).
+        */
+       public final int to;
+
+       /**
+        * Flag as to whether or not the text should be preserved upon
+        * semantic processing.
+        */
+       public final boolean preserve;
+
+       /**
+        * Returns the first character position spanned by this parse
+        * tree.
+        */
+       public int getFrom() {
+           return from;
+       }
+
+       /**
+        * Returns the first significant character position within the
+        * region spanned by this parse tree.
+        */
+       public int getFirst() {
+           return first;
+       }
+
+       /**
+        * Returns the first character position after the region
+        * spanned by this parse tree.
+        */
+       public int getTo() {
+           return to;
+       }
+
+       /**
+        * Constructor.
+        */
+       public Result(CharSequence s,int a,int b,int c) {
+           this( s, a, b, c, false );
+       }
+
+       /**
+        * Constructor.
+        */
+       public Result(CharSequence s,int a,int b,int c,boolean p) {
+           from = a;
+           first = b;
+           to = c;
+           input = s;
+           preserve = p;
+       }
+
+       /**
+        * Returns the 'substance' portion of the span.
+        */
+       public String getText() {
+           return input.subSequence( first, to ).toString();
+       }
+
+       /**
+        * Returns the grammar rule index for this result.
+        */
+       public int getIndex() {
+           return -1;
+       }
+
+       /**
+        * Returns the grammar rule for this result.
+        */
+       public String getRule() {
+           return null;
+       }
+
+       /**
+        * Returns the parsetree's generic syntax name, which is the
+        * rule name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName() {
+           return ":";
+       }
+
+       /**
+        * Returns the canonical token string for this result.
+        */
+       public String getToken() {
+           return "'" + getText();
+       }
+
+       /**
+        * Forms a String representaion of this Result object.
+        * The argument is ignored.
+        */
+       public String toString(String head) {
+           return toString();
+       }
+
+       /**
+        * Forms a string representation of the region spanned.
+        */
+       public String spanString() {
+           return "[" + from + "," + to + "]";
+       }
+
+       /**
+        * Presents this result bmo of an input context on one line,
+        * followed by a 'marker' for the span beneath it.
+        */
+       public String inputSpan() {
+           return inputPosition( input, first, to - first );
+       }
+
+       /**
+        * Forms a String representaion of this Result object.
+        */
+       public String toString() {
+           return "{" + from + "," + first + "," + to +
+               ( preserve? "=!'" : "='" ) +
+               getText() + "'}" ;
+       }
+
+       /**
+        * Default result processing returns null.
+        */
+       public Object process() throws Throwable {
+           if ( preserve )
+               return getText();
+           return null;
+       }
+
+       /**
+        * Processing method for the children of this parse tree.
+        */
+       public Vector processChildren() throws Throwable {
+           return null;
+       }
+    }
+
+    /**
+     * The ProductionResult class is for carrrying production rule
+     * result.
+     */
+    public class ProductionResult extends Result {
+
+       /**
+        * Tells the name of the non-terminal produced.
+        */
+       public String rule;
+
+       /**
+        * Tells the index for the production used.
+        */
+       public int index;
+
+       /**
+        * Holds the result objects of associated with production rule
+        * tokens.
+        */
+       public ParseTree [] results;
+
+       /**
+        * Returns the grammar rule index for this result.
+        */
+       public int getIndex() {
+           return index;
+       }
+
+       /**
+        * Returns the grammar rule for this result.
+        */
+       public String getRule() {
+           return rule;
+       }
+
+       /**
+        * Constructor intended for Lexical rules.
+        */
+       public ProductionResult(String r,CharSequence s,int a,int b,int c) {
+           super( s, a, b, c );
+           rule = r;
+           index = -1;
+       }
+
+       /**
+        * General constructor.
+        */
+       public ProductionResult(String r,CharSequence s,int i,ParseTree [] rs) {
+           this( r, s,
+                 rs == null? 0 : rs[0].getFrom(),
+                 rs == null? 0 : rs[0].getFirst(),
+                 rs == null? -1 : rs[ rs.length - 1 ].getTo() );
+           index = i;
+           results = rs;
+       }
+
+       /**
+        * Returns the parsetree's generic syntax name, which is the
+        * rule name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName() {
+           return rule;
+       }
+
+       /**
+        * Returns the canonical token string for this result.
+        */
+       public String getToken() {
+           return "<" + getRule() + ">";
+       }
+
+       /**
+        * Forms a string representation of the parse tree.
+        */
+       public String toString() {
+           return toString( "" );
+       }
+
+       /**
+        * Forms a string representation of the parse tree, using the
+        * given string as indentation prefix.
+        */
+       public String toString(String head) {
+           StringBuffer s = new StringBuffer();
+           s.append( rule );
+           s.append( ":" );
+           s.append( index );
+           s.append( "[" );
+           head += " ";
+           if ( results == null ) {
+               s.append( super.toString() );
+           } else {
+               for ( int i=0; i<results.length; i++ ) {
+                   if ( i > 0 ) {
+                       s.append( "," );
+                   }
+                   s.append( "\n" );
+                   s.append( head );
+                   s.append( results[i].toString( head ) );
+               }
+           }
+           s.append( "]" );
+           return s.toString();
+       }
+
+       /**
+        * Default result processing invokes the process method of the
+        * syntax rule.
+        */
+       public Object process() throws Throwable {
+           try {
+               return getDefinition( rule ).process( this );
+           } catch (Throwable e) {
+               throw hideParse( e );
+           }
+       }
+
+       /**
+        * Utility method to process the children
+        */
+       public Vector processChildren() throws Throwable {
+           Vector v = new Vector();
+           if ( results != null ) {
+               for ( int i=0; i<results.length; i++ ) {
+                   Object x = results[i].process();
+                   if ( x != null )
+                       v.add( x );
+               }
+           }
+           return v;
+       }
+    }
+
+
+    /**
+     * The non-terminals.
+     */
+    public TreeMap rules = new TreeMap();
+
+    //
+    // Inner interfaces to support concept generalisation.
+    // These are: Rule, Token and ErrorToken.
+    //
+
+    /**
+     * The Rule interface is for all kinds of syntax definitions.
+     * Presently there are only the two kinds of ProductionList and
+     * Lexical.
+     */
+    public interface Rule {
+
+       /**
+        * Returns a ParseTree object for successful parse, or
+        * null.
+        */
+       public ParseTree parse(CharSequence s,int from) throws ParseException;
+
+       /**
+        * Returns the rule name; the non-terminal defined.
+        */
+       public String getName();
+
+       /**
+        * Performs synthesising operations on a ParseTree
+        * parse tree, and returns a value representing the parse tree
+        * value.
+        */
+       public Object process(ParseTree pr) throws Throwable;
+    }
+
+    //
+    // The parsing elements.
+    //
+
+    /**
+     * Bit field for stderr tracing of parser operation.
+     */
+    public int trace = 0;
+
+    /**
+     * A counter of entries to syntax rules; "attempts"
+     */
+    public int trace_count = 0;
+
+    public static int TRACE_TERMINALS = 1;
+    public static int TRACE_PARSE = 2;
+    public static int TRACE_RECURSION = 4;
+    public static int TRACE_ACTION = 8;
+    public static int TRACE_OPTIONS = 16;
+    public static int TRACE_LINK = 32;
+    public static int TRACE_STACK = 64;
+
+    public static int REUSE_ALWAYS = 0;
+    public static int REUSE_RECURSIVE = 1;
+    public static int REUSE_RARELY = 2;
+    public int reuse_mode = 0;
+    /**
+     * Trace variable.
+     */
+    public int current_from = 0;
+
+    /**
+     * Utility method for tracing.
+     */
+    public void log(CharSequence input,int from,String rule,String m) {
+       ParseTree last = last( from, rule );
+
+       String text = "At " + from + ", " + rule + " " +
+           ( last != null? last.spanString() : "[null]" ) + " - " + m ;
+
+       if ( from != current_from && input != null ) {
+           System.err.println();
+           int span = current_from > from? current_from - from : 0;
+           text = inputPosition( input, from, span ) + "\n" + text;
+       }
+       current_from = from;
+
+       System.err.println( text );
+    }
+
+    /**
+     * Utility method to test trace mode.
+     */
+    public boolean tracing(int mode) {
+       return ( trace & mode ) != 0;
+    }
+
+    /**
+     * Table of successful parse results.
+     */
+    public Hashtable results = null;
+
+    /**
+     * Table of known failed parse postions.
+     */
+    public HashSet failed = null;
+
+    /**
+     * Current parsing level.
+     */
+    public int level = 0;
+
+    /**
+     * Maps positions to tried lexical rules and terminals
+     */
+    public TreeMap errors;
+
+    /**
+     * Support method to create a hash key for a rule at a position.
+     */
+    public String key(int from,String rule) {
+       return rule + " at " + from ;
+    }
+
+    /**
+     * Support method to create a hash key for a rule at a position.
+     */
+    public String key(int from,String rule,int index) {
+       return rule + ":" + index + " at " + from;
+    }
+
+    /**
+     * Returns the last parse result for a rule at a position.
+     */
+    public ParseTree last(int from,String rule) {
+       if ( results == null )
+           return null;
+       return (ParseTree) results.get( key( from, rule ) );
+    }
+
+    /**
+     * Support method to set the parse result for a rule at a position.
+     */
+    public void saveLast(int from,String rule,ParseTree x) {
+       if ( x == null )
+           return;
+       if ( results == null )
+           results = new Hashtable();
+       results.put( key( from, rule ), x );
+    }
+
+    /**
+     * Support method to remove the parse result for a rule at a
+     * position.
+     */
+    public void clearLast(int from,String rule) {
+       if ( results != null )
+           results.remove( key( from, rule ) );
+    }
+
+    /**
+     * Tells whether a parse position is known to be failed.
+     */
+    public boolean isFailed(int from,String rule) {
+       return failed != null && failed.contains( key( from, rule ) );
+    }
+
+    /**
+     * Marks a parse position as failed.
+     */
+    public void setFailed(int from,String rule) {
+       if ( failed == null )
+           failed = new HashSet();
+       failed.add( key( from, rule ) );
+    }
+
+    /**
+     * Unmarks a parse position as failed.
+     */
+    public void clearFailed(int from,String rule) {
+       if ( failed != null )
+           failed.remove( key( from, rule ) );
+    }
+
+    /**
+     * Tells whether a parse position is known to be failed.
+     */
+    public boolean isFailed(int from,String rule,int index) {
+       return failed != null && failed.contains( key( from, rule, index ) );
+    }
+
+    /**
+     * Marks a parse option position as failed.
+     */
+    public void setFailed(int from,String rule,int index) {
+       if ( failed == null )
+           failed = new HashSet();
+       failed.add( key( from, rule, index ) );
+    }
+
+    /**
+     * Unmarks a parse position as failed.
+     */
+    public void clearFailed(int from,String rule,int index) {
+       if ( failed != null )
+           failed.remove( key( from, rule, index ) );
+    }
+
+    //
+    // Error handling...
+    //
+
+    /**
+     * Support method to log an attempted lexical match.
+     */
+    public void tryToken(ErrorToken token,int from) {
+       if ( errors == null )
+           errors = new TreeMap(
+               new Comparator() {
+                   public int compare(Object a,Object b)
+                   {
+                       return ((Integer) a).compareTo( (Integer) b );
+                   }
+               } );
+       Integer i = new Integer( from );
+       TreeSet t = (TreeSet) errors.get( i );
+       if ( t == null ) {
+           t = new TreeSet( new Comparator() {
+                   public int compare(Object a,Object b) {
+                       return compare( (ErrorToken) a, (ErrorToken) b );
+                   }
+
+                   public int compare(ErrorToken a,ErrorToken b) {
+                       return a.errorName().compareTo( b.errorName() );
+                   }
+               } );
+           errors.put( i, t );
+       }
+       t.add( token );
+    }
+
+    /**
+     * Report all error tokens.
+     */
+    public String allErrors() {
+       StringBuffer s = new StringBuffer();
+       for ( Iterator i = errors.keySet().iterator(); i.hasNext(); ) {
+           Integer index = (Integer) i.next();
+           TreeSet list = getErrors( index.intValue() );
+           s.append( "Pos " );
+           s.append( index.toString() );
+           s.append( ":" );
+           for ( Iterator e = list.iterator(); e.hasNext(); ) {
+               s.append( " " );
+               s.append( (String) e.next() );
+           }
+           s.append( "\n" );
+       }
+       return s.toString();
+    }
+
+    /**
+     * Utility method that presents the deepest error in format.
+     * <lineno>: <line>
+     *             ^
+     * Expected: ...
+     */
+    public String lastError(CharSequence input) {
+       return errors == null? null : lastError( input, lastErrorPosition() );
+    }
+
+    public int lastErrorPosition() {
+       if ( errors == null )
+           return -1;
+       return ((Integer) errors.lastKey()).intValue();
+    }
+
+    /**
+     * Returns a string of the error names produced by the indicated
+     * error tokens.
+     */
+    public TreeSet getErrors(int from) {
+       return (TreeSet) errors.get( new Integer( from ) );
+    }
+
+    /**
+     * Utility method that presents the deepest error in format.
+     * <lineno>: <line>
+     *             ^
+     * Expected: ...
+     */
+    public String lastError(CharSequence input,int from) {
+       StringBuffer out = new StringBuffer();
+       out.append( inputPosition( input, from ) );
+       out.append( " [" + from + "]" );
+       out.append( "\nExpected: " );
+
+       TreeSet set = getErrors( from );
+
+       int at = set.size();
+       int i = at;
+
+       for ( Iterator e = set.iterator(); e.hasNext(); i--) {
+           if ( i == 1 ) {
+               if ( at > 1 )
+                   out.append( " or " );
+           } else {
+               if ( i != at )
+                   out.append( ", " );
+           }
+           out.append( ((ErrorToken) e.next()).errorName() );
+       }
+       out.append( "." );
+       return out.toString();
+    }
+
+    /**
+     * The ErrorToken interface is for producing error token name.
+     */
+    public interface ErrorToken {
+
+       /**
+        * Returns the token name.
+        */
+       public String errorName();
+    }
+
+
+    //
+    // Parsing elements
+    //
+
+    /**
+     * A ProductionList defines a rule by a list of alternative
+     * productions.
+     */
+    public class ProductionList implements Rule {
+
+       /**
+        * The rule defined by the productions.
+        */
+       public final String rule;
+
+       /**
+        * The list of optional productions for this rule.
+        */
+       public final Production [] options;
+
+       /**
+        * Constructor.
+        */
+       public ProductionList(String r,Production [] list) {
+           rule = r;
+           options = list;
+           if ( list != null ) {
+               for ( int i=0; i<list.length; i++ ) {
+                   list[i].setRule( r, i );
+               }
+           }
+       }
+
+       /**
+        * Returns the name of the non-terminal defined.
+        */
+       public String getName() {
+           return rule;
+       }
+
+       int usage = -1;
+
+       /**
+        * The default parsing of a ProductionList rule is to apply a
+        * modified recursive decent algorithm, which allows
+        * left-recursive as grammars.
+        */
+       public ParseTree parse(CharSequence input,int from)
+           throws ParseException {
+           if ( options == null ) {
+               // Missing syntax definition
+               throw new ParseException(
+                   "<" + rule + "> is not defined.", from );
+           }
+
+           boolean recursive = usage == from; // Re-entering this rule
+
+           ParseTree last = last( from, rule );
+           ParseTree result = null;
+
+           boolean reuse = reuse_mode < 2 && last != null &&
+               ( reuse_mode == 0 || recursive );
+
+           if ( reuse ) {
+               if ( tracing( TRACE_OPTIONS ) )
+                   log( input, from, rule,
+                        "re-using last (mode=" + reuse_mode + ")" );
+               return last;
+           }
+
+           int prior_usage = usage;
+           usage = from;
+
+           boolean again = false;
+
+           try {
+               do { 
+                   trace_count ++;
+                   again = false;
+                   result = null;
+                   for (int i=0; result == null && i<options.length; i++ ) {
+                       if ( tracing( TRACE_OPTIONS ) )
+                           log( input, from, rule, "check option " + i );
+                       result = options[i].parse( input, from );
+                   }
+                   if ( result == null ) {
+                       if ( tracing( TRACE_OPTIONS ) )
+                           log( input, from, rule, "all options failed" );
+                       result = last;
+                       again = false;
+                   } else {
+                       if ( last == null || last.getTo() < result.getTo() ) {
+                           saveLast( from, rule, result );
+                           if ( tracing( TRACE_OPTIONS ) )
+                               log( input, from, rule, "new result" );
+                           again = true;
+                           last = result;
+                       } else {
+                           if ( tracing( TRACE_OPTIONS ) )
+                               log( input, from, rule, "old result" );
+                           result = last;
+                           again = false;
+                       }
+                   }
+               } while ( again );
+           } finally {
+               usage = prior_usage;
+           }
+
+           return result;
+       }
+
+       /**
+        * Forms a string representation of this rule.
+        */
+       public String toString() {
+           if ( options == null ) {
+               // Missing syntax definition
+               return rule + " IS NOT DEFINED";
+           }
+
+           StringBuffer s = new StringBuffer();
+
+           s.append( rule );
+           s.append( " ::= " );
+           for ( int i=0; i<options.length; i++ ) {
+               if ( i > 0 )
+                   s.append( "\n    | " );
+               s.append( options[i].toString() );
+           }
+           s.append( "\n" );
+           return s.toString();
+       }
+
+       /**
+        * Default rule processing applies the option action to the
+        * processed children.
+        */
+       public Object process(ParseTree pr) throws Throwable {
+           try { 
+               return options[ pr.getIndex() ].
+                   action( pr, pr.processChildren() );
+           } catch (Throwable t) {
+               throw hideParse( t );
+           }
+       }
+
+    }
+
+    /**
+     * Represents a production alternative.
+     */
+    public class Production {
+
+       /**
+        * The production rule tokens.
+        */
+       public Token [] tokens;
+
+       /**
+        * Set to mark where this rule is attempted
+        */
+       public int usage = -1;
+
+       /**
+        * Set to the non-terminal that this is a production for, if
+        * any.
+        */
+       public String rule = null;
+
+       /**
+        * Help variable used for tracing purposes.
+        */
+       private String rulex;
+
+       /**
+        * Set to be the option index for the rule the Production
+        * belongs to.
+        */
+       public int index = -1;
+
+       /**
+        * Constructor.
+        */
+       public Production() {
+       }
+
+       /**
+        * Alternative constructor.
+        */
+       public Production(Token [] t) {
+           tokens = t;
+       }
+
+       /**
+        * Utility method to set the tokens of the production.
+        */
+       public void setTokens(Token [] t) {
+           tokens = t;
+       }
+
+       /**
+        * Utility method to set the rule name.
+        */
+       public void setRule(String r,int i) {
+           rule = r;
+           index = i;
+           rulex = rule + ":" + i;
+       }
+
+       /**
+        * Utility variable to hold the key string.
+        */
+       public String key = null;
+
+       /**
+        * Returns the key string; computes it once first.
+        */
+       public String getKey() {
+           if ( key == null )
+               key = toString();
+           return key;
+       }
+
+       /**
+        * Equality method is based on production string.
+        */
+       public boolean equals(Object x) {
+           return x instanceof Production &&
+               getKey().equals( ((Production) x).getKey() );
+       }
+
+       /**
+        * Hash code method; uses the key string.
+        */
+       public int hashCode() {
+           return getKey().hashCode();
+       }
+
+       /**
+        * Returns the number of tokens.
+        */
+       public int length() {
+           return tokens == null? -1 : tokens.length;
+       }
+
+       boolean recursive;
+
+       /**
+        * The default parsing of a Production is to try the tokens
+        * from left to right.
+        */
+        public ParseTree parse(CharSequence input,int from)
+            throws ParseException {
+           if ( tokens == null ) {
+               throw new ParseException(
+                   rule + ":" + index + " has no tokens", from );
+           }
+
+           recursive = usage == from;
+
+           if ( usage == from ) {
+               // Reentering this Production recursively.
+               if ( tokens.length > 1 ) {
+                   if ( tracing( TRACE_OPTIONS ) )
+                       log( input, from, rulex, "recursive fail");
+                   return null;
+               }
+               // Recursive singleton
+               if ( tracing( TRACE_OPTIONS ) )
+                   log( input, from, rulex, "recursive singleton");
+               return null;
+           }
+
+           if ( tracing( TRACE_OPTIONS ) )
+               log( input, from, rulex, "previous usage = " + usage );
+
+           int prior = usage;
+           usage = from;
+
+           try {
+               ParseTree [] results = new ParseTree [ tokens.length ];
+               ParseTree result = null;
+               for ( int i=0; i<tokens.length; i++ ) {
+                   results[i] = tokens[i].parse( input, from );
+                   if ( results[i] == null ) {
+                       if ( tracing( TRACE_OPTIONS ) )
+                           log( input, from, rulex,
+                                "failed at token " + tokens[i] );
+                       return null;
+                   }
+                   usage = -1;
+                   from = results[i].getTo() ;
+               }
+               result = new ProductionResult( rule, input, index, results );
+
+               if ( tracing( TRACE_OPTIONS ) )
+                   log( input, prior, rulex,
+                        "result = " + result.spanString() );
+               return result;
+           } finally {
+               usage = prior;
+           }
+
+       }
+
+       /**
+        * Forms a string representation of this Production.
+        */
+       public String toString() {
+           StringBuffer s = new StringBuffer();
+
+           for ( int i=0; i<tokens.length; i++ ) {
+               s.append( tokens[i].toString() );
+               s.append( " " );
+           }
+           String actual = getClass().getName();
+           if ( ! actual.equals( Production.class.getName() ) ) {
+               s.append( "# " );
+               s.append( strip( actual ) );
+               s.append( " " );
+           }
+            return s.toString();
+        }
+
+       /**
+        * Default option action is to call action(Vector), and
+        * thereby ignore the source position.
+        */
+       public Object action(ParseTree pr,Vector v) throws Throwable {
+           try {
+               if ( tracing( TRACE_ACTION ) )
+                   System.err.println(
+                       "action " + pr.getRule() + ":" + pr.getIndex() +
+                       " with " + v.size() + " arguments" );
+               return action( v );
+           } catch (Throwable t) {
+               t = new Exception(
+                   t + "\n" + pr.inputSpan() + " " + pr.spanString(), t );
+               t.setStackTrace( new StackTraceElement [0] );
+               throw t;
+           }
+       }
+
+       /**
+        * Default Production action is to return the first argument
+        * if there is a single one, otherwise the whole argument
+        * vector.
+        */
+       public Object action(Vector v) throws Exception {
+           if ( tracing( TRACE_ACTION ) )
+               System.err.println( "default Production action with " +
+                                   v.size() + " arguments" );
+           if ( v.size() == 0 )
+               return null;
+           if ( v.size() == 1 )
+               return v.elementAt( 0 );
+           return v;
+       }
+    }
+
+    /**
+     * The Token interface is for production rule tokens.
+     */
+    public interface Token {
+
+       /**
+        * Returns the token name.
+        */
+       public String getName();
+
+       /**
+        * Returns the token's generic syntax name, which is the rule
+        * name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName();
+
+       /**
+        * Returns a parse Result object, or null
+        */
+       public ParseTree parse(CharSequence s,int from) throws ParseException;
+
+    }
+
+    /**
+     * A Terminal is a symbol to find verbatim.
+     */
+    public class Terminal implements Token, ErrorToken {
+
+       /**
+        * The symbol to find.
+        */
+       public final String text;
+
+       /**
+        * The key symbol to use for this token.
+        */
+       public final String ttext;
+
+       /**
+        * Flag to mark whether or not this terminal token is visible
+        * in semantics.
+        */
+       public final boolean preserve;
+
+       /**
+        * Flag to mark whether or not this terminal token is visible
+        * in semantics.
+        */
+       public final boolean optional;
+
+       /**
+        * Constructor.
+        */
+       public Terminal(String t) {
+           this( t, false, false );
+       }
+
+       /**
+        * Constructor.
+        */
+       public Terminal(String t,boolean p) {
+           this( t, p, false );
+       }
+
+       /**
+        * Constructor.
+        */
+       public Terminal(String t,boolean p,boolean opt) {
+           text = t;
+           preserve = p;
+           optional = opt;
+           if ( optional )
+               ttext = "?" + text;
+           else if ( preserve )
+               ttext = "!" + text;
+           else
+               ttext = "'" + text;
+       }
+
+       /**
+        * Equality for terminals are measured on their name strings.
+        */
+       public boolean equals(Object x) {
+           return x instanceof Terminal &&
+               getName().equals( ((Terminal) x).getName() ) ;
+       }
+
+       /**
+        * Hash code method; uses the key string.
+        */
+       public int hashCode() {
+           return getName().hashCode();
+       }
+
+       /**
+        * Returns the rule name; the non-terminal defined.
+        */
+       public String getName() {
+           return text;
+       }
+
+       /**
+        * Returns the token's generic syntax name, which is the rule
+        * name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName() {
+           return ":";
+       }
+
+       /**
+        * The default parsing of a terminal token is to match it
+        * against the current position, after ignoring whitespace and
+        * comments.
+         */
+        public ParseTree parse(CharSequence s,int from) throws ParseException {
+           ParseTree result = last( from, ttext );
+           if ( result != null )
+               return result;
+           if ( isFailed( from, ttext ) )
+               return null;
+           tryToken( this, from );
+           if ( tracing( TRACE_TERMINALS ) )
+               System.err.println( "Find " + text + " at pos " + from );
+           int first = skipBlanksAndComments( s, from );
+           if ( ! startsWith( s, first, text ) ) {
+               if ( optional ) {
+                   if ( tracing( TRACE_TERMINALS ) )
+                       System.err.println(
+                           "Found omitted optional " + text +
+                           " at pos " + first );
+                   result = new Result(
+                       s, from, first, first, false );
+                   saveLast( from, "", result );
+                   return result;
+               } else {
+                   setFailed( from, ttext );
+                   return null;
+               }
+           }
+           if ( tracing( TRACE_TERMINALS ) )
+               System.err.println( "Found " + text + " at pos " + first );
+           result = new Result(
+               s, from, first, first + text.length(), preserve );
+           saveLast( from, ttext, result );
+           return result;
+       }
+
+       /**
+        * Forms a string representation of this terminal token.
+        */
+       public String toString() {
+           return ttext ;
+       }
+
+       /**
+        * The standard error name for a terminal token.
+        */
+       public String errorName() {
+           return "'" + text + "'" ;
+       }
+    }
+
+    /**
+     * A NonTerminal is a reference to a syntax rule.
+     */
+    public class NonTerminal implements Token {
+
+       /**
+        * Holds the name of the non-terminal.
+        */
+       public final String rule;
+
+       /**
+        * Constructor.
+        */
+       public NonTerminal(String r) {
+           rule = r;
+       }
+
+       /**
+        * Returns the rule name; the non-terminal defined.
+        */
+       public String getName() {
+           return rule;
+       }
+
+       /**
+        * Returns the token's generic syntax name, which is the rule
+        * name for production syntax, and ":" otherwise.
+        */
+       public String getSyntaxName() {
+           return getDefinition( rule ) instanceof ProductionList?
+               rule : ":" ;
+       }
+
+       /**
+        * The default parsing of a non-terminal token is to defer to
+        * apply its definition to the current input position.
+        */
+        public ParseTree parse(CharSequence input,int from)
+            throws ParseException {
+           try {
+               if ( tracing( TRACE_PARSE ) ) {
+                   log( input, from, rule, ">>-- " + (++level) + " -->>" );
+               }
+
+               Rule r = getDefinition( rule );
+               if ( r == null ) {
+                   throw new ParseException(
+                       "<" + rule + "> is not defined.", from );
+               }
+
+               ParseTree result = r.parse( input, from );
+
+               if ( result == null ) {
+                   if ( tracing( TRACE_PARSE ) )
+                       log( input, from, rule,
+                            "<<-- " + (level--) + " --<< failed" );
+                   return null;
+               }
+
+               if ( tracing( TRACE_PARSE ) )
+                   log( input, from, rule, "<<-- " + (level--) + " --<< " +
+                        result.spanString() );
+
+               return result;
+           } catch (ParseException e) {
+               throw (ParseException) hideParse( e );
+           } catch (Throwable e) {
+               ParseException pe = new ParseException( e.toString(), from );
+               pe.initCause( e );
+               throw pe;
+           }
+       }
+
+       /**
+        * Forms a string representation of a non-terminal token.
+        */
+       public String toString() {
+            return "<" + rule + ">" ;
+        }
+    }
+
+    /**
+     * The Lexical class is a base class for lexical tokens.
+     */
+    abstract public class Lexical implements Rule, ErrorToken {
+
+       /**
+        * The rule name.
+        */
+       final public String rule;
+
+       /**
+        * Constructor.
+        */
+       public Lexical(String r) {
+           rule = r;
+       }
+
+       /**
+        * Returns the rule name.
+        */
+       public String getName() {
+           return rule;
+       }
+
+       /**
+        * Equality for terminals are measured on their error name
+        * strings. 
+        */
+       public boolean equals(Object x) {
+           return x instanceof Lexical &&
+               errorName().equals( ((Lexical) x).errorName() ) ;
+       }
+
+       /**
+        * Hash code method; uses the key string.
+        */
+       public int hashCode() {
+           return errorName().hashCode();
+       }
+
+       /**
+        * Handle token ingress, which typically means to skip
+        * whitespace.
+        */
+       public int ingress(CharSequence input,int from) {
+           return skipBlanksAndComments( input, from );
+       }
+
+       /**
+        * The default parsing of a lexical rule: skips blanks and
+        * comments, and then invokes scan.
+        */
+       public ParseTree parse(CharSequence input,int from)
+           throws ParseException {
+           ParseTree last = last( from, rule );
+           if ( isFailed( from, rule ) ) {
+               if ( tracing( TRACE_OPTIONS ) )
+                    log( input, from, rule, "lexical known failed" );
+               return null;
+           }
+           if ( last != null ) {
+               if ( tracing( TRACE_OPTIONS ) )
+                   log( input, from, rule,
+                        "lexical reuse " + last.spanString() );
+               return last;
+           }
+           tryToken( this, from );
+           int first = ingress( input, from );
+           int to = scan( input, first );
+           if ( to < 0 ) {
+               setFailed( from, rule );
+               if ( tracing( TRACE_OPTIONS ) )
+                   log( input, from, rule, "lexical failed" );
+               return null;
+           }
+           last = new ProductionResult( rule, input, from, first, to );
+           saveLast( from, rule, last );
+           if ( tracing( TRACE_OPTIONS ) )
+               log( input, from, rule, "lexical success" );
+           return last;
+       }
+
+       /**
+        * The lexical function, which returns the end of the span
+        * covered by the lexical rule, or -1. The method is given the
+        * input string and the position to scan, which is neither
+        * whitespace nor in a comment.
+        */
+       abstract public int scan(CharSequence s,int from);
+
+       /**
+        * Forms a string representation of this lexical rule.
+        */
+       public String toString() {
+           return
+               rule + " is lexical " + strip( getClass().getName() ) + "\n" ;
+       }
+
+       /**
+        * The standard error name for a lexical rule.
+        */
+       public String errorName() {
+           return "<" + rule + ">";
+       }
+
+       /**
+        * Semantic processing for a lexical will return the matched
+        * string unless it's an empty string.
+        */
+       public Object process(ParseTree pr) {
+           return pr.getFirst() == pr.getTo()? null : pr.getText();
+       }
+    }
+
+    /**
+     * The Link class is a base class for rules that link to
+     * other grammars.
+     */
+    public class Link implements Rule {
+
+       /**
+        * The grammar linked to.
+        */
+       public final Grammar grammar;
+
+       /**
+        * The name of the non-terminal in this grammar.
+        */
+       public final String rule;
+
+       /**
+        * The corresponding name in the linked grammar.
+        */
+       public final String goal;
+
+       /**
+        * Constructor.
+        */
+       public Link(String r,String g,Grammar gr) {
+           grammar = gr;
+           rule = r;
+           goal = g;
+       }
+
+       /**
+        * Constructor. Alternative constructor using the same rule
+        * name in both grammars.
+        */
+       public Link(String r,Grammar gr) {
+           this( r, r, gr );
+       }
+
+       /**
+        * Returns the 'local' rule name
+        */
+       public String getName() {
+           return rule;
+       }
+
+       /**
+        * Parse input, save result, and return true if successful.
+        */
+       public ParseTree parse(CharSequence input,int from)
+           throws ParseException {
+           if ( grammar.tracing( TRACE_LINK ) )
+               grammar.log( input, from, rule, "link: " + goal );
+           int trace_start = grammar.trace_count;
+
+           ParseTree p = grammar.parse( goal, input, from );
+
+           trace_count += grammar.trace_count - trace_start;
+           int index = grammar.lastErrorPosition();
+           TreeSet t = grammar.getErrors( index );
+           for ( Iterator i = t.iterator(); i.hasNext(); )
+               tryToken( (ErrorToken) i.next(), index );
+
+           if ( p != null ) {
+               if ( tracing( TRACE_LINK ) )
+                   log(
+                       input, from, rule, "link return: " + goal + " = " +
+                       p.spanString() + " with last error at " + index );
+           } else {    
+               if ( tracing( TRACE_LINK ) )
+                   log(
+                       input, from, rule,
+                       "link return: " + goal + " failed" );
+           }
+           return p;
+       }
+
+       /**
+        * Semantic processing for a Link
+        */
+       public Object process(ParseTree pr) throws Throwable {
+           // The parse tree is contained in the other grammar.
+           return grammar.process( pr );
+       }
+
+       public String toString() {
+           String pkg = Grammar.this.getClass().getName();
+           pkg = pkg.substring( 0, pkg.lastIndexOf( "." ) + 1 );
+           String name = grammar.getClass().getName();
+           if ( name.startsWith( pkg ) )
+               name = name.substring( pkg.length() );
+           return 
+               rule + " is linked to <" + goal + "> in " + name + "\n";
+       }
+    }
+
+    //
+    // Additional Grammar methods
+    //
+
+    /**
+     * Constructor. Invokes the initialisation() method.
+     */
+    public Grammar() {
+       initialisation();
+    }
+
+    /**
+     * Overridable method to perform grammar initialisation.
+     */
+    public void initialisation() {
+    }
+
+    /**
+     * Utility method to add a syntax rule.
+     */
+    public void addRule(Rule rule) {
+       rules.put( rule.getName(), rule );
+    }
+
+    /**
+     * Applies a syntax rule to a string.
+     */
+    public ParseTree parse(String rule,CharSequence input)
+       throws ParseException {
+       for ( Iterator i = getGrammars().iterator(); i.hasNext(); ) {
+           Grammar g = (Grammar) i.next();
+           g.reset();
+       }
+       return parse( rule, input, 0 );
+    }
+
+    /**
+     * Resets the parsing machinery.
+     */
+    public void reset() {
+       errors = null;
+       results = null;
+       failed = null;
+       trace_count = 0;
+    }
+
+    /**
+     * Applies a syntax rule to a string.
+     */
+    public ParseTree parse(String rule,CharSequence input,int from)
+       throws ParseException {
+       Rule r = getDefinition( rule );
+       if ( r == null )
+           throw new ParseException(
+               "<" + rule + "> is not defined.", from );
+       return r.parse( input, from );
+    }
+
+    /**
+     * Utility method to change the stack trace of an exception by
+     * removing all Grammar elements.
+     */
+    public Throwable hideParse(Throwable e) {
+       return hideParse( e, Grammar.class.getName() );
+    }
+
+    /**
+     * Utility method to change the stack trace of an exception by
+     * removing all elements with agiven class name prefix.
+     */
+    public Throwable hideParse(Throwable e,String prefix) {
+       if ( tracing( TRACE_STACK ) )
+           return e;
+
+       for ( Throwable t = e; t != null; t = t.getCause() ) {
+           StackTraceElement[] stack = t.getStackTrace();
+           Vector v = new Vector();
+           for ( int i=0; i<stack.length; i++ ) {
+               String s = stack[i].getClassName();
+               if ( ! ( s.startsWith( prefix ) ||
+                        s.startsWith( "java." ) ||
+                        s.startsWith( "sun." ) ) )
+                   v.add( stack[i] );
+           }
+           t.setStackTrace( (StackTraceElement[]) v.toArray(
+                                new StackTraceElement [ v.size() ] ) );
+       }
+       return e;
+    }
+
+    public Rule getDefinition(String rule) {
+       return (Rule) rules.get( rule );
+    }
+
+    /**
+     * Utility method to process a parse, and catch exception
+     */
+    public Object process(ParseTree pr) throws Throwable {
+       return pr.process();
+    }
+
+    /**
+     * Applies a syntax rule to a string and performs its semantics.
+     */
+    public Object parseAndProcess(String rule,String input)
+       throws Throwable {
+       ParseTree p = parse( rule, input );
+       if ( p != null )
+           return process( p );
+       System.err.println( lastError( input ) );
+       return null;
+    }
+
+    /**
+     * Returns a TreeSet of all included grammars, sorted by class
+     * name.
+     */
+    public Vector getGrammars() {
+       return getGrammars( null );
+    }
+
+    /**
+     * Collect all grammars involved, by linking.
+     */
+    private Vector getGrammars(Vector v) {
+       Vector links = v != null? v : new Vector();
+
+       links.add( this );
+
+       for ( Iterator i = rules.keySet().iterator(); i.hasNext(); ) {
+           Rule r = getDefinition( (String) i.next() );
+           if ( r instanceof Link ) {
+               Link link = (Link) r;
+               if ( ! links.contains( link.grammar ) )
+                   link.grammar.getGrammars( links );
+           }
+       }
+       return links;
+    }
+
+    /**
+     * Utility method for text representation.
+     */
+    public String toStringAlone() {
+       StringBuffer s = new StringBuffer();
+       for ( Iterator i = rules.keySet().iterator(); i.hasNext(); ) {
+           Rule r = getDefinition( (String) i.next() );
+           s.append( r.toString() );
+       }
+       return s.toString();
+    }
+
+    /**
+     * Textual representation of a grammar.
+     */
+    public String toString() {
+       Vector t = getGrammars( null );
+       StringBuffer s = new StringBuffer();
+       boolean rest = false;
+       for ( Iterator i = t.iterator(); i.hasNext(); ) {
+           Grammar g = (Grammar) i.next();
+           if ( rest )
+               s.append( "\n" );
+           rest = true;
+           s.append( "/*** Definitions of grammar " );
+           s.append( g.getClass().getName() );
+           s.append( " follow ***/\n" );
+           s.append( g.toStringAlone() );
+       }
+       return s.toString();
+    }
+
+    //
+    // Auxillary support for 'remoteHCyl2 semantics'. These are used by the
+    // action tagging of LLBNF.
+    //
+
+    /**
+     * An interface for production rule actions.
+     */
+    public interface RuleAction {
+        public Object action(Vector v)
+            throws Exception;
+    }
+
+    /**
+     * A table of tagged rule actions.
+     */
+    Hashtable rule_actions = new Hashtable();
+
+    /**
+     * Utility method for defining rule actions.
+     */
+    public void setAction(String tag,RuleAction r) {
+       rule_actions.put( tag, r );
+    }
+
+    /**
+     * Utility method for defining rule actions.
+     */
+    public void setAction(Class c,String tag,RuleAction r) {
+       for ( Iterator i = getGrammars().iterator(); i.hasNext(); ) {
+           Grammar g = (Grammar) i.next();
+           if ( c.isInstance( g ) )
+               g.rule_actions.put( tag, r );
+       }
+    }
+}
+
diff --git a/com/intendico/sdp/LLBNF.java b/com/intendico/sdp/LLBNF.java
new file mode 100644 (file)
index 0000000..9f0a352
--- /dev/null
@@ -0,0 +1,474 @@
+/*********************************************************************
+Copyright 2012, Ralph Ronnquist.
+
+This file is part of GORITE.
+
+GORITE is free software: you can redistribute it and/or modify it
+under the terms of the Lesser GNU General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GORITE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the Lesser GNU General Public
+License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+
+package com.intendico.sdp;
+
+import java.lang.reflect.Constructor;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * Class LLBNF defines a standard recursive descent BNF, using "<" and
+ * ">" to indicate non-terminals, and "'" to indicate terminals.
+ */
+public class LLBNF extends BNFGrammar {
+
+    /**
+     * Utility method to instantiate a Production object. The method
+     * first tries to instantiate with the name given, thereafter by
+     * assuming its an inner class of the target grammar
+     */
+    Class findClass(String clsname)
+       throws ClassNotFoundException
+    {
+       ClassNotFoundException ex = null;
+       // Try the name as given
+       try {
+           return Class.forName( clsname );
+       } catch (ClassNotFoundException e) {
+           ex = e;
+       }
+       // Trying the name as inner class of the target class, or
+       // any of the target class base classes.
+       for (Class tc = target.getClass(); tc != null;
+            tc = tc.getSuperclass() ) {
+                try {
+                    return Class.forName( tc.getName() + "$" + clsname );
+                } catch (ClassNotFoundException e) {
+                }
+       }
+
+       // Trying the name as a class of the same package as the
+       // target class or any of its base classes
+       for (Class tc = target.getClass(); tc != null;
+            tc = tc.getSuperclass() ) {
+           String pkg = tc.getName();
+           int x = pkg.lastIndexOf( '.' );
+           if ( x > 0 ) {
+               try {
+                   return Class.forName(
+                       pkg.substring( 0, x ) + "." + clsname );
+               } catch (ClassNotFoundException e) {
+               }
+           }
+       }
+       throw ex;
+    }
+
+    Object create(String clsname)
+       throws Exception
+    {
+       Class cls = findClass( clsname );
+       Class ctx = cls.getDeclaringClass();
+       if ( ctx == null ) 
+           return cls.newInstance();
+       return cls.getConstructor( 
+           new Class [] { ctx } ).newInstance( new Object [] { target } );
+    }
+
+    Object createNamed(String clsname,String name)
+       throws Exception
+    {
+       Class cls = findClass( clsname );
+       Class ctx = cls.getDeclaringClass();
+       if ( ctx == null ) 
+           return cls.getConstructor( 
+               new Class [] { String.class } ).
+               newInstance( new Object [] { name } );
+       return cls.getConstructor( 
+           new Class [] { ctx, String.class } ).
+           newInstance( new Object [] { target, name } );
+    }
+
+    Hashtable grammars = new Hashtable();
+
+    Grammar getGrammar(String grammar)
+       throws Exception
+    {
+       Class c = findClass( grammar );
+       grammar = c.getName();
+       Grammar g = (Grammar) grammars.get( grammar );
+       if ( g == null ) {
+           g = (Grammar) create( grammar );
+           grammars.put( grammar, g );
+       }
+       return g;
+    }
+
+    Link linkGrammar(String rule,String goal,String grammar)
+       throws Exception
+    {
+       return target.new Link( rule, goal, getGrammar( grammar ) );
+    }
+
+    class BackLink {
+
+       final Grammar grammar;
+       final Link link;
+
+       BackLink(String rule,String goal,String g,Grammar target)
+           throws Exception
+       {
+           grammar = getGrammar( g );
+           link = grammar.new Link( rule, goal, target );
+       }
+
+       void addRule()
+       {
+           grammar.addRule( link );
+       }
+    }
+
+    BackLink backlinkGrammar(String rule,String goal,String grammar)
+       throws Exception
+    {
+       return new BackLink( rule, goal, grammar, target );
+    }
+
+    /**
+     * Generic Helper class that tags with a string.
+     */
+    public static class Tag extends Production {
+
+       public final String tag;
+       public final Grammar grammar;
+
+       public Tag(Grammar g,String t)
+       {
+           g.super();
+           tag = t;
+           grammar = g;
+       }
+
+       public Object action(Vector v)
+           throws Exception
+       {
+           RuleAction action = (RuleAction) grammar.rule_actions.get( tag );
+           if ( action != null )
+               return action.action( v );
+           if ( v.size() == 0 )
+               return tag;
+           v.add( 0, tag );
+           return v;
+       }
+
+       public String toString() {
+           StringBuffer s = new StringBuffer();
+
+           for ( int i=0; i<tokens.length; i++ ) {
+               s.append( tokens[i].toString() );
+               s.append( " " );
+           }
+           s.append( "# '" );
+           s.append( tag );
+           return s.toString();
+       }
+    }
+
+    
+    Grammar target;
+
+    public LLBNF()
+    {
+    }
+
+    public void initialisation()
+    {
+       addRule(
+           new ProductionList(
+               "rule",
+               new Production [] {
+                   new Production( new Token [] {
+                       new NonTerminal( "identifier" ),
+                       new Terminal( "::=" ),
+                       new NonTerminal( "productions" )
+                   } ) {
+                       public Object action(Vector v) {
+                           Vector p = (Vector) v.get( 1 );
+                           Production [] px = new Production[ p.size() ];
+                           return target.new ProductionList(
+                               (String) v.get( 0 ),
+                               (Production[]) p.toArray( px ) );
+                       } },
+                   new Production( new Token [] {
+                       new NonTerminal( "identifier" ),
+                       new Terminal( "is" ),
+                       new Terminal( "lexical" ),
+                       new NonTerminal( "any" )
+                   } ) {
+                       public Object action(Vector v)
+                           throws Exception
+                       {
+                           String name = (String) v.get( 0 );
+                           String clsname = (String) v.get( 1 );
+                           return (Lexical) createNamed( clsname, name );
+                       }
+                   },
+
+                   new Production( new Token [] {
+                       new NonTerminal( "identifier" ),
+                       new Terminal( "is" ),
+                       new Terminal( "linked" ),
+                       new Terminal( "to" ),
+                       new NonTerminal( "nonterminal" ),
+                       new Terminal( "in" ),
+                       new NonTerminal( "any" )
+                   } ) {
+                       public Object action(Vector v)
+                           throws Exception
+                       {
+                           String rule = (String) v.get( 0 );
+                           String goal = (String) v.get( 1 );
+                           String grammar = (String) v.get( 2 );
+                           return linkGrammar( rule, goal, grammar );
+                       }
+                   },
+
+                   new Production( new Token [] {
+                       new NonTerminal( "identifier" ),
+                       new Terminal( "of" ),
+                       new NonTerminal( "any" ),
+                       new Terminal( "is" ),
+                       new Terminal( "linked" ),
+                       new Terminal( "to" ),
+                       new NonTerminal( "nonterminal" ),
+
+                   } ) {
+                       public Object action(Vector v)
+                           throws Exception
+                       {
+                           String rule = (String) v.get( 0 );
+                           String goal = (String) v.get( 2 );
+                           String grammar = (String) v.get( 1 );
+                           return backlinkGrammar( rule, goal, grammar );
+                       }
+                   }
+
+               } ) );
+       addRule(
+           new ProductionList(
+               "rules",
+               new Production [] {
+                   new Production( new Token [] {
+                       new NonTerminal( "end" ),
+                   } ) {
+                        public Object action(Vector v) {
+                           return v;
+                       }
+                   },
+                   new Production( new Token [] {
+                       new NonTerminal( "rule" ),
+                       new NonTerminal( "rules" )
+                   } ) {
+                       public Object action(Vector v) {
+                           Vector all = (Vector) v.get( 1 );
+                           all.add( 0, v.get( 0 ) );
+                           return all;
+                       }
+                   }
+               } ) );
+       addRule(
+            new ProductionList(
+                "productions",
+                new Production [] {
+                   new Production( new Token [] {
+                        new NonTerminal( "production" ),
+                        new Terminal( "|" ),
+                        new NonTerminal( "productions" )
+                    } ) {
+                        public Object action(Vector v) {
+                           Vector all = (Vector) v.get( 1 );
+                           all.add( 0, v.get( 0 ) );
+                           return all;
+                       }
+                   },
+                    new Production( new Token [] {
+                        new NonTerminal( "production" )
+                    } ) {
+                        public Object action(Vector v) {
+                           return v;
+                       }
+                   }
+               } ) );
+       addRule(
+           new ProductionList(
+               "production",
+               new Production [] {
+                   new Production( new Token [] {
+                       new NonTerminal( "tokenlist" ),
+                       new Terminal( "#" ),
+                       new Terminal( "'" ),
+                       new NonTerminal( "any" ),
+                   } ) {
+                       public Object action(Vector v)
+                           throws Exception
+                       {
+                           Vector x = (Vector) v.get( 0 );
+                           Production p = new Tag(
+                               target, (String) v.get( 1 ) );
+                           p.setTokens(
+                               (Token[]) x.toArray( new Token[x.size()] ) );
+                           return p;
+                       }
+                   },
+                   new Production( new Token [] {
+                       new NonTerminal( "tokenlist" ),
+                       new Terminal( "#" ),
+                       new NonTerminal( "any" ),
+                   } ) {
+                       public Object action(Vector v)
+                           throws Exception
+                       {
+                           Vector x = (Vector) v.get( 0 );
+                           Production p = (Production)
+                               create( (String) v.get( 1 ) );
+                           p.setTokens(
+                               (Token[]) x.toArray( new Token[x.size()] ) );
+                           return p;
+                       }
+                   },
+                   new Production( new Token [] {
+                       new NonTerminal( "tokenlist" )
+                   } ) {
+                       public Object action(Vector v) {
+                           Vector x = (Vector) v.get( 0 );
+                           return target.new Production(
+                               (Token[]) x.toArray( new Token [ x.size() ] )
+                               );
+                       }
+                   }
+               } ) );
+       addRule(
+           new ProductionList(
+               "tokenlist",
+                new Production [] {
+                    new Production( new Token [] {
+                       new NonTerminal( "token" ),
+                       new NonTerminal( "tokenlist" )
+                   } ) {
+                        public Object action(Vector v) {
+                           Vector all = (Vector) v.get( 1 );
+                           all.add( 0, v.get( 0 ) );
+                           return all;
+                       }
+                   },
+                    new Production( new Token [] {
+                       new NonTerminal( "token" )
+                   } ) {
+                       public Object action(Vector v) {
+                           return v;
+                       }
+                   }
+               } ) );
+       addRule(
+            new ProductionList(
+                "token",
+                new Production [] {
+                    new Production( new Token [] {
+                       new NonTerminal( "nonterminal" ),
+                   } ) {
+                       public Object action(Vector v) {
+                           return target.new NonTerminal(
+                               (String) v.get( 0 ) );
+                       }
+                   },
+                    new Production( new Token [] {
+                        new NonTerminal( "terminal" ),
+                   } ) {
+                       public Object action(Vector v) {
+                           v = (Vector) v.get( 0 );
+                           boolean option = "?".equals( v.get( 0 ) );
+                           boolean preserve =
+                               option || "!".equals( v.get( 0 ) );
+                           return target.new Terminal(
+                               (String) v.get( 1 ),
+                               preserve, option );
+                       }
+                   }
+                } ) );
+        addRule(
+            new ProductionList(
+                "nonterminal",
+                new Production [] {
+                    new Production( new Token [] {
+                        new Terminal( "<" ),
+                        new NonTerminal( "identifier" ),
+                        new Terminal( ">" ),
+                    } )
+                } ) );
+        addRule(
+            new ProductionList(
+                "terminal",
+                new Production [] {
+                    new Production( new Token [] {
+                        new Terminal( "!", true ),
+                        new NonTerminal( "any" )
+                    } ),
+                    new Production( new Token [] {
+                        new Terminal( "'", true ),
+                        new NonTerminal( "any" )
+                   } ),
+                    new Production( new Token [] {
+                       new Terminal( "?", true ),
+                        new NonTerminal( "any" )
+                    } )
+                } ) );
+       addRule( new AnySymbol( "any" ) );
+       addRule( new Identifier( "identifier" ) );
+       addRule( new End( "end" ) );
+    }
+
+    /**
+     * Utility method to define rules using LLBNF syntax.
+     */
+    public Grammar addRules(Grammar g,String rules)
+       throws Throwable
+    {
+       try {
+           //trace = TRACE_STACK;
+           target = g;
+           grammars.put( g.getClass().getName(), target );
+
+           Vector v = (Vector) parseAndProcess( "rules", rules );
+           for ( Enumeration e = v.elements(); e.hasMoreElements(); ) {
+               Object r = e.nextElement();
+               if ( r instanceof BackLink )
+                   ((BackLink) r).addRule();
+               else
+                   target.addRule( (Rule) r );
+           }
+           return target;
+       } catch (Throwable t) {
+           throw hideParse( t, LLBNF.class.getName() );
+       }
+    }
+
+    /**
+     * A Test main method.
+     */
+    public static void main(String [] args)
+       throws Throwable
+    {
+       LLBNF g = new LLBNF();
+       String rules = g.toString() ;
+       Grammar x = g.addRules( new Grammar(), rules );
+       System.out.println( x );
+    }
+}
diff --git a/examples/planchoice/Main.java b/examples/planchoice/Main.java
new file mode 100644 (file)
index 0000000..663a046
--- /dev/null
@@ -0,0 +1,193 @@
+package examples.planchoice;
+
+import com.intendico.gorite.*;
+import com.intendico.data.*;
+import com.intendico.data.addon.Language;
+import java.util.Vector;
+import java.util.Map;
+import java.util.Hashtable;
+
+/**
+ * This is an example of using a plan choice plan for choosing which
+ * plan instance to pursue first / next for a goal.
+ */
+public class Main {
+
+    /**
+     * Debug: dump the plan choice options
+     */
+    static private void debugOptions(Data data) {
+       Vector<ContextualGoal> options = (Vector<ContextualGoal>)
+           data.getValue( "options" );
+       Vector<ContextualGoal> failed = (Vector<ContextualGoal>)
+           data.getValue( "failed" );
+       int n = 0;
+       System.err.println( "=============" );
+       for ( ContextualGoal cg : failed ) {
+           Plan p = (Plan) cg.getGoalSubgoals()[0];
+           if ( p instanceof TransferGoal ) {
+               p = (Plan) ((TransferGoal) p).goal;
+           }
+           System.err.printf(
+               "Failed %d = %s (%s)\n", n++, cg.context, p.getType() );
+       }
+       n = 0;
+       for ( ContextualGoal cg : options ) {
+           Plan p = (Plan) cg.getGoalSubgoals()[0];
+           if ( p instanceof TransferGoal ) {
+               p = (Plan) ((TransferGoal) p).goal;
+           }
+           System.err.printf(
+               "Option %d = %s (%s)\n", n++, cg.context, p.getType() );
+       }
+    }
+
+    /**
+     * Application entry point.
+     */
+    static public void main(String [] args) throws Exception {
+       //Goal.tracing = true;
+       Performer ralph = new Performer( "ralph" ) {{
+           setPlanChoice( "go for it", "go for it choice" );
+           putInquirable( new Relation( "unary", 1 ) );
+           getInquirable( "unary" ).get( new Object [] { "AAA" } ).add();
+           getInquirable( "unary" ).get( new Object [] { "BBB" } ).add();
+           getInquirable( "unary" ).get( new Object [] { "CCC" } ).add();
+
+           addGoal( new Plan( "go for it", new Goal [] {
+               new Goal( "print $x" ) {
+                   public States execute(Data data) {
+                       System.err.println(
+                           "First: " + data.getValue( "$x" ) );
+                       return States.FAILED;
+                   }
+               }
+                   } ) {
+               public Query context(Data d) {
+                   return Language.textToQuery(
+                       "unary( $x )", getInquirables().values(), null, d );
+               }
+           } );
+
+           addGoal( new Plan( "go for it", new Goal [] {
+               new Goal( "print $y" ) {
+                   public States execute(Data data) {
+                       System.err.println(
+                           "Second: " + data.getValue( "$y" ) );
+                       return States.FAILED;
+                   }
+               }
+                   }) {
+               public Query context(Data d) {
+                   return Language.textToQuery(
+                       "unary( $y )", getInquirables().values(), null, d );
+               }
+           } );
+
+           /**
+            * Add a plan choice plan that inspects the bindings for
+            * the applicable plan instance options, then selects the
+            * first of highest character code of the first binding.
+            *
+            * Note that this particular choice logic could also be
+            * implemented as precedence methods of the object level
+            * plans.
+            */
+           addGoal( new Plan( "go for it choice", new Goal [] {
+               new Goal( "select by planCode" ) {
+                   @Override
+                   public States execute(Data data,Goal.Instance i) {
+                       /**
+                          Input data elements:
+                          "options" = Vector of ContextualGoal
+                          "failed" = Vector of ContextualGoal
+                          Output data elements:
+                          "choice" = ContextualGoal (or null)
+                       **/
+                       debugOptions( data );
+                       int code = -1;
+                       Vector<ContextualGoal> options =
+                           (Vector<ContextualGoal>)
+                           data.getValue( "options" );
+                       if ( options.size() >= 1 ) {
+                           System.out.println(
+                               "Cause: " +
+                               options.get( 0 ).goal_data.thread_name );
+                       }
+                       ContextualGoal choice = null;
+                       for ( ContextualGoal g : options ) {
+                           int c = planCode( g );
+                           System.out.printf(
+                               "code for %s is %d\n", g.context, c );
+                           if ( c <= code )
+                               continue;
+                           choice = g;
+                           code = c;
+                       }
+                       if ( choice != null ) {
+                           data.setValue( "choice", choice );
+                       }
+                       return code > (int)'A'? States.PASSED : States.FAILED;
+                   }
+               }
+                   } ) );
+       }};
+       Team team = new Team( "everyone" ) {{
+           setTaskTeam( "group", new TaskTeam() {{
+               addRole( new Role( "sub", null ) {{
+                   putInquirable( new Relation( "unary", 1 ) );
+                   getInquirable( "unary" ).get(
+                       new Object [] { "ABC" } ).add();
+                   getInquirable( "unary" ).get(
+                       new Object [] { "DEF" } ).add();
+                   addGoal( new Plan( "go for it", new Goal [] {
+                       new Goal( "print $y" ) {
+                           public States execute(Data data) {
+                               System.err.println(
+                                   "Third: " +
+                                   data.getValue( "$y" ) );
+                               return States.FAILED;
+                           }
+                       }
+                           }) {
+                       public Query context(Data d) {
+                           return Language.textToQuery(
+                               "unary( $y )",
+                               getInquirables().values(), null, d );
+                       }
+                   } );
+               }} );
+           }} );
+           addGoal( new Plan( "go for it", new Goal [] {
+                       deploy( "group" ),
+                       new TeamGoal( "sub", "go for it" )
+                   } ) );
+       }};
+       team.addPerformer( ralph );
+       team.performGoal( new BDIGoal( "go for it" ), "TOP", new Data() );
+    }
+
+    /**
+     * Utility method that gives a given plan option a code, which
+     * generally is 0, except when the goal represents a contextual
+     * plan instance with an instantiation context that offers a
+     * non-empty String for the first context variable, and in that
+     * case, the character code of the first character of that string
+     * is used.
+     */
+    static public int planCode(ContextualGoal g) {
+       if ( g.context == null ) {
+           return 0;
+       }
+       Vector<Ref> cg = (Vector<Ref>) g.context;
+       if ( cg == null || cg.size() == 0 )
+           return 0;
+       Object x = Ref.deref( cg.get( 0 ) );
+       if ( ! ( x instanceof String ) )
+           return 0;
+       String s = (String) x;
+       if ( s.length() == 0 )
+           return 0;
+       return (int) s.charAt( 0 );
+    }
+}
diff --git a/examples/planchoice/filter b/examples/planchoice/filter
new file mode 100755 (executable)
index 0000000..a961e78
--- /dev/null
@@ -0,0 +1,7 @@
+# Defines a pipeline filter on the application output for reducing
+# it to regression test output.
+# Repeatable output should use: cat
+# Repeatable with internal pointers may use: sed -e 's/#[0-9]\+/#nnn/g'
+# Non repeatable should use: sort
+# Extreme cases may use special filter
+cat
diff --git a/examples/planchoice/output b/examples/planchoice/output
new file mode 100644 (file)
index 0000000..4657922
--- /dev/null
@@ -0,0 +1,328 @@
+** can Fill: sub com.intendico.gorite.examples.planchoice.Main$1: ralph
+** Sharing inquirables in com.intendico.gorite.examples.planchoice.Main$2: everyone
+** perform TOP
+=> TOP "go for it" (group=null)
+** Lookup "go for it"
+** "go for it" attempt 1
+=> TOP*0 "go for it" (group=null)
+=> TOP*0{} "go for it" (group=null)
+=> TOP*0{}.0 "deploy group" (group=null)
+** establish task team
+** can Act: sub com.intendico.gorite.examples.planchoice.Main$1: ralph
+-- noting everyone:TOP*0{}.0 "deploy group" (PASSED)
+<= everyone:TOP*0{}.0 "deploy group" (PASSED)
+=> TOP*0{}.1 "go for it" (group=null)
+=> TOP*0{}.1:0 "go for it" (group=null)
+** Lookup "go for it"
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 1
+=> TOP*0{}.1:0*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*0? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*0?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*0?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*0?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [DEF]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [CCC]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [CCC]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*0?*0{}.0 "select by planCode" (PASSED)
+<= ralph:TOP*0{}.1:0*0?*0{}.0 "select by planCode" (PASSED)
+-- noting ralph:TOP*0{}.1:0*0?*0{} "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*0?*0{} "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*0?*0 "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*0?*0 "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*0? "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*0? "go for it choice" (PASSED)
+=> TOP*0{}.1:0*0! "go for it" (group=null)
+=> TOP*0{}.1:0*0!{$y=DEF} "go for it" (group=null)
+=> TOP*0{}.1:0*0!{$y=DEF}.0 "print $y" (group=null)
+Third: DEF
+-- noting everyone:TOP*0{}.1:0*0!{$y=DEF}.0 "print $y" (FAILED)
+<= everyone:TOP*0{}.1:0*0!{$y=DEF}.0 "print $y" (FAILED)
+-- noting everyone:TOP*0{}.1:0*0!{$y=DEF} "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*0!{$y=DEF} "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*0! "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*0! "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*0 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*0 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 2
+=> TOP*0{}.1:0*1 "go for it choice" (group=null)
+=> TOP*0{}.1:0*1? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*1?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*1?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*1?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [CCC]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [CCC]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*1?*0{}.0 "select by planCode" (PASSED)
+<= ralph:TOP*0{}.1:0*1?*0{}.0 "select by planCode" (PASSED)
+-- noting ralph:TOP*0{}.1:0*1?*0{} "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*1?*0{} "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*1?*0 "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*1?*0 "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*1? "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*1? "go for it choice" (PASSED)
+=> TOP*0{}.1:0*1! "go for it" (group=null)
+=> TOP*0{}.1:0*1!{$x=CCC} "go for it" (group=null)
+>> com.intendico.gorite.examples.planchoice.Main$1: ralph
+=> +TOP*0{}.1:0*1!{$x=CCC} "go for it" (group=null)
+=> +TOP*0{}.1:0*1!{$x=CCC}.0 "print $x" (group=null)
+First: CCC
+-- noting ralph:+TOP*0{}.1:0*1!{$x=CCC}.0 "print $x" (FAILED)
+<= ralph:+TOP*0{}.1:0*1!{$x=CCC}.0 "print $x" (FAILED)
+-- noting ralph:+TOP*0{}.1:0*1!{$x=CCC} "go for it" (FAILED)
+<= ralph:+TOP*0{}.1:0*1!{$x=CCC} "go for it" (FAILED)
+<< com.intendico.gorite.examples.planchoice.Main$1: ralph
+-- noting everyone:TOP*0{}.1:0*1!{$x=CCC} "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*1!{$x=CCC} "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*1! "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*1! "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*1 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*1 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 3
+=> TOP*0{}.1:0*2 "go for it choice" (group=null)
+=> TOP*0{}.1:0*2? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*2?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*2?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*2?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [CCC]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*2?*0{}.0 "select by planCode" (PASSED)
+<= ralph:TOP*0{}.1:0*2?*0{}.0 "select by planCode" (PASSED)
+-- noting ralph:TOP*0{}.1:0*2?*0{} "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*2?*0{} "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*2?*0 "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*2?*0 "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*2? "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*2? "go for it choice" (PASSED)
+=> TOP*0{}.1:0*2! "go for it" (group=null)
+=> TOP*0{}.1:0*2!{$y=CCC} "go for it" (group=null)
+>> com.intendico.gorite.examples.planchoice.Main$1: ralph
+=> +TOP*0{}.1:0*2!{$y=CCC} "go for it" (group=null)
+=> +TOP*0{}.1:0*2!{$y=CCC}.0 "print $y" (group=null)
+Second: CCC
+-- noting ralph:+TOP*0{}.1:0*2!{$y=CCC}.0 "print $y" (FAILED)
+<= ralph:+TOP*0{}.1:0*2!{$y=CCC}.0 "print $y" (FAILED)
+-- noting ralph:+TOP*0{}.1:0*2!{$y=CCC} "go for it" (FAILED)
+<= ralph:+TOP*0{}.1:0*2!{$y=CCC} "go for it" (FAILED)
+<< com.intendico.gorite.examples.planchoice.Main$1: ralph
+-- noting everyone:TOP*0{}.1:0*2!{$y=CCC} "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*2!{$y=CCC} "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*2! "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*2! "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*2 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*2 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 4
+=> TOP*0{}.1:0*3 "go for it choice" (group=null)
+=> TOP*0{}.1:0*3? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*3?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*3?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*3?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*3?*0{}.0 "select by planCode" (PASSED)
+<= ralph:TOP*0{}.1:0*3?*0{}.0 "select by planCode" (PASSED)
+-- noting ralph:TOP*0{}.1:0*3?*0{} "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*3?*0{} "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*3?*0 "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*3?*0 "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*3? "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*3? "go for it choice" (PASSED)
+=> TOP*0{}.1:0*3! "go for it" (group=null)
+=> TOP*0{}.1:0*3!{$x=BBB} "go for it" (group=null)
+>> com.intendico.gorite.examples.planchoice.Main$1: ralph
+=> +TOP*0{}.1:0*3!{$x=BBB} "go for it" (group=null)
+=> +TOP*0{}.1:0*3!{$x=BBB}.0 "print $x" (group=null)
+First: BBB
+-- noting ralph:+TOP*0{}.1:0*3!{$x=BBB}.0 "print $x" (FAILED)
+<= ralph:+TOP*0{}.1:0*3!{$x=BBB}.0 "print $x" (FAILED)
+-- noting ralph:+TOP*0{}.1:0*3!{$x=BBB} "go for it" (FAILED)
+<= ralph:+TOP*0{}.1:0*3!{$x=BBB} "go for it" (FAILED)
+<< com.intendico.gorite.examples.planchoice.Main$1: ralph
+-- noting everyone:TOP*0{}.1:0*3!{$x=BBB} "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*3!{$x=BBB} "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*3! "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*3! "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*3 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*3 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 5
+=> TOP*0{}.1:0*4 "go for it choice" (group=null)
+=> TOP*0{}.1:0*4? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*4?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*4?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*4?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [BBB]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*4?*0{}.0 "select by planCode" (PASSED)
+<= ralph:TOP*0{}.1:0*4?*0{}.0 "select by planCode" (PASSED)
+-- noting ralph:TOP*0{}.1:0*4?*0{} "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*4?*0{} "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*4?*0 "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*4?*0 "go for it choice" (PASSED)
+-- noting ralph:TOP*0{}.1:0*4? "go for it choice" (PASSED)
+<= ralph:TOP*0{}.1:0*4? "go for it choice" (PASSED)
+=> TOP*0{}.1:0*4! "go for it" (group=null)
+=> TOP*0{}.1:0*4!{$y=BBB} "go for it" (group=null)
+>> com.intendico.gorite.examples.planchoice.Main$1: ralph
+=> +TOP*0{}.1:0*4!{$y=BBB} "go for it" (group=null)
+=> +TOP*0{}.1:0*4!{$y=BBB}.0 "print $y" (group=null)
+Second: BBB
+-- noting ralph:+TOP*0{}.1:0*4!{$y=BBB}.0 "print $y" (FAILED)
+<= ralph:+TOP*0{}.1:0*4!{$y=BBB}.0 "print $y" (FAILED)
+-- noting ralph:+TOP*0{}.1:0*4!{$y=BBB} "go for it" (FAILED)
+<= ralph:+TOP*0{}.1:0*4!{$y=BBB} "go for it" (FAILED)
+<< com.intendico.gorite.examples.planchoice.Main$1: ralph
+-- noting everyone:TOP*0{}.1:0*4!{$y=BBB} "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*4!{$y=BBB} "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*4! "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0*4! "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1:0*4 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*4 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 6
+=> TOP*0{}.1:0*5 "go for it choice" (group=null)
+=> TOP*0{}.1:0*5? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*5?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*5?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*5?*0{}.0 "select by planCode" (group=null)
+com.intendico.gorite.ContextualGoal [ABC]Plan:
+"go for it" (Main$2$1$1$2)
+1 "print $y" (Main$2$1$1$1)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+com.intendico.gorite.ContextualGoal [AAA]Plan:
+"go for it" (TransferGoal)
+-- noting ralph:TOP*0{}.1:0*5?*0{}.0 "select by planCode" (FAILED)
+<= ralph:TOP*0{}.1:0*5?*0{}.0 "select by planCode" (FAILED)
+-- noting ralph:TOP*0{}.1:0*5?*0{} "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*5?*0{} "go for it choice" (FAILED)
+-- noting ralph:TOP*0{}.1:0*5?*0 "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*5?*0 "go for it choice" (FAILED)
+-- noting ralph:TOP*0{}.1:0*5? "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*5? "go for it choice" (FAILED)
+-- noting everyone:TOP*0{}.1:0*5 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*5 "go for it choice" (FAILED)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+~(context)> (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+<(context)~ (com.intendico.gorite.examples.planchoice.Main$1: ralph)
+** "go for it" attempt 7
+=> TOP*0{}.1:0*6 "go for it choice" (group=null)
+=> TOP*0{}.1:0*6? "go for it choice" (group=null)
+** Lookup "go for it choice"
+** "go for it choice" attempt 1
+=> TOP*0{}.1:0*6?*0 "go for it choice" (group=null)
+=> TOP*0{}.1:0*6?*0{} "go for it choice" (group=null)
+=> TOP*0{}.1:0*6?*0{}.0 "select by planCode" (group=null)
+-- noting ralph:TOP*0{}.1:0*6?*0{}.0 "select by planCode" (FAILED)
+<= ralph:TOP*0{}.1:0*6?*0{}.0 "select by planCode" (FAILED)
+-- noting ralph:TOP*0{}.1:0*6?*0{} "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*6?*0{} "go for it choice" (FAILED)
+-- noting ralph:TOP*0{}.1:0*6?*0 "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*6?*0 "go for it choice" (FAILED)
+-- noting ralph:TOP*0{}.1:0*6? "go for it choice" (FAILED)
+<= ralph:TOP*0{}.1:0*6? "go for it choice" (FAILED)
+-- noting everyone:TOP*0{}.1:0*6 "go for it choice" (FAILED)
+<= everyone:TOP*0{}.1:0*6 "go for it choice" (FAILED)
+-- noting everyone:TOP*0{}.1:0 "go for it" (FAILED)
+<= everyone:TOP*0{}.1:0 "go for it" (FAILED)
+-- noting everyone:TOP*0{}.1 "go for it" (FAILED)
+<= everyone:TOP*0{}.1 "go for it" (FAILED)
+-- noting everyone:TOP*0{} "go for it" (FAILED)
+<= everyone:TOP*0{} "go for it" (FAILED)
+-- noting everyone:TOP*0 "go for it" (FAILED)
+<= everyone:TOP*0 "go for it" (FAILED)
+-- noting everyone:TOP "go for it" (FAILED)
+<= everyone:TOP "go for it" (FAILED)
+** runPerformersBlocked
+** runPerformersOnce
+-- everyone running 0
+** runPerformersOnce stopped false
+** runPerformersBlocked done something false
diff --git a/examples/ruleset/Main.java b/examples/ruleset/Main.java
new file mode 100644 (file)
index 0000000..5580fa2
--- /dev/null
@@ -0,0 +1,43 @@
+package examples.ruleset;
+
+import com.intendico.data.*;
+import com.intendico.data.addon.*;
+import java.util.Vector;
+
+public class Main {
+
+    // Missing constructor
+    static Rule asRule(Vector v) {
+       return new Rule( (Query) v.get( 0 ), (Query) v.get( 1 ) );
+    }
+    
+    /**
+     * Application main.
+     */
+    static public void main(String [] args) {
+       Language logic = Language.getLanguage();
+       //logic.trace = -1;
+       
+       // Define the belief structures
+       Vector kb = new Vector();
+       kb.add( new Relation( "P", 1 ) );
+
+       // Define the theory
+       RuleSet rs = new RuleSet();
+       rs.add( asRule( logic.textToRule( "P($x) => Not P($x)", kb ) ) );
+       rs.add( asRule( logic.textToRule( "Lost P($x) => P($x)", kb ) ) );
+
+       // Add a belief
+       logic.textToQuery( "P(1)", kb, null, null ).add();
+
+       // Tell the start state
+       System.out.println( kb );
+       System.out.println( rs );
+
+       // Run infer as per the theory
+       rs.run();
+
+       // Tell the result
+       System.out.println( kb );
+    }
+}
diff --git a/overview.html b/overview.html
new file mode 100644 (file)
index 0000000..3df552b
--- /dev/null
@@ -0,0 +1,6 @@
+<html>
+  <head><title>GORITE 10.5</title></head>
+  <body>
+    <h1>GORITE 10.5</h1>
+  </body>
+</head>