--- /dev/null
+#
+# 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 "© 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
--- /dev/null
+echo $(pwd)
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+/*********************************************************************
+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 ) + ")";
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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 ;
+
+}
--- /dev/null
+/*********************************************************************
+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 + ")";
+ }
+}
--- /dev/null
+/*********************************************************************
+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 + ")";
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+
+}
--- /dev/null
+/*********************************************************************
+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 + ")";
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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() + " )";
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+}
--- /dev/null
+/*********************************************************************
+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 ::= <predicate> '=> <predicate>
+ *
+ * predicate ::= 'And '( <predicates> ')
+ * | 'Or '( <predicates> ')
+ * | 'Not <predicate>
+ * | 'Lost <predicate>
+ * | 'Equals '( <arguments> ')
+ * | 'Distinct '( <arguments> ')
+ * | <name> '( <arguments> ')
+ *
+ * predicates ::= <predicate> ', <predicates>
+ * | <predicate>
+ *
+ * arguments ::= <argument> ', <arguments>
+ * | <argument>
+ *
+ * argument ::= <number>
+ * | <string> -- double-quoted
+ * | <xstring> -- single-quoted
+ * | '$ <name>
+ * </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;
+ }
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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);
+
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+
+
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+
+
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
+
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+}
--- /dev/null
+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 );
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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);
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 ;
+ }
+ }
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+ };
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
+ }
+ }
+}
+
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+}
--- /dev/null
+/*********************************************************************
+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" )
+ } );
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+}
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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();
+ }
--- /dev/null
+/*********************************************************************
+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
--- /dev/null
+/*********************************************************************
+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();
+ }
+ }
+ }
+}
+
--- /dev/null
+/*********************************************************************
+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;
+ }
+
+}
--- /dev/null
+/*********************************************************************
+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 );
+ }
+ }
+}
+
--- /dev/null
+/*********************************************************************
+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 );
+ }
+}
--- /dev/null
+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 );
+ }
+}
--- /dev/null
+# 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
--- /dev/null
+** 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
--- /dev/null
+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 );
+ }
+}
--- /dev/null
+<html>
+ <head><title>GORITE 10.5</title></head>
+ <body>
+ <h1>GORITE 10.5</h1>
+ </body>
+</head>