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