1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
4 This file is part of GORITE.
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE. If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
20 package com.intendico.gorite;
22 import com.intendico.data.Query;
23 import com.intendico.data.Ref;
25 import java.util.Enumeration;
26 import java.util.Formatter;
27 import java.util.HashSet;
28 import java.util.Hashtable;
29 import java.util.Iterator;
30 import java.util.Vector;
31 import java.util.Observable;
32 import java.util.Observer;
35 * This is a container class for data elements that goals use and
38 * <p> The data that a business process is instantiated for has an
39 * internal structure which allows references to individual data
40 * elements, and that a data element is either singular or a plurality
41 * of other data elements.
43 * <p> A model includes data references, primarily in connection with
44 * task goals where the data references identify the data elements
45 * that the associated tasks are to operate on, but also in connection
46 * with repeat goals, which get instantiated for the constituent
47 * elements of a plural element.
49 * <p> Any actual transformation of data is done in tasks, and the
50 * business process model is limited to data reference.
52 * <p> A task is associated with two collections of data elements,
53 * known as inputs and results. The association is defined by means of
54 * using data element names, which are symbols that refer to
55 * individual or groups of data elements.
57 * <p> A data object is a container for data references, which are
58 * names of inidividual or groups of data elements.
63 * This is a base class for data elements.
65 public class Element {
68 * The name of this data element.
70 public final String name;
73 * The values of this data element.
75 public Vector/*<Object>*/ values = new Vector/*<Object>*/();
78 * Constructor with initial value
80 public Element(String n,Object v) {
83 values.insertElementAt( v, 0 );
89 public Element(String n) {
94 * Tracking whether this element is ready or not.
96 public boolean ready = false;
101 public synchronized void markReady() {
106 * Utility method to set value and mark ready.
108 public void set(Object v)
114 * Utility method to set value and maybe mark ready.
116 public void set(Object v,boolean mark)
118 values.insertElementAt( v, 0 );
126 * Utility method to return top of values.
128 public Object get() {
129 return values.size() > 0? values.get( 0 ) : null ;
133 * Utility method to return and remove top of values.
135 public Object pop() {
136 if ( values.size() == 0 )
138 return values.remove( 0 );
142 * Utility method to forget all but N values. A given N less
143 * than 1 results in silently using 1.
145 public void forget(int n) {
147 while ( values.size() > n ) {
151 while ( n++ < 0 && values.size() > 0 ) {
158 * Returns the i:th sub element, if any, or null otherwise.
160 public Object get(int i)
162 return ( i < values.size() )? values.get( i ): null;
166 * Returns the number of values.
169 return values.size();
173 * Text representation of this data element.
175 public String toString() {
177 Formatter f = new Formatter( new StringBuilder() );
178 f.format( "\"%s\" ", name );
180 f.format( "%s", values.toString() );
182 f.format( "[not ready]" );
186 StringBuilder s = new StringBuilder();
191 s.append( values.toString() );
193 s.append( "[not ready]" );
200 * Utility method to attach an observer to a given observable, to
201 * be used for triggering continuation of blocked intention.
203 public void setTrigger(Observable s) {
204 getBindings().setTrigger( s );
208 * Utility method to attach an observer to a given query, to be
209 * used for triggering continuation of blocked intention. The
210 * query is reset and re-tested upon notification, and if true,
211 * the blocked intention is allowed to continue.
213 public void setTrigger(Query q) {
214 getBindings().setTrigger( q );
218 * Utility method to remove a trigger.
220 public void clearTrigger() {
221 getBindings().clearTrigger();
225 * Utility method to check whether a goal instance execution is
226 * running or not. All instance executions are considered running
227 * unless there a trigger is set via a {@link #setTrigger
228 * setTrigger} call, and still armed.
230 public boolean isRunning(String thread_name) {
231 return ! ((Bindings) bindings.get( thread_name )).monitoring ;
235 * Utility method to attach an observer on the intention-local
236 * bindings. The observer will then be notified when any trigger
237 * (see {@link #setTrigger setTrigger}) fires.
239 public void addObserver(String thread_name,Observer x) {
240 ((Bindings) bindings.get( thread_name )).addObserver( x );
244 * Utility method to remove a trigger observer.
246 public void deleteObserver(String thread_name,Observer x) {
247 ((Bindings) bindings.get( thread_name )).deleteObserver( x );
251 * A class for allowing transient name space split.
253 public class Bindings extends Observable {
256 * The intention thread name that caused this Bindings
262 * A bindings-local flag to indicate that the associated
263 * intention is has an unblocking monitor.
265 public boolean monitoring = false;
268 * Utility method to clear the monitoring flag.
270 void clearTrigger() {
275 * Utility method to clear the monitoring flag and propagate
276 * notification to any observer.
278 void clearMonitoring() {
280 if ( Goal.isTracing() ) {
282 "** Clearing monitoring for " + name );
288 //setFlag(); // and wake executor....
289 Performer p = (Performer) getValue( Goal.PERFORMER );
297 * Utility method to attach an Observer to the given
298 * Observable, to capture its notification as a trigger.
300 public void setTrigger(Observable s) {
303 s.addObserver( new Observer() {
304 public void update(Observable x,Object y) {
305 if ( Goal.isTracing() ) {
307 "** Observer updated by " + x +
312 x.deleteObserver( this );
318 * Utility class to observe a query, to allow an intention to
319 * continue when the query becomes true.
321 private class QueryMonitor implements Observer {
324 * The query being observed.
331 QueryMonitor(Query q) {
335 q.addObserver( this );
336 update( null, null );
340 * The Observer callback method. This will call reset()
341 * and next() on the given query, and optionally reset
342 * monitoring and call setFlag().
344 synchronized public void update(Observable x,Object y) {
345 if ( Goal.isTracing() ) {
347 "** Query " + query + " updated by " + x );
349 if ( ! monitoring ) {
351 query.deleteObserver( this );
354 boolean test = false;
356 if ( Goal.isTracing() ) {
357 System.err.println( "** Query reset on " + query );
361 if ( Goal.isTracing() ) {
362 System.err.println( "** Query next on " + query );
364 test = ( query != null )? query.next() : true;
365 if ( Goal.isTracing() ) {
367 "** Query " + query + " is " + test );
369 } catch (Exception e) {
375 query.deleteObserver( this );
377 if ( Goal.isTracing() ) {
379 "** Query data: " + Data.this.toString() );
386 * Utility method to attach an Observer to the given Query, to
387 * capture its becoming true as a trigger.
389 public void setTrigger(Query q) {
390 new QueryMonitor( q );
394 * This is the transient data store for an execution path.
396 public Hashtable/*<String,Element>*/ elements =
397 new Hashtable/*<String,Element>*/();
400 * Link to a prior Bindings object; the base of this one.
402 Bindings prior = null;
407 public Bindings(String n,Bindings p)
414 * Special constructor to transiently bind a given name to its
415 * i:th sub element, if it exists.
417 public Bindings(String n,Bindings p,String name,int i)
421 if ( name != null ) {
422 Element e = Data.this.find( p, name );
424 Object v = e.get( i );
426 Element x = new Element( name, v );
428 elements.put( name, x );
435 * Returns the named data elements, if any, or null otherwise.
437 public Element find(String name)
439 return (Element) elements.get( name );
443 * Utility method to create an Element instance.
445 public Element create(String name) {
446 Element e = (Element) elements.get( name );
448 e = new Element( name );
449 elements.put( name, e );
455 * Utility method that pushes all data except the given one
456 * into the prior bindings.
458 public void push(String except,HashSet/*<String>*/ h) {
461 boolean gf = goldfish;
463 for ( Enumeration/*<String>*/ i = elements.keys();
464 i.hasMoreElements(); ) {
465 String key = (String) i.nextElement();
466 if ( key.equals( except ) )
469 Element e = prior.create( key );
470 e.set( ((Element) elements.get( key )).get(), false );
476 * Returns a String representation of this Bindings object.
478 public String toString() {
480 Formatter f = new Formatter( new StringBuilder() );
481 f.format( "Data.Bindings %s", name );
483 f.format( " [prior = %s]", prior.name );
484 for (Iterator/*<String>* / i =
485 elements.keySet().iterator(); i.hasNext(); ) {
486 f.format( "\n %s", elements.get( i.next() ).toString() );
491 StringBuilder s = new StringBuilder();
492 s.append( "Data.Bindings " );
494 if ( prior != null ) {
495 s.append( " [prior = " );
496 s.append( prior.name );
499 for (Iterator/*<String>*/ i =
500 elements.keySet().iterator(); i.hasNext(); ) {
502 s.append( elements.get( i.next() ).toString() );
509 * Flags this data into goldfish mode, which means for data
510 * elements to forget past values when new values are set.
512 public boolean goldfish = false;
515 * Keeps the 'global' bindings object.
517 public Hashtable/*<String,Bindings>*/ bindings =
518 new Hashtable/*<String,Bindings>*/();
521 * Utility method that links a thread name with the current
522 * bindings. This may be overridden later by parallel goals, which
523 * get their local bindings.
525 public void link(String thread_name) {
526 bindings.put( thread_name, getBindings() );
530 * Utility method to remove a bindings entry.
532 public void dropBindings(String thread_name) {
533 bindings.remove( thread_name );
537 * Utility method to locate an element from within a "thread".
539 public Element getLocalElement(String thread_name,String name) {
540 Bindings b = (Bindings) bindings.get( thread_name );
541 return find( b != null? b : getBindings(), name );
545 * Utility method that creates a thread-local binding for the
546 * given name to its i:th sub element. If name is null, or the
547 * i:th sub element missing, a thread local bindings object is
548 * still created, but without initialising the name.
550 public synchronized void fork(String name,int i,String thread_name)
552 bindings.put( thread_name,
553 new Bindings( thread_name, getBindings(), name, i ) );
557 * Utility method to push data. All names of the given HashSet
558 * except the exception are pushd down to be sub elements of the
559 * equally named element of the prior bindings object.
561 public synchronized void join(
562 String except,HashSet/*<String>*/ h,String thread_name) {
564 getBindings().push( except, h );
565 dropBindings( thread_name );
569 * Obtains the bindings object chain for the calling thread.
571 public Bindings getBindings() {
572 Bindings b = (Bindings) bindings.get( thread_name );
574 b = new Bindings( thread_name, null );
575 bindings.put( thread_name, b );
581 // Tracking of threads and data dependencies.
585 * Holds the current "thread name".
587 public String thread_name = "X";
590 * Change thread name and return the prior name.
592 public String setThreadName(String n) {
593 String old = thread_name;
599 * Determine the call data element name from a given head string
600 * or thread_name string.
602 public String getArgsName(String name) {
603 int i = name.indexOf( '"' );
606 i = name.lastIndexOf( '*', i );
609 return name.substring( 0, i ) + "-ARG";
613 * Determine the call data element name from the thread_name, and
617 public Object getArgs(String head) {
618 return getValue( getArgsName( head ) );
622 * The most recently assigned arg.
627 * Set up the call data agrument for the given instance head name
628 * as given, adding the "-ARG" suffix.
630 public void setArgs(String name,Object x) {
633 setValue( name + "-ARG", x );
638 // Normal mode accessors
642 * Utility method to obtain data element value.
644 public Object getValue(String name) {
645 Element e = find( name );
646 return e != null? e.get() : null ;
650 * Utility method to obtain data element value as seen by a given
653 public Object getValue(String name,String th_name) {
654 Bindings b = (Bindings) bindings.get( th_name );
656 return getValue( name );
657 Element e = find( b, name );
658 return e != null? e.get() : getValue( name ) ;
662 * Utility method to set data element value.
664 public Data setValue(String name,Object v) {
665 create( name ).set( v );
670 * Utility method to set data element value relative a given
673 public Data setValue(String th_name, String name,Object v) {
674 String old = setThreadName( th_name );
675 create( name ).set( v );
676 setThreadName( old );
681 * Utility method to delete all elements for a name along the
682 * whole binding chain.
684 public void forget(String name) {
685 for ( Bindings b = getBindings(); b != null; b = b.prior ) {
686 b.elements.remove( name );
691 * Utility method to forget all but n values for an element (i.e.,
692 * its "innermost" Binding occurrence). If n < 0, then -n top
693 * values are forgotten. If n == 0, then everyhing is forgotten,
694 * except that the element remains.
696 public void forget(String name,int n)
698 Element e = find( name );
705 * Utility method to replace top data element value.
707 public Data replaceValue(String name,Object v)
709 Element e = create( name );
716 * Utility method to restore to a prior value.
718 public Data restoreValue(String name,Object v) {
719 Element e = create( name );
721 if ( goldfish || e.size() == 0 ) {
728 * Find a given data element following the bindings chain of the
731 public synchronized Element find(String name) {
732 return find( getBindings(), name );
736 * Find a given data element following the bindings chain of the
739 public Element find(Bindings b,String name) {
740 for ( ; b != null; b = b.prior ) {
741 Element e = (Element) b.elements.get( name );
749 * Tells whether there is an element of a given name in the
750 * bindings object chain of the calling thread or not
752 public boolean hasElement(String name)
754 return find( name ) != null;
758 * Utility to tell how many values a data element has.
760 public int size(String name) {
761 Element e = find( name );
762 return e != null? e.values.size() : 0;
766 * Utility method to retrieve an Element instance on the calling
767 * thread's bindings chain, or create one at the head of the chain
770 public synchronized Element create(String name) {
771 Element e = find( name );
773 e = getBindings().create( name );
779 * This method is used for querying about readiness of a data
780 * element. The method returns immediately, with true if the
781 * element is missing or not ready, and false otherwise.
783 public boolean pending(String name) {
784 Element e = find( name );
785 return e == null || ! e.ready ;
789 * Marks a named element as ready. If no such element exists, then
790 * this method will create a new one.
792 public void ready(String name) {
793 create( name ).markReady();
797 * Set data elements from the {@link Ref} objects of the given
800 public void set(Query query) {
801 set( query.getRefs( new Vector/*<Ref>*/() ) );
805 * Set data elements from the given {@link Ref} objects, using
806 * {@link #replaceValue}.
808 public void set(Vector/*<Ref>*/ refs) {
809 for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); ) {
810 Ref ref = (Ref) i.next();
811 //setValue( ref.getName(), ref.get() );
812 replaceValue( ref.getName(), ref.get() );
817 * Set the {@link Ref} objects of a {@link Query} from data
820 public void get(Query query,boolean keep) {
821 get( query.getRefs( new Vector/*<Ref>*/() ), keep );
825 * Set the given {@link Ref} objects from data elements.
827 //@SuppressWarnings("unchecked")
828 public void get(Vector/*<Ref>*/ refs,boolean keep) {
829 for ( Iterator/*<Ref>*/ i = refs.iterator(); i.hasNext(); ) {
830 Ref ref = (Ref) i.next();
831 Element e = find( ref.getName() );
833 ref.set( keep? e.get() : e.pop() );
839 * Set the given {@link Ref} object from its data element, without
840 * popping the data element value.
842 //@SuppressWarnings("unchecked")
843 public void get(Ref/*...*/ [] refs) {
844 for ( int i = 0; i < refs.length; i++ ) {
845 Element e = find( refs[ i ].getName() );
847 refs[ i ].set( e.get() );
852 // Thread control support
858 public boolean flag = true;
861 * Utility method to raise the {@link #flag}.
863 public synchronized void setFlag() {
870 public synchronized void unsetFlag() {
878 * Utility method to wait for the rendezvous {@link #flag} to be
881 public synchronized void waitFlag() {
885 } catch (InterruptedException e) {
892 * Returns a String representation of this Data object.
894 public String toString() {
896 Formatter f = new Formatter( new StringBuilder() );
898 HashSet/*<String>* / done = new HashSet/*<String>* /();
899 for ( Iterator/*<String>* / i =
900 bindings.keySet().iterator(); i.hasNext(); ) {
901 String tn = (String) i.next();
904 Bindings b = bindings.get( tn );
905 String h = "Data.Bindings " + b.name;
906 if ( ! done.contains( b.name ) )
908 f.format( "Intention %s --> %s", tn, h );
914 StringBuilder s = new StringBuilder();
916 HashSet/*<String>*/ done = new HashSet/*<String>*/();
917 for ( Iterator/*<String>*/ i =
918 bindings.keySet().iterator(); i.hasNext(); ) {
919 String tn = (String) i.next();
922 Bindings b = (Bindings) bindings.get( tn );
923 String h = "Data.Bindings " + b.name;
924 if ( ! done.contains( b.name ) )
926 s.append( "Intention " );