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 }