001 package sysModel; 002 003 import controller.IEditAdapter; 004 import controller.IScrollAdapter; 005 import controller.ISimulationExceptionAdapter; 006 import controller.IDisplayAdapter; 007 import model.ILambda; 008 import sysModel.env.AEnvFactory; 009 import sysModel.env.AGlobalEnv; 010 import sysModel.env.FishApplyParams; 011 import sysModel.env.NullEnv; 012 import sysModel.fish.DynamicFishFactory; 013 import sysModel.fish.AFish; 014 import sysModel.parser.Parser; 015 import sysModel.parser.ParserException; 016 017 import javax.swing.*; 018 import java.awt.*; 019 import java.awt.event.ActionEvent; 020 import java.awt.event.ActionListener; 021 import java.io.FileWriter; 022 import java.io.PrintWriter; 023 import java.io.IOException; 024 025 /** 026 * Simulation control coordinating interaction between view, simulation engine and environment. 027 * 028 * @author Mathias Ricken 029 */ 030 public class SimDriver { 031 /** 032 * Environment. 033 */ 034 private AGlobalEnv _env; 035 036 /** 037 * Simulation engine. 038 */ 039 private SimEngine _simEngine; 040 041 /** 042 * Command to set the seed when a new environment is loaded or created. 043 */ 044 private ILambda _seedCommand; 045 046 /** 047 * Command executed before the simulation starts running. 048 */ 049 private ILambda _startCommand; 050 051 /** 052 * Command executed after the simulation finishes an iteration. 053 */ 054 private ILambda _iterationCommand; 055 056 /** 057 * Simulation speed. 058 */ 059 private int _simulationSpeed; 060 061 /** 062 * Processing timer. 063 */ 064 private Timer _timer; 065 066 /** 067 * Edit adapter. 068 */ 069 private IEditAdapter _editAdapter = null; 070 071 /** 072 * Simulation exception adapter. 073 */ 074 private ISimulationExceptionAdapter _simulationExceptionAdapter = null; 075 076 /** 077 * Security manager. 078 */ 079 private MBSSecurityManager _securityManager = null; 080 081 /** 082 * Command factory for use in the environment. 083 */ 084 private ICmdFactory _cmdFactory = new ICmdFactory() { 085 public ILambda makeNotifyCmd(final ILambda lambda) { 086 return new ILambda() { 087 public Object apply(Object param) { 088 _simEngine.notify(lambda); 089 return null; 090 } 091 }; 092 } 093 094 public ILambda makeDeleteCmd(final AGlobalEnv.ALocalEnv env) { 095 return makeNotifyCmd(new ILambda() { 096 public Object apply(Object param) { 097 FishApplyParams applyParams = (FishApplyParams) param; 098 // if the fish is at this location 099 if (applyParams.localEnv() == env) { 100 // ask the simulation engine to remove it 101 _simEngine.delete(applyParams.fish()); 102 } 103 return null; 104 } 105 }); 106 } 107 108 public ILambda makeAddCmd(final AFish fish) { 109 return new ILambda() { 110 public Object apply(Object param) { 111 _simEngine.add(fish); 112 return null; 113 } 114 }; 115 } 116 }; 117 118 /** 119 * Construct a new model. 120 */ 121 public SimDriver() { 122 _env = NullEnv.instance(); 123 _simEngine = new SimEngine(); 124 _seedCommand = NoOpLambda.instance(); 125 _startCommand = NoOpLambda.instance(); 126 _iterationCommand = NoOpLambda.instance(); 127 // Note: DrJava incompatibility. 128 _securityManager = new MBSSecurityManager("true".equals(System.getProperty("edu.rice.cs.cunit.drjava"))); 129 _securityManager.setProtected(false); 130 System.setSecurityManager(_securityManager); 131 } 132 133 /** 134 * Get the simulation engine. 135 * 136 * @return simulation engine 137 */ 138 public ICmdFactory getCmdFactory() { 139 return _cmdFactory; 140 } 141 142 /** 143 * Set adapters. 144 * 145 * @param ea makeEditCmd adapter to use 146 * @param sea simulation exception adapter to use 147 */ 148 public void setAdapters(IEditAdapter ea, ISimulationExceptionAdapter sea) { 149 _editAdapter = ea; 150 _simulationExceptionAdapter = sea; 151 } 152 153 /** 154 * Load environment from file. 155 * 156 * @param filename filename 157 * @return true if successful 158 */ 159 public boolean loadEnvironment(String filename) { 160 try { 161 _simEngine.clear(); 162 _env = Parser.parse(filename, _cmdFactory, getSecurityAdapter()); 163 _seedCommand.apply(null); 164 return true; 165 } 166 catch (ParserException e) { 167 System.out.println(e); 168 return false; 169 } 170 } 171 172 /** 173 * Save environment to file. 174 * 175 * @param filename filename 176 * @return true if successful 177 */ 178 public boolean saveEnvironment(String filename) { 179 try { 180 // open a new PrintWriter 181 PrintWriter pw = new PrintWriter(new FileWriter(filename)); 182 183 // ask the environment what to do 184 ILambda lambda = _env.save(pw); 185 186 // and do it 187 _simEngine.notify(lambda); 188 189 // close the PrintWriter 190 pw.close(); 191 return true; 192 } 193 catch (IOException e) { 194 return false; 195 } 196 } 197 198 /** 199 * Create environment from factory. 200 * 201 * @param factory factory 202 * @return true if successful 203 */ 204 public boolean createEnvironment(AEnvFactory factory) { 205 _env = factory.create(); 206 _simEngine.clear(); 207 _seedCommand.apply(null); 208 return true; 209 } 210 211 /** 212 * Set the seed command. This command gets executed before a new environment is created or loaded. 213 * 214 * @param seedCmd command to set 215 */ 216 public void setSeedLambda(ILambda seedCmd) { 217 _seedCommand = seedCmd; 218 } 219 220 /** 221 * Make a simulation step. 222 */ 223 public void step() { 224 // ask the environment what to do 225 ILambda lambda = _env.makeStepCmd(); 226 227 // and do it 228 lambda.apply(this); 229 } 230 231 /** 232 * Start simulation. 233 */ 234 public void start() { 235 _startCommand.apply(null); 236 _timer = new Timer(_simulationSpeed, new ActionListener() { 237 public void actionPerformed(ActionEvent evt) { 238 step(); 239 _iterationCommand.apply(null); 240 } 241 }); 242 _timer.setCoalesce(false); 243 _timer.start(); 244 } 245 246 /** 247 * Stop simulation. 248 */ 249 public void stop() { 250 if (null != _timer) { 251 _timer.stop(); 252 } 253 } 254 255 /** 256 * Change the simulation speed. 257 * 258 * @param speed simulation speed 259 */ 260 public void setSpeed(int speed) { 261 _simulationSpeed = speed; 262 if (null != _timer) { 263 _timer.setDelay(speed); 264 } 265 } 266 267 /** 268 * Set simulation start command. This command gets called before the simulation starts. 269 * 270 * @param startCmd start command 271 */ 272 public void setStartLambda(ILambda startCmd) { 273 _startCommand = startCmd; 274 } 275 276 /** 277 * Set simulation iteration command. This command gets called after the simulation finishes a step. 278 * 279 * @param itCmd start command 280 */ 281 public void setIterationLambda(ILambda itCmd) { 282 _iterationCommand = itCmd; 283 } 284 285 /** 286 * Draw model in this region. The graphics object has been set up so that (0,0) represents the top left and 287 * (100,100) the bottom right corner. 288 * 289 * @param g graphics object 290 * @param comp component to drawFish on 291 * @param p1 top left corner of the region 292 * @param p2 bottom right corner of the region 293 */ 294 public void draw(Graphics2D g, Component comp, Point.Double p1, Point.Double p2) { 295 // ask the environment what to do 296 ILambda lambda = _env.makeDrawCmd(g, comp, p1, p2); 297 298 // and do it 299 lambda.apply(this); 300 } 301 302 /** 303 * Edit a field. 304 * 305 * @param p coordinates in model coordinate units 306 * @param button mouse button pressed 307 */ 308 public void edit(Point.Double p, int button) { 309 // create the fishFactory to use 310 DynamicFishFactory fishFactory = new DynamicFishFactory(_editAdapter.getCurrentFish(), 311 _editAdapter.getCurrentColor(), 312 new ISecurityAdapter() { 313 public void setProtected(boolean b) { 314 _securityManager.setProtected(b); 315 } 316 public ThreadGroup getFishThreadGroup() { 317 return _securityManager.getFishThreadGroup(); 318 } 319 public ClassLoader getClassLoader() { 320 return _securityManager.getClassLoader(); 321 } 322 public void handleException(Throwable t) { 323 _simulationExceptionAdapter.handleException(t); 324 } 325 }); 326 327 try { 328 // let the environment decide what to do 329 ILambda lambda = _env.makeEditCmd(p, fishFactory, button); 330 331 // and do it 332 lambda.apply(this); 333 } 334 catch(Throwable t) { 335 Throwable d = t; 336 while ((null != d) && (null != d.getCause())) { 337 d = d.getCause(); 338 } 339 _simulationExceptionAdapter.handleException(d); 340 } 341 } 342 343 /** 344 * The action to be executed if the display should return home. 345 * 346 * @param sa scroll adapter 347 */ 348 public void returnHome(IScrollAdapter sa) { 349 _env.returnHome(sa); 350 } 351 352 /** 353 * Get size of the display. 354 * 355 * @return size of the display in model coordinate units. 356 */ 357 public Dimension getDisplaySize() { 358 return _env.getDisplaySize(); 359 } 360 361 /** 362 * Ask the model where to scroll, given where the user has scrolled. If the environment just acts like a normal 363 * panal, it should return pos without modification. If the environment recenters, it should return a position in 364 * the middle of the pan area. All coordinates are in model coordinate units. 365 * 366 * @param pos position where the user scrolled to 367 * @return position where the environment wants the view to be 368 * @see IDisplayAdapter#getPanDelta 369 */ 370 public Point.Double getViewPosition(Point.Double pos) { 371 return _env.getViewPosition(pos); 372 } 373 374 /** 375 * Ask the model how much to pan, given where the user scrolled. If the environment just acts like a normal panal, 376 * it should return (0,0). If the environment recenters, it should return delta without modification. All 377 * coordinates are in model coordinate units. 378 * 379 * @param delta how far the user scrolled 380 * @return how far the panel should scroll 381 * @see IDisplayAdapter#getViewPosition 382 */ 383 public Point.Double getPanDelta(Point.Double delta) { 384 return _env.getPanDelta(delta); 385 } 386 387 /** 388 * Get a tool tip description for a specific place in the environment. 389 * 390 * @param p mouse coordinates 391 * @return tool tip text 392 */ 393 public String getToolTipText(Point.Double p) { 394 return ((String) _env.getToolTipText(p).apply(this)); 395 } 396 397 /** 398 * Return the security adapter. 399 * 400 * @return security adapter 401 */ 402 public ISecurityAdapter getSecurityAdapter() { 403 return new ISecurityAdapter() { 404 public void setProtected(boolean b) { 405 // Note: DrJava incompatibility. 406 _securityManager.setProtected(b); 407 } 408 public ThreadGroup getFishThreadGroup() { 409 return _securityManager.getFishThreadGroup(); 410 } 411 public ClassLoader getClassLoader() { 412 return _securityManager.getClassLoader(); 413 } 414 public void handleException(Throwable t) { 415 _simulationExceptionAdapter.handleException(t); 416 } 417 }; 418 } 419 }