001 package sysModel.env;
002
003 import controller.IScrollAdapter;
004 import controller.IDisplayAdapter;
005 import junit.framework.TestCase;
006 import model.ILambda;
007 import model.fish.GenericFish;
008 import sysModel.ICmdFactory;
009 import sysModel.ISecurityAdapter;
010 import sysModel.NoOpLambda;
011 import sysModel.fish.IFishFactory;
012 import sysModel.fish.AFish;
013 import sysModel.parser.DefaultTokenVisitor;
014 import sysModel.parser.Lexer;
015 import sysModel.parser.ParserException;
016
017 import javax.swing.*;
018 import java.awt.*;
019 import java.awt.event.MouseEvent;
020 import java.awt.geom.Point2D;
021 import java.util.LinkedList;
022 import java.io.PrintWriter;
023
024 /**
025 * Implementation of a square bounded environment.
026 *
027 * @author Mathias G. Ricken
028 */
029 public class BoundedEnv extends ASquareEnv {
030 /**
031 * Field interface.
032 */
033 public static interface IField {
034 /**
035 * Visitor hook.
036 *
037 * @param vis visitor to execute.
038 * @param param visitor-specific parameter
039 * @return visitor-specific return value
040 */
041 public abstract Object execute(IFieldVisitor vis, Object param);
042 }
043
044 /**
045 * Empty field singleton.
046 */
047 public static class EmptyField implements IField {
048 /**
049 * Singleton instance.
050 */
051 public static final EmptyField Singleton = new EmptyField();
052
053 /**
054 * Singleton ctor.
055 */
056 private EmptyField() {
057 }
058
059 /// call empty case
060 public Object execute(IFieldVisitor vis, Object param) {
061 return vis.emptyCase(this, param);
062 }
063 }
064
065 /**
066 * Non-empty field class.
067 */
068 public static class NonEmptyField implements IField {
069 /// local environment in this field
070 private LocalEnvironment _localEnv;
071
072 /**
073 * Constructor.
074 *
075 * @param localEnv local environment in this field
076 */
077 public NonEmptyField(LocalEnvironment localEnv) {
078 _localEnv = localEnv;
079 }
080
081 /**
082 * Return local environment.
083 *
084 * @return local environment in this field
085 */
086 public LocalEnvironment getLocalEnv() {
087 return _localEnv;
088 }
089
090 /**
091 * Set local environment.
092 *
093 * @param localEnv environment in this field
094 */
095 public void setLocalEnv(LocalEnvironment localEnv) {
096 _localEnv = localEnv;
097 }
098
099 /// call non-empty case
100 public Object execute(IFieldVisitor vis, Object param) {
101 return vis.nonEmptyCase(this, param);
102 }
103 }
104
105 /**
106 * Field visitor interface.
107 */
108 public static interface IFieldVisitor {
109 /**
110 * Empty case.
111 *
112 * @param host empty field
113 * @param param visitor-specific parameter
114 * @return visitor-specific return value
115 */
116 public abstract Object emptyCase(EmptyField host, Object param);
117
118 /**
119 * Non-empty case.
120 *
121 * @param host non-empty field
122 * @param param visitor-specific parameter
123 * @return visitor-specific return value
124 */
125 public abstract Object nonEmptyCase(NonEmptyField host, Object param);
126 }
127
128 /**
129 * Concrete local environment for the square bounded environment.
130 */
131 protected class LocalEnvironment extends ASquareLocalEnvironment {
132 /**
133 * Location.
134 */
135 protected Location _loc;
136
137 /**
138 * Direction.
139 */
140 protected Direction _dir;
141
142 /**
143 * Lambda to execute a move.
144 */
145 protected class MoveLambda implements ILambda {
146 /// target direction
147 protected Direction _newDir;
148 /// target location
149 protected Location _newLoc;
150
151 /**
152 * Constructor.
153 *
154 * @param le target local environment
155 */
156 public MoveLambda(LocalEnvironment le) {
157 _newLoc = makeLocation(le._loc.getX(), le._loc.getY());
158 _newDir = makeDirection(le._dir);
159 }
160
161 /**
162 * Execute the move.
163 *
164 * @param param not used
165 * @return null
166 */
167 public Object apply(Object param) {
168 _fieldMap[(int) _loc.getX()][(int) _loc.getY()] = EmptyField.Singleton;
169
170 // execute the movement
171 _loc = _newLoc;
172 _dir = _newDir;
173
174 _fieldMap[(int) _loc.getX()][(int) _loc.getY()] = new NonEmptyField(LocalEnvironment.this);
175
176 // deactivate all lambdas
177 deactivateMoveLambdas();
178 return null;
179 }
180 }
181
182 /**
183 * Construct a new local environment.
184 *
185 * @param loc location
186 * @param dir direction
187 */
188 public LocalEnvironment(Location loc, Direction dir) {
189 _loc = loc;
190 _dir = dir;
191 }
192
193 /**
194 * Accessor for the location.
195 *
196 * @return location
197 */
198 public Location location() {
199 return _loc;
200 }
201
202 /**
203 * Accessor for the direction.
204 *
205 * @return direction
206 */
207 public Direction direction() {
208 return _dir;
209 }
210
211 /**
212 * Make local environment in forward direction. Do not block yourself.
213 *
214 * @return new local environment in forward direction
215 */
216 protected ALocalEnv makeMoveFwdLocalEnv() {
217 // remove this local environment to prevent collision with itself
218 _fieldMap[(int) location().getX()][(int) location().getY()] = EmptyField.Singleton;
219 ALocalEnv le = makeLocalEnv(_loc.getNeighbor(_dir), _dir);
220 // add this local environment back in
221 _fieldMap[(int) location().getX()][(int) location().getY()] = new NonEmptyField(this);
222 return le;
223 }
224
225 /**
226 * Factory method for a move lambda.
227 *
228 * @param le local environment for the target
229 * @return move lambda to execute the move to the target
230 */
231 protected ILambda makeMoveLambda(ALocalEnv le) {
232 return new MoveLambda((LocalEnvironment) le);
233 }
234
235 }
236
237 /**
238 * Size of the pan area.
239 */
240 protected final int PAN_SIZE = 2000;
241
242 /**
243 * Center of the pan area.
244 */
245 protected final Point.Double PAN_CENTER = new Point.Double(PAN_SIZE / 2, PAN_SIZE / 2);
246
247 /**
248 * List of local environments in this global environment.
249 */
250 protected IField[][] _fieldMap;
251
252 /**
253 * "Singleton" instance for all out-of-bounds locations.
254 */
255 protected final LocalEnvironment _outOfBounds = new LocalEnvironment(new Location(-1, -1), new Direction());
256
257 {
258 // static initializer block
259 _outOfBounds.setState(NonEmptyLocalEnvState.Singleton);
260 }
261
262 /**
263 * Width.
264 */
265 protected int _width;
266
267 /**
268 * Height.
269 */
270 protected int _height;
271
272 /**
273 * Construct a new square bounded environment. Does not set this object up for actual use. Note: This constructor
274 * needs to exist and be public for the "environment selection" dialog to work.
275 *
276 * @param cmdFactory command factory to use
277 * @param sm security manager to control fish actions
278 */
279 public BoundedEnv(ICmdFactory cmdFactory, ISecurityAdapter sm) {
280 super(cmdFactory, sm);
281 _fieldMap = new IField[0][0];
282 }
283
284 /**
285 * Construct a new square bounded environment.
286 *
287 * @param cmdFactory command factory to use
288 * @param sm security manager to control fish actions
289 * @param width width of environment
290 * @param height height of environment
291 */
292 public BoundedEnv(ICmdFactory cmdFactory, ISecurityAdapter sm, int width, int height) {
293 super(cmdFactory, sm);
294 _width = width;
295 _height = height;
296 _fieldMap = new IField[width][height];
297 for (int y = 0; y < height; ++y) {
298 for (int x = 0; x < width; ++x) {
299 _fieldMap[x][y] = EmptyField.Singleton;
300 }
301 }
302 }
303
304 /**
305 * Add the fish to the global environment.
306 *
307 * @param localEnv local environment
308 * @param fish fish to add
309 */
310 protected void addFishToInternalData(ALocalEnv localEnv, AFish fish) {
311 _fieldMap
312 [(int) ((LocalEnvironment) localEnv).location().getX()]
313 [(int) ((LocalEnvironment) localEnv).location().getY()] = new NonEmptyField((LocalEnvironment) localEnv);
314 }
315
316 /**
317 * Remove the fish from the global environment.
318 *
319 * @param localEnv local environment
320 */
321 protected void removeFishFromInternalData(ALocalEnv localEnv) {
322 _fieldMap
323 [(int) ((LocalEnvironment) localEnv).location().getX()]
324 [(int) ((LocalEnvironment) localEnv).location().getY()] = EmptyField.Singleton;
325 }
326
327 /**
328 * Create a local environment for the position.
329 *
330 * @param loc location
331 * @param dir direction
332 * @return local environment
333 */
334 protected ASquareLocalEnvironment makeLocalEnv(final Location loc, final Direction dir) {
335 long x = (int) Math.floor(loc.getX());
336 long y = (int) Math.floor(loc.getY());
337 if (0 > x || 0 > y || x >= _width || y >= _height) {
338 return _outOfBounds;
339 }
340 else {
341 return ((ASquareLocalEnvironment) _fieldMap[(int) loc.getX()][(int) loc.getY()].execute(new IFieldVisitor() {
342 public Object emptyCase(EmptyField host, Object inp) {
343 return new LocalEnvironment(loc, dir);
344 }
345
346 public Object nonEmptyCase(NonEmptyField host, Object inp) {
347 return host.getLocalEnv();
348 }
349 }, null));
350 }
351 }
352
353 /**
354 * Create a local environment with the given data.
355 *
356 * @param loc location
357 * @param dir direction
358 * @return new local environment
359 */
360 protected ASquareLocalEnvironment createLocalEnvironment(Location loc, Direction dir) {
361 return new LocalEnvironment(loc, dir);
362 }
363
364 /**
365 * Factory method for parsing a stream of tokens and creating a global environment from it.
366 *
367 * @param l lexer
368 * @return new global environment
369 */
370 protected AGlobalEnv parseEnvironment(final Lexer l) {
371 // have to read size of environment
372 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
373 public Object defaultCase() {
374 throw new ParserException("Invalid token");
375 }
376
377 public Object numCase(final double width) {
378 // width was read
379 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() {
380 public Object defaultCase() {
381 throw new ParserException("Invalid token");
382 }
383
384 public Object numCase(double height) {
385 // height was read
386 BoundedEnv env = new BoundedEnv(_cmdFactory, _securityAdapter, (int) width, (int) height);
387
388 // parse fish
389 env.parseFish(l);
390
391 return env;
392 }
393 });
394 }
395 });
396 }
397
398 /**
399 * Get the environment settings class.
400 *
401 * @return environment settings class
402 */
403 public AEnvFactory makeEnvFactory() {
404 return new AEnvFactory() {
405 private JTextField _rowField;
406 private JTextField _colField;
407
408 {
409 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
410 add(new JLabel("rows: "));
411 add(_rowField = new JTextField("10"));
412 add(new JLabel(" cols: "));
413 add(_colField = new JTextField("10"));
414 }
415
416 public AGlobalEnv create() {
417 return new BoundedEnv(_cmdFactory,
418 _securityAdapter,
419 Integer.parseInt(_colField.getText()),
420 Integer.parseInt(_rowField.getText()));
421 }
422
423 public String toString() {
424 return BoundedEnv.class.getName();
425 }
426 };
427 }
428
429 /**
430 * Print file header.
431 *
432 * @param pw PrintWriter to use
433 */
434 protected void printHeader(PrintWriter pw) {
435 pw.println(getClass().getName() + ' ' + _width + ' ' + _height);
436 }
437
438 /**
439 * Get size of the display.
440 *
441 * @return size of the display in model coordinate units.
442 */
443 public Dimension getDisplaySize() {
444 return new Dimension(_width, _height);
445 }
446
447 /**
448 * The action to be executed if the display should return home.
449 *
450 * @param sa scroll adapter
451 */
452 public void returnHome(IScrollAdapter sa) {
453 sa.setCorner(0, 0);
454 }
455
456 /**
457 * Ask the model where to scroll, given where the user has scrolled. If the environment just acts like a normal
458 * panal, it should return pos without modification. If the environment recenters, it should return a position in
459 * the middle of the pan area. All coordinates are in model coordinate units.
460 *
461 * @param pos position where the user scrolled to
462 * @return position where the environment wants the view to be
463 * @see IDisplayAdapter#getPanDelta
464 */
465 public Point.Double getViewPosition(Point.Double pos) {
466 // the panel just acts like a normal panal, return position without any changes
467 return pos;
468 }
469
470 /**
471 * Ask the model how much to pan, given where the user scrolled. If the environment just acts like a normal panal,
472 * it should return (0,0). If the environment recenters, it should return delta without modification. All
473 * coordinates are in model coordinate units.
474 *
475 * @param delta how far the user scrolled
476 * @return how far the panel should scroll
477 * @see IDisplayAdapter#getViewPosition
478 */
479 public Point.Double getPanDelta(Point.Double delta) {
480 // we do not want the panel to keep track of position, do not pan
481 return new Point.Double(0, 0);
482 }
483
484
485
486 /*****************************************************************************************************************
487 * Tests follow
488 *****************************************************************************************************************/
489
490 /**
491 * Test cases for BoundedEnv.
492 *
493 * @author Mathias Ricken
494 */
495 public static class Test_BoundedEnv extends TestCase {
496 private ICmdFactory _cmdFactory;
497 private ISecurityAdapter _sm;
498 private BoundedEnv _env;
499 private IFishFactory _fishFactory;
500
501 private static final ILambda _notify = new ILambda() {
502 public Object apply(Object param) {
503 return "notifyCmd";
504 }
505 };
506 private static final ILambda _delete = new ILambda() {
507 public Object apply(Object param) {
508 return "deleteCmd";
509 }
510 };
511 private static final ILambda _add = new ILambda() {
512 public Object apply(Object param) {
513 return "addCmd";
514 }
515 };
516
517 public void setUp() throws Exception {
518 super.setUp();
519 _cmdFactory = new ICmdFactory() {
520 public ILambda makeNotifyCmd(ILambda lambda) {
521 return _notify;
522 }
523
524 public ILambda makeDeleteCmd(ALocalEnv env) {
525 return _delete;
526 }
527
528 public ILambda makeAddCmd(AFish fish) {
529 return _add;
530 }
531 };
532 _sm = new ISecurityAdapter() {
533 public void setProtected(boolean _protected) {
534 }
535 public ThreadGroup getFishThreadGroup() {
536 return null;
537 }
538 public ClassLoader getClassLoader() {
539 return null;
540 }
541 public void handleException(Throwable t) {
542 }
543 };
544
545 _env = new BoundedEnv(_cmdFactory, _sm, 10, 10);
546
547 _fishFactory = new IFishFactory() {
548 /**
549 * Create a new fish.
550 *
551 * @return new fish
552 */
553 public AFish createFish() {
554 return new GenericFish(Color.RED);
555 }
556 };
557 }
558
559 /**
560 * Test outOfRange.
561 */
562 public void testOutOfRange() {
563 assertTrue(_env._outOfBounds != _env.makeLocalEnv(new Point2D.Double(0, 0)));
564 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(10, 0)));
565 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(0, 10)));
566 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(10, 10)));
567 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(-1, 0)));
568 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(0, -1)));
569 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(-1, -1)));
570 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(-1, 9)));
571 assertTrue(_env._outOfBounds == _env.makeLocalEnv(new Point2D.Double(9, -1)));
572 }
573
574 int countNonEmpty() {
575 int count = 0;
576 BoundedEnv.IField[][] map = _env._fieldMap;
577 IFieldVisitor countVisitor = new IFieldVisitor() {
578 public Object emptyCase(EmptyField host, Object param) {
579 return new Integer(0);
580 }
581
582 public Object nonEmptyCase(NonEmptyField host, Object param) {
583 return new Integer(1);
584 }
585 };
586 for (int i = 0; i < map.length; i++) {
587 BoundedEnv.IField[] iFields = map[i];
588 for (int j = 0; j < iFields.length; j++) {
589 BoundedEnv.IField iField = iFields[j];
590 count += ((Integer) iField.execute(countVisitor, null)).intValue();
591 }
592 }
593 return count;
594 }
595
596 /**
597 * Test addFishToInternalData.
598 */
599 public void testAddFish() {
600 assertEquals(0, countNonEmpty());
601
602 AGlobalEnv.ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
603 GenericFish fTop = new GenericFish(Color.RED);
604 fTop.setLocalEnvironment(lTop);
605 _env.addFishToInternalData(lTop, fTop);
606
607 assertEquals(1, countNonEmpty());
608
609 AGlobalEnv.ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
610 GenericFish fBottom = new GenericFish(Color.RED);
611 fBottom.setLocalEnvironment(lBottom);
612 _env.addFishToInternalData(lBottom, fBottom);
613
614 assertEquals(2, countNonEmpty());
615 }
616
617 /**
618 * Test editFish.
619 */
620 public void testEditFish() {
621 assertEquals(0, countNonEmpty());
622
623 ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
624 GenericFish f = new GenericFish(Color.RED);
625 f.setLocalEnvironment(l);
626 _env.addFishToInternalData(l, f);
627 BoundedEnv.Direction d = l.direction();
628
629 assertEquals(1, countNonEmpty());
630 assertEquals(0.0, d.getAngle(), 0.01);
631
632 ILambda lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
633 assertEquals(1, countNonEmpty());
634 assertEquals(Math.PI / 2.0, d.getAngle(), 0.01);
635 assertEquals(NoOpLambda.instance(), lambda);
636
637 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
638 assertEquals(1, countNonEmpty());
639 assertEquals(Math.PI, d.getAngle(), 0.01);
640 assertEquals(NoOpLambda.instance(), lambda);
641
642 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
643 assertEquals(1, countNonEmpty());
644 assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01);
645 assertEquals(NoOpLambda.instance(), lambda);
646
647 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
648 assertEquals(0, countNonEmpty());
649 assertEquals(0, d.getAngle(), 0.01);
650 assertEquals(_delete, lambda);
651 }
652
653 /**
654 * Test getViewPosition.
655 */
656 public void testGetViewPosition() {
657 assertTrue(_env.getViewPosition(new Point.Double(0, 0)).equals(new Point.Double(0, 0)));
658 assertTrue(_env.getViewPosition(new Point.Double(1.0, 0)).equals(new Point.Double(1.0, 0)));
659 assertTrue(_env.getViewPosition(new Point.Double(1.2, 0)).equals(new Point.Double(1.2, 0)));
660 assertTrue(_env.getViewPosition(new Point.Double(0, 1.0)).equals(new Point.Double(0, 1.0)));
661 assertTrue(_env.getViewPosition(new Point.Double(0, 1.3)).equals(new Point.Double(0, 1.3)));
662 assertTrue(_env.getViewPosition(new Point.Double(-2.5, 0)).equals(new Point.Double(-2.5, 0)));
663 assertTrue(_env.getViewPosition(new Point.Double(-3.0, 0)).equals(new Point.Double(-3.0, 0)));
664 assertTrue(_env.getViewPosition(new Point.Double(0, -2.5)).equals(new Point.Double(0, -2.5)));
665 assertTrue(_env.getViewPosition(new Point.Double(0, -3.0)).equals(new Point.Double(0, -3.0)));
666 assertTrue(_env.getViewPosition(new Point.Double(2.0, 1.0)).equals(new Point.Double(2.0, 1.0)));
667 assertTrue(_env.getViewPosition(new Point.Double(-4.0, -2.3)).equals(new Point.Double(-4.0, -2.3)));
668 }
669
670 /**
671 * Test getPanDelta.
672 */
673 public void testGetPanDelta() {
674 assertTrue(_env.getPanDelta(new Point.Double(0, 0)).equals(new Point.Double(0, 0)));
675 assertTrue(_env.getPanDelta(new Point.Double(1.0, 0)).equals(new Point.Double(0, 0)));
676 assertTrue(_env.getPanDelta(new Point.Double(1.2, 0)).equals(new Point.Double(0, 0)));
677 assertTrue(_env.getPanDelta(new Point.Double(0, 1.0)).equals(new Point.Double(0, 0)));
678 assertTrue(_env.getPanDelta(new Point.Double(0, 1.3)).equals(new Point.Double(0, 0)));
679 assertTrue(_env.getPanDelta(new Point.Double(-2.5, 0)).equals(new Point.Double(0, 0)));
680 assertTrue(_env.getPanDelta(new Point.Double(-3.0, 0)).equals(new Point.Double(0, 0)));
681 assertTrue(_env.getPanDelta(new Point.Double(0, -2.5)).equals(new Point.Double(0, 0)));
682 assertTrue(_env.getPanDelta(new Point.Double(0, -3.0)).equals(new Point.Double(0, 0)));
683 assertTrue(_env.getPanDelta(new Point.Double(2.0, 1.0)).equals(new Point.Double(0, 0)));
684 assertTrue(_env.getPanDelta(new Point.Double(-4.0, -2.3)).equals(new Point.Double(0, 0)));
685 }
686 }
687
688 /**
689 * Test cases for BoundedEnv.LocalEnv.
690 *
691 * @author Mathias Ricken
692 */
693 public static class Test_BoundedEnv_LocalEnv extends TestCase {
694 private ICmdFactory _cmdFactory;
695 private ISecurityAdapter _sm;
696 private BoundedEnv _env;
697
698 private static class SuccessException extends RuntimeException {
699 public SuccessException() {
700 super();
701 }
702 }
703
704 private static final ILambda _notify = new ILambda() {
705 public Object apply(Object param) {
706 return "notifyCmd";
707 }
708 };
709 private static final ILambda _delete = new ILambda() {
710 public Object apply(Object param) {
711 return "deleteCmd";
712 }
713 };
714 private static final ILambda _add = new ILambda() {
715 public Object apply(Object param) {
716 return "addCmd";
717 }
718 };
719
720 public void setUp() throws Exception {
721 super.setUp();
722 _cmdFactory = new ICmdFactory() {
723 public ILambda makeNotifyCmd(ILambda lambda) {
724 return _notify;
725 }
726
727 public ILambda makeDeleteCmd(ALocalEnv env) {
728 return _delete;
729 }
730
731 public ILambda makeAddCmd(AFish fish) {
732 return _add;
733 }
734 };
735 _sm = new ISecurityAdapter() {
736 public void setProtected(boolean _protected) {
737 }
738 public ThreadGroup getFishThreadGroup() {
739 return null;
740 }
741 public ClassLoader getClassLoader() {
742 return null;
743 }
744 public void handleException(Throwable t) {
745 }
746 };
747
748 _env = new BoundedEnv(_cmdFactory, _sm, 10, 10);
749 }
750
751 /**
752 * Test local environment's execute.
753 */
754 public void testExecute() {
755 ALocalEnv l = _env.makeLocalEnv(new Point.Double(1, 1));
756
757 try {
758 l.execute(new AGlobalEnv.ILocalEnvVisitor() {
759 public Object emptyCase(ALocalEnv host, Object param) {
760 // ok
761 throw new SuccessException();
762 }
763
764 public Object nonEmptyCase(ALocalEnv host, Object param) {
765 throw new RuntimeException("Should be empty");
766 }
767 }, null);
768 fail("emptyCase should have been called --");
769 }
770 catch (SuccessException e) {
771 }
772
773
774 GenericFish f = new GenericFish(Color.RED);
775 f.setLocalEnvironment(l);
776 _env.addFish(l, f);
777 l.setState(NonEmptyLocalEnvState.Singleton);
778 try {
779 l.execute(new AGlobalEnv.ILocalEnvVisitor() {
780 public Object emptyCase(ALocalEnv host, Object param) {
781 throw new RuntimeException("Should be non-empty");
782 }
783
784 public Object nonEmptyCase(ALocalEnv host, Object param) {
785 // ok
786 throw new SuccessException();
787 }
788 }, null);
789 fail("nonEmptyCase should have been called --");
790 }
791 catch (SuccessException e) {
792 }
793
794 ALocalEnv l2 = _env.makeLocalEnv(new Point.Double(1, 1));
795 try {
796 l2.execute(new AGlobalEnv.ILocalEnvVisitor() {
797 public Object emptyCase(ALocalEnv host, Object param) {
798 throw new RuntimeException("Should be non-empty");
799 }
800
801 public Object nonEmptyCase(ALocalEnv host, Object param) {
802 // ok
803 throw new SuccessException();
804 }
805 }, null);
806 fail("nonEmptyCase should have been called --");
807 }
808 catch (SuccessException e) {
809 }
810
811 ALocalEnv l3 = _env.makeLocalEnv(new Point.Double(1.4, 1.6));
812 try {
813 l3.execute(new AGlobalEnv.ILocalEnvVisitor() {
814 public Object emptyCase(ALocalEnv host, Object param) {
815 throw new RuntimeException("Should be non-empty");
816 }
817
818 public Object nonEmptyCase(ALocalEnv host, Object param) {
819 // ok
820 throw new SuccessException();
821 }
822 }, null);
823 fail("nonEmptyCase should have been called --");
824 }
825 catch (SuccessException e) {
826 }
827
828 ALocalEnv l4 = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
829 try {
830 l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
831 public Object emptyCase(ALocalEnv host, Object param) {
832 // ok
833 throw new SuccessException();
834 }
835
836 public Object nonEmptyCase(ALocalEnv host, Object param) {
837 throw new RuntimeException("Should be empty");
838 }
839 }, null);
840 fail("emptyCase should have been called --");
841 }
842 catch (SuccessException e) {
843 }
844
845 GenericFish f4 = new GenericFish(Color.RED);
846 f4.setLocalEnvironment(l4);
847 _env.addFish(l4, f4);
848 l4.setState(NonEmptyLocalEnvState.Singleton);
849 try {
850 l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
851 public Object emptyCase(ALocalEnv host, Object param) {
852 throw new RuntimeException("Should be non-empty");
853 }
854
855 public Object nonEmptyCase(ALocalEnv host, Object param) {
856 // ok
857 throw new SuccessException();
858 }
859 }, null);
860 fail("nonEmptyCase should have been called --");
861 }
862 catch (SuccessException e) {
863 }
864 }
865
866 /**
867 * Test local environment's tryMoveFwd.
868 */
869 public void testTryMoveFwd() {
870 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
871 GenericFish fTop = new GenericFish(Color.RED);
872 fTop.setLocalEnvironment(lTop);
873 _env.addFish(lTop, fTop);
874 lTop.setState(NonEmptyLocalEnvState.Singleton);
875
876 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
877 GenericFish fBottom = new GenericFish(Color.RED);
878 fBottom.setLocalEnvironment(lBottom);
879 _env.addFish(lBottom, fBottom);
880 lBottom.setState(NonEmptyLocalEnvState.Singleton);
881
882 // move lBottom into lTop --> blocked
883 Integer i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
884 public Object apply(Object param) {
885 // ok
886 return new Integer(456);
887 }
888 }, new IOpenCommand() {
889 public Object apply(Object param) {
890 throw new RuntimeException("Should be blocked");
891 }
892 });
893 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
894 new Integer(456),
895 i);
896
897 // move lTop --> open, don't move
898 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
899 public Object apply(Object param) {
900 throw new RuntimeException("Should be open");
901 }
902 }, new IOpenCommand() {
903 public Object apply(Object param) {
904 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
905 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
906 DeactivatableLambda.class,
907 param.getClass());
908 // ok
909 return new Integer(123);
910 }
911 });
912 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
913
914 // move lBottom into lTop --> blocked
915 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
916 public Object apply(Object param) {
917 // ok
918 return new Integer(789);
919 }
920 }, new IOpenCommand() {
921 public Object apply(Object param) {
922 throw new RuntimeException("Should be blocked");
923 }
924 });
925 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
926 new Integer(789),
927 i);
928
929 // move lTop --> open, move
930 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
931 public Object apply(Object param) {
932 throw new RuntimeException("Should be open");
933 }
934 }, new IOpenCommand() {
935 public Object apply(Object param) {
936 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
937 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
938 DeactivatableLambda.class,
939 param.getClass());
940 // ok, make move
941 ((ILambda) param).apply(null);
942 return new Integer(111);
943 }
944 });
945 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(111), i);
946
947 // move lBottom --> open
948 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
949 public Object apply(Object param) {
950 throw new RuntimeException("Should be open");
951 }
952 }, new IOpenCommand() {
953 public Object apply(Object param) {
954 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
955 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
956 DeactivatableLambda.class,
957 param.getClass());
958 // ok
959 return new Integer(222);
960 }
961 });
962 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(222), i);
963
964 // move lTop into edge --> blocked
965 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
966 public Object apply(Object param) {
967 // ok
968 return new Integer(333);
969 }
970 }, new IOpenCommand() {
971 public Object apply(Object param) {
972 throw new RuntimeException("Should be blocked");
973 }
974 });
975 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
976 new Integer(333),
977 i);
978
979 // turn and move lTop --> open
980 lTop.turnRight(fTop, Math.PI / 2.0);
981 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
982 public Object apply(Object param) {
983 throw new RuntimeException("Should be open");
984 }
985 }, new IOpenCommand() {
986 public Object apply(Object param) {
987 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
988 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
989 DeactivatableLambda.class,
990 param.getClass());
991 // ok
992 return new Integer(444);
993 }
994 });
995 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(444), i);
996 }
997
998 int countNonEmpty() {
999 int count = 0;
1000 BoundedEnv.IField[][] map = _env._fieldMap;
1001 IFieldVisitor countVisitor = new IFieldVisitor() {
1002 public Object emptyCase(EmptyField host, Object param) {
1003 return new Integer(0);
1004 }
1005
1006 public Object nonEmptyCase(NonEmptyField host, Object param) {
1007 return new Integer(1);
1008 }
1009 };
1010 for (int i = 0; i < map.length; i++) {
1011 BoundedEnv.IField[] iFields = map[i];
1012 for (int j = 0; j < iFields.length; j++) {
1013 BoundedEnv.IField iField = iFields[j];
1014 count += ((Integer) iField.execute(countVisitor, null)).intValue();
1015 }
1016 }
1017 return count;
1018 }
1019
1020 /**
1021 * Test local environment's tryBreedFwd.
1022 */
1023 public void testTryBreedFwd() {
1024 assertEquals(0, countNonEmpty());
1025
1026 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1027 GenericFish fTop = new GenericFish(Color.RED);
1028 fTop.setLocalEnvironment(lTop);
1029 _env.addFish(lTop, fTop);
1030 lTop.setState(NonEmptyLocalEnvState.Singleton);
1031
1032 assertEquals(1, countNonEmpty());
1033
1034 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1035 GenericFish fBottom = new GenericFish(Color.RED);
1036 fBottom.setLocalEnvironment(lBottom);
1037 _env.addFish(lBottom, fBottom);
1038 lBottom.setState(NonEmptyLocalEnvState.Singleton);
1039
1040 assertEquals(2, countNonEmpty());
1041
1042 // breed lBottom into lTop --> blocked
1043 Integer i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1044 public Object apply(Object param) {
1045 // ok
1046 return new Integer(456);
1047 }
1048 }, new IOpenCommand() {
1049 public Object apply(Object param) {
1050 throw new RuntimeException("Should be blocked");
1051 }
1052 });
1053 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1054 new Integer(456),
1055 i);
1056
1057 // breed lTop --> open, don't breed
1058 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1059 public Object apply(Object param) {
1060 throw new RuntimeException("Should be open");
1061 }
1062 }, new IOpenCommand() {
1063 public Object apply(Object param) {
1064 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1065 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1066 DeactivatableLambda.class,
1067 param.getClass());
1068 // ok
1069 return new Integer(123);
1070 }
1071 });
1072 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
1073
1074 assertEquals(2, countNonEmpty());
1075
1076 // breed lBottom into lTop --> blocked
1077 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1078 public Object apply(Object param) {
1079 // ok
1080 return new Integer(456);
1081 }
1082 }, new IOpenCommand() {
1083 public Object apply(Object param) {
1084 throw new RuntimeException("Should be blocked");
1085 }
1086 });
1087 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1088 new Integer(456),
1089 i);
1090
1091 // breed lTop --> open, breed
1092 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1093 public Object apply(Object param) {
1094 throw new RuntimeException("Should be open");
1095 }
1096 }, new IOpenCommand() {
1097 public Object apply(Object param) {
1098 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1099 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1100 DeactivatableLambda.class,
1101 param.getClass());
1102 // ok, breed
1103 ((ILambda) param).apply(null);
1104 return new Integer(123);
1105 }
1106 });
1107 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
1108
1109 assertEquals(3, countNonEmpty());
1110
1111 // breed lBottom into lTop --> blocked
1112 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
1113 public Object apply(Object param) {
1114 // ok
1115 return new Integer(456);
1116 }
1117 }, new IOpenCommand() {
1118 public Object apply(Object param) {
1119 throw new RuntimeException("Should be blocked");
1120 }
1121 });
1122 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1123 new Integer(456),
1124 i);
1125
1126 // breed lTop --> blocked
1127 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1128 public Object apply(Object param) {
1129 // ok
1130 return new Integer(456);
1131 }
1132 }, new IOpenCommand() {
1133 public Object apply(Object param) {
1134 throw new RuntimeException("Should be blocked");
1135 }
1136 });
1137 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1138 new Integer(456),
1139 i);
1140
1141 // turn and breed lTop --> open, don't breed
1142 lTop.turnRight(fTop, Math.PI / 2.0);
1143 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1144 public Object apply(Object param) {
1145 throw new RuntimeException("Should be open");
1146 }
1147 }, new IOpenCommand() {
1148 public Object apply(Object param) {
1149 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1150 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1151 DeactivatableLambda.class,
1152 param.getClass());
1153 // ok
1154 return new Integer(789);
1155 }
1156 });
1157 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
1158
1159 assertEquals(3, countNonEmpty());
1160
1161 // turn and breed lTop --> open, breed
1162 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1163 public Object apply(Object param) {
1164 throw new RuntimeException("Should be open");
1165 }
1166 }, new IOpenCommand() {
1167 public Object apply(Object param) {
1168 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
1169 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
1170 DeactivatableLambda.class,
1171 param.getClass());
1172 // ok, breed
1173 ((ILambda) param).apply(null);
1174 return new Integer(789);
1175 }
1176 });
1177 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
1178
1179 assertEquals(4, countNonEmpty());
1180
1181 // turn and breed lTop --> blocked
1182 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
1183 public Object apply(Object param) {
1184 // ok
1185 return new Integer(789);
1186 }
1187 }, new IOpenCommand() {
1188 public Object apply(Object param) {
1189 throw new RuntimeException("Should be blocked");
1190 }
1191 });
1192 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
1193 new Integer(789),
1194 i);
1195
1196 assertEquals(4, countNonEmpty());
1197 }
1198
1199 /**
1200 * Test local environment's turnRight.
1201 */
1202 public void testTurnRight() {
1203 ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1204 GenericFish f = new GenericFish(Color.RED);
1205 f.setLocalEnvironment(l);
1206 _env.addFish(l, f);
1207 l.setState(NonEmptyLocalEnvState.Singleton);
1208 BoundedEnv.Direction d = l.direction();
1209
1210 assertEquals(0.0, d.getAngle(), 0.01);
1211 l.turnRight(f, Math.PI / 2);
1212 assertEquals(Math.PI / 2.0, d.getAngle(), 0.01);
1213 l.turnRight(f, Math.PI / 2);
1214 assertEquals(Math.PI, d.getAngle(), 0.01);
1215 l.turnRight(f, Math.PI / 2);
1216 assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01);
1217 l.turnRight(f, Math.PI / 2);
1218 assertEquals(0, d.getAngle(), 0.01);
1219 }
1220
1221 /**
1222 * Test local environment's removeFish.
1223 */
1224 public void testRemoveFish() {
1225 assertEquals(0, countNonEmpty());
1226
1227 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
1228 GenericFish fTop = new GenericFish(Color.RED);
1229 fTop.setLocalEnvironment(lTop);
1230 _env.addFish(lTop, fTop);
1231 lTop.setState(NonEmptyLocalEnvState.Singleton);
1232
1233 assertEquals(1, countNonEmpty());
1234
1235 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
1236 GenericFish fBottom = new GenericFish(Color.RED);
1237 fBottom.setLocalEnvironment(lBottom);
1238 _env.addFish(lBottom, fBottom);
1239 lBottom.setState(NonEmptyLocalEnvState.Singleton);
1240
1241 assertEquals(2, countNonEmpty());
1242
1243 lTop.removeFish(fTop);
1244
1245 assertEquals(1, countNonEmpty());
1246
1247 lBottom.removeFish(fBottom);
1248
1249 assertEquals(0, countNonEmpty());
1250 }
1251
1252 /**
1253 * Test to make sure only one move lambda can be executed.
1254 */
1255 public void testOnlyOneMove() {
1256 ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(5.0, 5.0));
1257 GenericFish f = new GenericFish(Color.RED);
1258 f.setLocalEnvironment(l);
1259 _env.addFish(l, f);
1260 l.setState(NonEmptyLocalEnvState.Singleton);
1261
1262 final LinkedList<ILambda> lambdas = new LinkedList<ILambda>();
1263 l.tryMoveFwd(f, new IBlockedCommand() {
1264 public Object apply(Object param) {
1265 throw new RuntimeException("Should be open");
1266 }
1267 }, new IOpenCommand() {
1268 public Object apply(Object param) {
1269 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1270 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1271 DeactivatableLambda.class,
1272 param.getClass());
1273 lambdas.add((ILambda)param);
1274 return null;
1275 }
1276 });
1277 f.turnRight();
1278 l.tryMoveFwd(f, new IBlockedCommand() {
1279 public Object apply(Object param) {
1280 throw new RuntimeException("Should be open");
1281 }
1282 }, new IOpenCommand() {
1283 public Object apply(Object param) {
1284 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1285 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1286 DeactivatableLambda.class,
1287 param.getClass());
1288 lambdas.add((ILambda)param);
1289 return null;
1290 }
1291 });
1292
1293 assertEquals(2, lambdas.size());
1294
1295 lambdas.get(0).apply(null);
1296 BoundedEnv.Location loc = l.location();
1297 assertTrue(loc.same(_env.makeLocation(5.0, 4.0)));
1298
1299 lambdas.get(1).apply(null);
1300 loc = l.location();
1301 assertTrue(loc.same(_env.makeLocation(5.0, 4.0)));
1302
1303 lambdas.clear();
1304 l.tryMoveFwd(f, new IBlockedCommand() {
1305 public Object apply(Object param) {
1306 throw new RuntimeException("Should be open");
1307 }
1308 }, new IOpenCommand() {
1309 public Object apply(Object param) {
1310 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1311 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1312 DeactivatableLambda.class,
1313 param.getClass());
1314 lambdas.add((ILambda)param);
1315 return null;
1316 }
1317 });
1318 f.turnRight();
1319 l.tryMoveFwd(f, new IBlockedCommand() {
1320 public Object apply(Object param) {
1321 throw new RuntimeException("Should be open");
1322 }
1323 }, new IOpenCommand() {
1324 public Object apply(Object param) {
1325 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1326 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1327 DeactivatableLambda.class,
1328 param.getClass());
1329 lambdas.add((ILambda)param);
1330 return null;
1331 }
1332 });
1333
1334 assertEquals(2, lambdas.size());
1335
1336 lambdas.get(1).apply(null);
1337 loc = l.location();
1338 assertTrue(loc.same(_env.makeLocation(6.0, 4.0)));
1339
1340 lambdas.get(0).apply(null);
1341 loc = l.location();
1342 assertTrue(loc.same(_env.makeLocation(6.0, 4.0)));
1343 }
1344 }
1345
1346 }