001    /**
002     * @author paulk
003     */
004    
005    package toolbus.atom;
006    
007    import java.util.ArrayList;
008    import java.util.Iterator;
009    import java.util.List;
010    import java.util.Stack;
011    import toolbus.AtomList;
012    import toolbus.Functions;
013    import toolbus.State;
014    import toolbus.StateElement;
015    import toolbus.TBTermFactory;
016    import toolbus.ToolBus;
017    import toolbus.environment.Environment;
018    import toolbus.exceptions.ToolBusException;
019    import toolbus.parsercup.PositionInformation;
020    import toolbus.process.ProcessExpression;
021    import toolbus.process.ProcessInstance;
022    import aterm.AFun;
023    import aterm.ATerm;
024    import aterm.ATermAppl;
025    import aterm.ATermInt;
026    import aterm.ATermList;
027    
028    /**
029     * The class Atom forms the basic building block of Tscripts. Instances of Atom
030     * are both the most primitive elements of process expressions and the elements of
031     * the states to which process exprssions are being compiled.
032     *
033     */
034    
035    abstract public class Atom extends ProcessExpression implements StateElement{
036            private final static String SECS = "sec";
037            private final static String MSECS = "msec";
038            
039            private final static Ref[] NOATOMARGS = new Ref[0];
040            
041            private ProcessInstance processInstance;        // process instance to which the atom belongs
042            private Environment env;                                        // the environment of this atom
043            private List<Test> tests;                                 // optional tests that guard this atom
044            private Ref[] atomArgs = NOATOMARGS;            // arguments of the atom
045            private int delay = 0;                                          // time delay before atom can be executed
046            private int timeout = 0;                                        // timeout after which atom can no longer be executed
047            private boolean timeExpr = false;                       // the actual time expression for delay/timeout
048            private long activateTime;                                      // when this atom was activated
049            private long enabledTime;                                       // When this atom becomes ready for execution
050            private long timeoutTime;                                       // When this atom becomes no longer eligable for execution
051            protected String externalNameAsReceivedByTool;
052            
053            public Atom(TBTermFactory tbfactory, PositionInformation posInfo){
054                    super(tbfactory, posInfo);
055                    addToFirst(this);
056                    externalNameAsReceivedByTool = shortName();
057            }
058            
059            public void destroy(){/* Intended to (optionally) be overwritten in subclasses to enable them to execute cleanup actions if needed. */}
060            
061            public void setAtomArgs(Ref[] refs){
062                    atomArgs = refs;
063            }
064            
065            public ATerm getAtomArgValue(int i){
066                    return atomArgs[i].value;
067            }
068            
069            public void copyAtomAttributes(Atom a){
070                    delay = a.delay;
071                    timeout = a.timeout;
072                    timeExpr = a.timeExpr;
073            }
074            
075            protected void setEnv(Environment env){
076                    this.env = env;
077            }
078            
079            public Environment getEnv(){
080                    return env;
081            }
082            
083            public void setTest(ATerm test, Environment e) throws ToolBusException{
084                    if(test != null){
085                            //System.err.println(this + "." + "setTest: env " + env.hashCode() + " => " + e.hashCode());
086                            if(tests == null) tests = new ArrayList<Test>(4);
087                            Test t = new Test(test, e);
088                            //System.out.println("setTest: " + t);
089                            this.tests.add(t);
090                    }
091            }
092            
093            public List<ATerm> getTests(){
094                    if(tests != null){
095                            List<ATerm> testExpressions = new ArrayList<ATerm>(tests.size());
096                            Iterator<Test> testsIterator = tests.iterator();
097                            while(testsIterator.hasNext()){
098                                    testExpressions.add(testsIterator.next().testExpr);
099                            }
100                            return testExpressions;
101                    }
102                    return new ArrayList<ATerm>(0);
103            }
104            
105            public void setDelay(ATerm delay){
106                    if(delay instanceof ATermAppl){
107                            ATermAppl timeExpression = (ATermAppl) delay;
108                            AFun timeFun = timeExpression.getAFun();
109                            if(timeFun.getArity() == 1){
110                                    String order = timeFun.getName();
111                                    ATerm argument = timeExpression.getArgument(0);
112                                    if(argument instanceof ATermInt){
113                                            ATermInt time = (ATermInt) argument;
114                                            if(order == SECS){
115                                                    this.delay = time.getInt() * 1000;
116                                                    timeExpr = true;
117                                                    return;
118                                            }else if(order == MSECS){
119                                                    this.delay = time.getInt();
120                                                    timeExpr = true;
121                                                    return;
122                                            }
123                                    }
124                            }
125                    }
126                    // If the function didn't return before this point the argument wasn't formatted properly.
127                    throw new RuntimeException("Not a time expression: "+delay);
128            }
129            
130            public void setAbsoluteDelay(ATermList delay){
131                    // TODO Implement as soon as we know how to do it properly.
132                    throw new UnsupportedOperationException("Absolute delays are currently unsupported.");
133            }
134            
135            public int getDelay(){
136                    return delay;
137            }
138            
139            public void setTimeout(ATerm timeout){
140                    if(timeout instanceof ATermAppl){
141                            ATermAppl timeExpression = (ATermAppl) timeout;
142                            AFun timeFun = timeExpression.getAFun();
143                            if(timeFun.getArity() == 1){
144                                    String order = timeFun.getName();
145                                    ATerm argument = timeExpression.getArgument(0);
146                                    if(argument instanceof ATermInt){
147                                            ATermInt time = (ATermInt) argument;
148                                            if(order == SECS){
149                                                    this.timeout = time.getInt() * 1000;
150                                                    timeExpr = true;
151                                                    return;
152                                            }else if(order == MSECS){
153                                                    this.timeout = time.getInt();
154                                                    timeExpr = true;
155                                                    return;
156                                            }
157                                    }
158                            }
159                    }
160                    // If the function didn't return before this point the argument wasn't formatted properly.
161                    throw new RuntimeException("Not a time expression: "+delay);
162            }
163            
164            public void setAbsoluteTimeout(ATermList timeout){
165                    // TODO Implement as soon as we know how to do it properly.
166                    throw new UnsupportedOperationException("Absolute timeouts are currently unsupported.");
167            }
168            
169            public int getTimeout(){
170                    return timeout;
171            }
172            
173            public ToolBus getToolBus(){
174                    return processInstance.getToolBus();
175            }
176            
177            public AtomList getAtoms(){
178                    return new AtomList(this);
179            }
180            
181            private String shortName(){
182                    return getClass().getSimpleName();
183            }
184            
185            public String toString(){
186                    /*String pidStr = (processInstance != null) ? "[" + processInstance.getProcessName() + "/" + processInstance.getProcessId() + "]" : "";
187                    String args = "(";
188                    String sep = "";
189                    
190                    for(int i = 0; i < atomArgs.length; i++){
191                            args = args + sep + atomArgs[i];
192                            //args = args + sep + tbfactory.substitute(atomArgs[i].value, env);
193                            sep = ", ";
194                    }
195                    args = args + ")";
196                    String strtest = (tests == null) ? "" : " if " + tests;*/
197                    if(atomArgs.length > 0){
198                            StringBuilder sb = new StringBuilder();
199                            sb.append(shortName());
200                            sb.append('(');
201                            sb.append(atomArgs[0].value);
202                            int nrOfAtomArgs = atomArgs.length;
203                            for(int i = 1; i < nrOfAtomArgs; i++){
204                                    sb.append(',');
205                                    sb.append(atomArgs[i].value);
206                            }
207                            sb.append(')');
208                            return sb.toString();
209                    }
210                    return shortName()/* + pidStr + args + strtest*/;
211            }
212            
213            public ATermAppl toATerm(){
214                    int nargs = atomArgs.length;
215                    // System.err.println("toATerm: " + externalNameAsReceivedByTool);
216                    
217                    AFun afun = tbfactory.makeAFun(externalNameAsReceivedByTool, nargs, false);
218                    ATerm pat[] = new ATerm[nargs];
219                    
220                    for(int i = 0; i < nargs; i++){
221                            pat[i] = tbfactory.makePattern(atomArgs[i].value);
222                    }
223                    return tbfactory.makeAppl(afun, pat);
224            }
225            
226            public void computeFirst(){/* Overwritten in subclass */}
227            
228            public void replaceFormals(Environment e) throws ToolBusException{
229                    this.env = e;
230                    //System.err.println("Atom.replaceFormals: " + env);
231                    for(int i = 0; i < atomArgs.length; i++){
232                            //System.err.println("atomArg[" + i + "] = " + atomArgs[i] + " ; env = " + env);
233                            ATerm arg = tbfactory.replaceFormals(atomArgs[i].value, env);
234                            //System.err.println("atomArg[" + i + "] = " + atomArgs[i].value + " => " + arg + "; env = " + env);
235                            atomArgs[i].value = arg;
236                    }
237            }
238            
239            public void compile(ProcessInstance pi, Stack<String> calls, State follow) throws ToolBusException{
240                    this.processInstance = pi;
241                    // System.err.println("Atom.compile, prev env = " + env);
242                    // this.env = env.copy();
243                    setFollow(follow);
244                    // System.err.println("Compiling " + this + ";\n env = " + this.env);
245                    // replaceFormals(env); //TODO redundant?
246            }
247            
248            // Implementation of the StateElement interface
249            
250            public boolean isEnabled() throws ToolBusException{
251                    // System.err.println("Atom.isEnabled: " + this.getProcess().getProcessId() + ": " + this);
252                    if(timeExpr){
253                            // System.err.println("Has a TimeExpr; delay = " + delay + "; timeout = " + timeout);
254                            long currentTime = getToolBus().getRunTime();
255                            // System.err.println("startTime = " + startTime + "; currentTime = " + currentTime);
256                            if(currentTime < enabledTime){
257                                    getToolBus().setNextTime(enabledTime);
258                                    // System.err.println("currentTime < startTime + delay");
259                                    /*incr(notEnabled);*/
260                                    return false;
261                            }
262                            if(timeout != 0 && currentTime > timeoutTime){
263                                    // System.err.println("currentTime > startTime + timeout");
264                                    /*incr(notEnabled);*/
265                                    return false;
266                            }
267                    }
268                    if(tests != null){
269                            //System.err.println("Atom.isEnabled: " + this.getProcess().getProcessId() + ": " +  this);
270                            Iterator<Test> testIterator = tests.iterator();
271                            while(testIterator.hasNext()){
272                                    Test t = testIterator.next();
273                                    //System.err.print("evaluate: " + t);
274                                    boolean res = tbfactory.isTrue(Functions.eval(t.testExpr, processInstance, t.testEnv));
275                                    //System.err.println(" ==> " + res);
276                                    if(!res){
277                                            /*incr(notEnabled);*/
278                                            return false;
279                                    }
280                            }
281                    }
282                    /*incr(enabled);*/
283                    return true;
284            }
285            
286            public boolean contains(StateElement b){
287                    return this == b;
288            }
289            
290            public ProcessInstance getProcess(){
291                    return processInstance;
292            }
293            
294            public State gotoNextStateAndActivate(){
295                    // System.err.println(this + "getNextState ==> " + getFollow());
296                    State s = getFollow();
297                    s.activate();
298                    return s;
299            }
300            
301            public State gotoNextStateAndActivate(StateElement b){
302                    if(this.equals(b)){
303                            State s = getFollow();
304                            s.activate();
305                            return s;
306                    }
307                    System.err.println("Atom.getNextState2: wrong arg: " + b); // TODO exception
308                    return null;
309            }
310            
311            public void activate(){
312                    activateTime = getToolBus().getRunTime();
313                    enabledTime = activateTime + delay;
314                    timeoutTime = activateTime + timeout;
315                    
316                    // System.err.println("used_vars = " + collected);
317                    // System.err.println("Atom.activate: " + this);
318            }
319            
320            /**
321             * This class holds static reference to an empty process instance array, which can be used as
322             * return value in the debugExecute method. Using this instead of a newly created array every
323             * time will reduce garbage creation. 
324             * 
325             * @author Arnold Lankamp
326             */
327            private static class EmptyProcessInstanceArrayHolder{
328                    public final static ProcessInstance[] ZEROPARTNERS = new ProcessInstance[0];
329            }
330            
331            public ProcessInstance[] debugExecute() throws ToolBusException{
332                    if(execute()) return EmptyProcessInstanceArrayHolder.ZEROPARTNERS;
333                    
334                    return null;
335            }
336            
337            /**
338             * This class represents a test associated with an atom.
339             * It carries its own execution environment.
340             */
341            private static class Test{
342                    public final ATerm testExpr;
343                    public final Environment testEnv;
344                    
345                    public Test(ATerm test, Environment env){
346                            testExpr = test;
347                            testEnv = env;
348                    }
349                    public String toString(){
350                            return "Test(" + testExpr + ", " + testEnv + ")";
351                    }
352            }
353    }