1// BEGIN LICENSE BLOCK
2// Version: CMPL 1.1
3//
4// The contents of this file are subject to the Cisco-style Mozilla Public
5// License Version 1.1 (the "License"); you may not use this file except
6// in compliance with the License.  You may obtain a copy of the License
7// at www.eclipse-clp.org/license.
8//
9// Software distributed under the License is distributed on an "AS IS"
10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11// the License for the specific language governing rights and limitations
12// under the License.
13//
14// The Original Code is  The ECLiPSe Constraint Logic Programming System.
15// The Initial Developer of the Original Code is  Cisco Systems, Inc.
16// Portions created by the Initial Developer are
17// Copyright (C) 2006 Cisco Systems, Inc.  All Rights Reserved.
18//
19// Contributor(s):
20//
21// END LICENSE BLOCK
22
23package com.parctechnologies.eclipse.visualisation;
24
25import com.parctechnologies.eclipse.*;
26
27import java.beans.*;
28import javax.swing.*;
29import java.awt.event.*;
30import java.io.*;
31import java.util.*;
32
33/** Encapsulation of the state of the vis client. The main aspect of this is
34 * what stage of event processing the vis client is in. Several objects may be
35 * interested in this, so VisClientStateModel uses a PropertyChangeSupport to
36 * register interested observers and inform them of changes to
37 * currentVisClientState. I.e. the state integer is "observable". At a more
38 * coarse level of state representation,
39 * the state model also maintains an observable boolean representing whether
40 * ECLiPSe or
41 * the VisClient has control and also whether the VisClient can perform RPC
42 * or not.
43 * <p>The VisClientStateModel class also has some other responsibilities:
44 * <ul>
45 * <li> Maintain settable, observable boolean autoResume to
46 * control whether autoResume is on.
47 * <li> Provide access to the autoResume function's delay
48 * model (i.e. how long it delays for), represented by a BoundedRangeModel.
49 * <li> If AutoResume is on, ensure that resume takes place automatically
50 * at the specified duration after HELD_ON_EVENT is entered.
51 * <li> Maintain settable boolean interrupt to control whether the VC should
52 * hold at the next event.
53 * <li> Maintain settable boolean terminate to control whether the VC should
54 * terminate as soon as possible.
55 * <li> Provide access to the vis client's Resume action and Interrupt action.
56 * <li> Provide access to the VisClient's eclipse-side name (an atom)
57 * </ul>*/
58public class VisClientStateModel
59{
60  public final static int NO_CURRENT_EVENT = 0;
61  public final static int SETTING_VIEWER_POLICY = 1;
62  public final static int COLLECTING_PRE_EVENT_GOALS = 2;
63  public final static int EXECUTING_PRE_EVENT_GOALS = 3;
64  public final static int DISTRIBUTING_PRE_EVENT_GOAL_RESULTS = 4;
65  public final static int SETTING_BREAKPOINT = 5;
66  public final static int HELD_ON_EVENT = 6;
67  public final static int EVENT_IS_FINISHED = 7;
68
69
70  private boolean eclipseHasControl ;
71  private boolean canPerformRPC ;
72  private boolean autoResume = false;
73  private boolean interrupt = false;
74  private boolean terminate = false;
75
76  private boolean recordScenario = true;
77  private boolean allScenarioCommandsExecuted;
78  private boolean viewerBuildingPolicySelected = false;
79
80  private Atom visClientName;
81  private VisClient visClient;
82
83  private BoundedRangeModel delayModel;
84
85  private int currentState;
86  /*private*/ EclipseMultitaskConnection eclipse;
87
88  private String eclipseLibDir;
89
90  private Action resumeAction;
91  private Action interruptAction;
92  private Action autoResumeAction;
93  private AutoResumeTimer autoResumeTimer;
94
95  private PropertyChangeSupport propertyChangeSupport;
96
97  /** Runnable objects paced on this list will be run after pending
98      RPCs in the multitaking phase */
99  private List multitaskRunnableList;
100
101  public VisClientStateModel(EclipseMultitaskConnection eclipse,
102                             Atom visClientName,
103                             VisClient visClient)
104  {
105    this.eclipse = eclipse;
106    this.visClientName = visClientName;
107    this.visClient = visClient;
108    propertyChangeSupport = new PropertyChangeSupport(this);
109    setCurrentState(NO_CURRENT_EVENT);
110    setEclipseHasControl(true);
111    setCanPerformRPC(false);
112    resumeAction = new ResumeAction();
113    autoResumeAction = new AutoResumeAction();
114    interruptAction = new InterruptAction();
115    delayModel = new DefaultBoundedRangeModel(1000,1,0,5000);
116    multitaskRunnableList = new LinkedList();
117  }
118
119  public synchronized void setAllScenarioCommandsExecuted(boolean newValue)
120  {
121    allScenarioCommandsExecuted = newValue;
122    notifyAll();
123  }
124
125  public Atom getVisClientName()
126  {
127    return(visClientName);
128  }
129
130
131  public VisClient getVisClient() {
132    return visClient;
133  }
134
135
136  public BoundedRangeModel getDelayModel()
137  {
138    return(delayModel);
139  }
140
141  synchronized void setCurrentState(int newState)
142  {
143    int oldState;
144    if(currentState != newState)
145    {
146      oldState = currentState;
147      currentState = newState;
148      if (DebuggingSupport.logMessages) {
149        DebuggingSupport.logMessage(this,
150                                    "VC state change from "+oldState+
151                                    " to " + newState);
152      }
153      propertyChangeSupport.
154        firePropertyChange("currentVisClientState", new Integer(oldState),
155                           new Integer(currentState));
156      if(getAutoResume() && !getInterrupt() &&
157         currentState == HELD_ON_EVENT)
158      {
159        startAutoResumeTimer();
160      }
161      if(getAutoResume() && currentState == NO_CURRENT_EVENT)
162      {
163        stopAutoResumeTimer();
164      }
165    }
166    notifyAll();
167  }
168
169  private void startAutoResumeTimer()
170  {
171    autoResumeTimer =
172          new AutoResumeTimer(delayModel.getValue(), autoResumeAction);
173    autoResumeTimer.start();
174  }
175
176  private void stopAutoResumeTimer()
177  {
178    if(autoResumeTimer != null)
179    {
180      autoResumeTimer.stop();
181      autoResumeTimer = null;
182    }
183  }
184
185  synchronized void setEclipseHasControl(boolean newValue)
186  {
187    boolean oldValue;
188    if(eclipseHasControl != newValue)
189    {
190      oldValue = eclipseHasControl;
191      eclipseHasControl = newValue;
192      propertyChangeSupport.
193        firePropertyChange("eclipseHasControl",
194                           new Boolean(oldValue),
195                           new Boolean(newValue));
196    }
197  }
198
199  synchronized void setCanPerformRPC(boolean newValue)
200  {
201    boolean oldValue;
202    if(canPerformRPC != newValue)
203    {
204      oldValue = canPerformRPC;
205      canPerformRPC = newValue;
206      propertyChangeSupport.
207        firePropertyChange("canPerformRPC",
208                           new Boolean(oldValue),
209                           new Boolean(newValue));
210    }
211  }
212
213  synchronized public void setAutoResume(boolean newValue)
214  {
215    boolean oldValue;
216    if(autoResume != newValue)
217    {
218      oldValue = autoResume;
219      autoResume = newValue;
220      propertyChangeSupport.
221        firePropertyChange("autoResume",
222                           new Boolean(oldValue),
223                           new Boolean(newValue));
224      if(!newValue && autoResumeTimer != null && autoResumeTimer.isRunning())
225      {
226        stopAutoResumeTimer();
227      }
228    }
229  }
230
231
232  synchronized public boolean getRecordScenario() {
233    return(recordScenario);
234  }
235
236  synchronized public void setRecordScenario(boolean newValue) {
237    boolean oldValue;
238    if (recordScenario != newValue) {
239      oldValue = recordScenario;
240      recordScenario = newValue;
241      propertyChangeSupport.firePropertyChange("recordScenario",
242					       new Boolean(oldValue),
243					       new Boolean(newValue));
244    }
245  }
246
247  synchronized public boolean getViewerBuildingPolicySelected() {
248    return viewerBuildingPolicySelected;
249  }
250
251  synchronized public void setViewerBuildingPolicySelected(boolean newValue) {
252    if (DebuggingSupport.logMessages) {
253      DebuggingSupport.logMessage(this,
254                                  "set policy newValue="+newValue);
255    }
256    viewerBuildingPolicySelected = newValue;
257  }
258
259  synchronized public int getCurrentState()
260  {
261    return(currentState);
262  }
263
264  synchronized public boolean getEclipseHasControl()
265  {
266    return(eclipseHasControl);
267  }
268
269  synchronized public boolean getCanPerformRPC()
270  {
271    return(canPerformRPC);
272  }
273
274  synchronized public boolean getAutoResume()
275  {
276    return(autoResume);
277  }
278
279  public void setInterrupt(boolean newValue)
280  {
281    interrupt = newValue;
282  }
283
284  public boolean getInterrupt()
285  {
286    return(interrupt);
287  }
288
289  public synchronized void setTerminate(boolean newValue)
290  {
291    terminate = newValue;
292    if (newValue && getEclipseHasControl()) {
293      visClient.exitNormal();
294    }
295    notifyAll();
296  }
297
298  public synchronized boolean getTerminate()
299  {
300    return(terminate);
301  }
302
303  void resume()
304  {
305
306    if (DebuggingSupport.logMessages) {
307      DebuggingSupport.logMessage(this, "disabling resume action");
308    }
309
310    //disable the resume button
311    getResumeAction().setEnabled(false);
312    // clear the interrupt flag
313    setInterrupt(false);
314
315    if (DebuggingSupport.logMessages) {
316      DebuggingSupport.logMessage(this, "resume action performed");
317    }
318
319    visClient.endMultitaskPhase();
320  }
321
322  public Action getResumeAction()
323  {
324    return(resumeAction);
325  }
326
327  public Action getInterruptAction()
328  {
329    return(interruptAction);
330  }
331
332  /**
333   * interested objects can become observers of one or more properties by
334   * invoking this object's addPropertyChangeListener method.
335   */
336  PropertyChangeSupport getPropertyChangeSupport()
337  {
338    return(propertyChangeSupport);
339  }
340
341  /**
342   * Class implementing the VisClient's record action.
343   *
344   */
345  private class RecordAction extends AbstractAction
346  {
347    RecordAction()
348    {
349      super("Record");
350      setEnabled(true);
351    }
352
353    public void actionPerformed(ActionEvent e)
354    {
355
356	if (DebuggingSupport.logMessages) {
357	    DebuggingSupport.logMessage(this, "RecordAction actionPerformed method invoked");
358	}
359
360      setRecordScenario(!getRecordScenario());
361    }
362  }
363
364  /** Class implementing the VisClient's resume action. The action is
365   * disabled by default but becomes enabled when the state changes to
366   * HELD_ON_EVENT or during a multitasking phase. The action
367   * performed simply calls the resume method which ultimately
368   * disables the resume action again. */
369  private class ResumeAction extends AbstractAction
370    implements PropertyChangeListener
371  {
372    static final String normalName = "Resume";
373    static final String multitaskName = "Continue";
374
375    ResumeAction()
376    {
377      super(normalName);
378      setEnabled(false);
379      propertyChangeSupport.
380        addPropertyChangeListener("canPerformRPC", this);
381    }
382
383    public void propertyChange(PropertyChangeEvent event)
384    {
385      if (((Boolean) event.getNewValue()).booleanValue()) {
386        setEnabled(true);
387      } else {
388        setEnabled(false);
389      }
390    }
391
392    public void actionPerformed(ActionEvent e)
393    {
394
395      if (DebuggingSupport.logMessages) {
396        DebuggingSupport.logMessage(this, "ResumeAction actionPerformed method invoked");
397      }
398
399      if (isEnabled()) {
400        if (DebuggingSupport.logMessages) {
401          DebuggingSupport.logMessage(this,
402                                      "ResumeAction current state queried");
403        }
404        resume();
405      }
406    }
407  }
408
409  /** action executed by the auto resume timer. It is as the resume
410   * action, but for the fact that execution has no effect if
411   * interrupt is true or if the state is not HELD_ON_EVENT (ie. if a
412   * multitask phase has started which is not a Visualisation
413   * phase). This means that interrupt being true has the additional
414   * effect of cancelling the next AutoResume if autoResume is on.
415   * */
416  private class AutoResumeAction extends ResumeAction
417  {
418    public void propertyChange(PropertyChangeEvent event)
419    {
420      super.propertyChange(event);
421      if (currentState != HELD_ON_EVENT) {
422        setEnabled(false);
423      }
424    }
425
426    public void actionPerformed(ActionEvent e)
427    {
428      if(!getInterrupt() && (currentState == HELD_ON_EVENT))
429      {
430        super.actionPerformed(e);
431      }
432    }
433  }
434
435
436  /** Class implementing the vis client's interrupt action. This
437   * action serves both to indicate that the vis client should hold at
438   * the next event or, if AutoResume is on and the VC has control,
439   * that the next autoResume should be cancelled. It is enabled by
440   * default, and manages its own enabling by observing the
441   * stateInteger. It becomes enabled when no event is current and
442   * disable either when it is executed, or when an event is happening
443   * (as long as autoResume is off). When executed, it stops the
444   * autoResume if it is on, and sets interrupt to true.  */
445  private class InterruptAction extends AbstractAction
446    implements PropertyChangeListener
447  {
448    InterruptAction()
449    {
450      super("Interrupt");
451      setEnabled(true);
452      propertyChangeSupport.
453        addPropertyChangeListener("currentVisClientState", this);
454    }
455
456    public void propertyChange(PropertyChangeEvent event)
457    {
458      int newState = ((Integer) event.getNewValue()).intValue();
459      if(newState == NO_CURRENT_EVENT)
460      {
461        setEnabled(true);
462        return;
463      }
464      DebuggingSupport.logMessage(this,"interrupt action newState="+newState+" getAutoResume()="+getAutoResume());
465      if(!getAutoResume() && newState == HELD_ON_EVENT)
466      {
467        setEnabled(false);
468        return;
469      }
470    }
471
472    public void actionPerformed(ActionEvent e)
473    {
474        setEnabled(false);
475
476	if (DebuggingSupport.logMessages) {
477	    DebuggingSupport.logMessage(this, "interrupt action performed");
478	}
479
480        if(getAutoResume() && autoResumeTimer != null && autoResumeTimer.isRunning())
481        {
482          stopAutoResumeTimer();
483        }
484        setInterrupt(true);
485    }
486  }
487
488
489  private class AutoResumeTimer extends javax.swing.Timer
490  {
491    AutoResumeTimer(int delay, Action action)
492    {
493      super(delay, action);
494
495      if (DebuggingSupport.logMessages) {
496	  DebuggingSupport.logMessage(this, "delay = "+delay);
497      }
498
499      setRepeats(false);
500      setInitialDelay(delay);
501    }
502  }
503
504
505  /** This method may be invoked by any thread to have the goal
506   * performed during the multitask phase. */
507  CompoundTerm executeMultitaskGoal(CompoundTerm goal)
508    throws IOException, EclipseException
509  {
510    CompoundTerm result;
511    if (DebuggingSupport.logMessages) {
512      DebuggingSupport.logMessage(this, "multitask goal posted");
513    }
514
515    // following statement blocks until goal is set.
516    result = eclipse.rpc(goal);
517
518    if (DebuggingSupport.logMessages) {
519      DebuggingSupport.logMessage(this, "multitask goal complete");
520    }
521
522    return(result);
523  }
524
525  /**
526   * Executes the batch goal in the multitask thread, suspends until
527   * the results is available.
528   * @return The results of the batch goal
529   */
530  public List executeMultitaskBatchGoal(BatchGoal batch) throws IOException, EclipseException {
531    List result;
532    if (DebuggingSupport.logMessages) {
533      DebuggingSupport.logMessage(this, "multitask batch goal posted");
534    }
535    result = batch.execute(eclipse);
536    if (DebuggingSupport.logMessages) {
537      DebuggingSupport.logMessage(this, "multitask batch goal complete");
538    }
539    return result;
540  }
541
542  /**
543   * Access the directory in which the ECLiPSe architecture specific
544   * libraries are installed
545   **/
546  public void setEclipseLibDir(String eclipseLibDir) {
547    this.eclipseLibDir=eclipseLibDir;
548  }
549  /**
550   * Access the directory in which the ECLiPSe architecture specific
551   * libraries are installed
552   **/
553  public String getEclipseLibDir() {
554    return eclipseLibDir;
555  }
556}
557