001    package sysModel.env;
002    
003    import controller.IScrollAdapter;
004    import controller.IDisplayAdapter;
005    import junit.framework.TestCase;
006    import lrs.IAlgo;
007    import lrs.LRStruct;
008    import lrs.visitor.GetLength;
009    import lrs.visitor.Remove;
010    import model.ILambda;
011    import model.fish.GenericFish;
012    import sysModel.ICmdFactory;
013    import sysModel.ISecurityAdapter;
014    import sysModel.NoOpLambda;
015    import sysModel.fish.IFishFactory;
016    import sysModel.fish.AFish;
017    import sysModel.parser.Lexer;
018    
019    import java.awt.*;
020    import java.awt.event.MouseEvent;
021    import java.util.LinkedList;
022    import java.io.PrintWriter;
023    
024    /**
025     * Implementation of a square unbounded environment.
026     *
027     * @author Mathias G. Ricken
028     */
029    public class UnboundedEnv extends ASquareEnv {
030    
031        /**
032         * Concrete local environment for the square unbounded environment.
033         */
034        protected class LocalEnvironment extends ASquareLocalEnvironment {
035            /**
036             * Location.
037             */
038            protected Location _loc;
039    
040            /**
041             * Direction.
042             */
043            protected Direction _dir;
044    
045            /**
046             * Lambda to execute a move.
047             */
048            protected class MoveLambda implements ILambda {
049                /// target direction
050                protected Direction _newDir;
051                /// target location
052                protected Location _newLoc;
053    
054                /**
055                 * Constructor.
056                 *
057                 * @param le target local environment
058                 */
059                public MoveLambda(LocalEnvironment le) {
060                    _newLoc = makeLocation(le._loc.getX(), le._loc.getY());
061                    _newDir = makeDirection(le._dir);
062                }
063    
064                /**
065                 * Execute the move.
066                 *
067                 * @param param not used
068                 * @return null
069                 */
070                public Object apply(Object param) {
071                    // execute the movement
072                    _loc = _newLoc;
073                    _dir = _newDir;
074    
075                    // deactivate all lambdas
076                    deactivateMoveLambdas();
077                    return null;
078                }
079            }
080    
081            /**
082             * Construct a new local environment.
083             *
084             * @param loc location
085             * @param dir direction
086             */
087            public LocalEnvironment(Location loc, Direction dir) {
088                _loc = loc;
089                _dir = dir;
090            }
091    
092            /**
093             * Accessor for the location.
094             *
095             * @return location
096             */
097            public Location location() {
098                return _loc;
099            }
100    
101            /**
102             * Accessor for the direction.
103             *
104             * @return direction
105             */
106            public Direction direction() {
107                return _dir;
108            }
109    
110            /**
111             * Make local environment in forward direction. Do not block yourself.
112             *
113             * @return new local environment in forward direction
114             */
115            protected ALocalEnv makeMoveFwdLocalEnv() {
116                // remove this local environment to prevent collision with itself
117                _localEnvList.execute(Remove.Singleton, this);
118                ALocalEnv le = makeLocalEnv(_loc.getNeighbor(_dir), _dir);
119                // add this local environment back in
120                _localEnvList.insertFront(this);
121                return le;
122            }
123    
124            /**
125             * Factory method for a move lambda.
126             *
127             * @param le local environment for the target
128             * @return move lambda to execute the move to the target
129             */
130            protected ILambda makeMoveLambda(ALocalEnv le) {
131                return new MoveLambda((LocalEnvironment) le);
132            }
133        }
134    
135        /**
136         * Size of the pan area.
137         */
138        protected final int PAN_SIZE = 2000;
139    
140        /**
141         * Center of the pan area.
142         */
143        protected final Point.Double PAN_CENTER = new Point.Double(PAN_SIZE / 2, PAN_SIZE / 2);
144    
145        /**
146         * List of local environments in this global environment.
147         */
148        private LRStruct _localEnvList;
149    
150        /**
151         * Construct a new square unbounded environment.
152         *
153         * @param cmdFactory command factory to use
154         * @param sm         security manager to control fish actions
155         */
156        public UnboundedEnv(ICmdFactory cmdFactory, ISecurityAdapter sm) {
157            super(cmdFactory, sm);
158            _localEnvList = new LRStruct();
159        }
160    
161        /**
162         * Add the fish to the global environment.
163         *
164         * @param localEnv local environment
165         * @param fish     fish to add
166         */
167        protected void addFishToInternalData(ALocalEnv localEnv, AFish fish) {
168            _localEnvList.insertFront(localEnv);
169        }
170    
171        /**
172         * Remove the fish from the global environment.
173         *
174         * @param localEnv local environment
175         */
176        protected void removeFishFromInternalData(ALocalEnv localEnv) {
177            _localEnvList.execute(Remove.Singleton, localEnv);
178        }
179    
180        /**
181         * Create a local environment for the position.
182         *
183         * @param loc location
184         * @param dir direction
185         * @return local environment
186         */
187        protected ASquareLocalEnvironment makeLocalEnv(final Location loc, final Direction dir) {
188            return (ASquareLocalEnvironment) _localEnvList.execute(new IAlgo() {
189                /**
190                 * Operates on a non-empty LRStruct host, given an input object.
191                 *
192                 * @param host a non-empty LRStruct.
193                 * @param inp  input object needed by this IAlgo.
194                 * @return an appropriate output object.
195                 */
196                public Object nonEmptyCase(LRStruct host, Object inp) {
197                    LocalEnvironment localEnv = (LocalEnvironment) host.getFirst();
198                    if (localEnv.location().inField(loc)) {
199                        return localEnv;
200                    }
201                    else {
202                        return host.getRest().execute(this, inp);
203                    }
204                }
205    
206                /**
207                 * Operates on an empty LRStruct host, given an input object.
208                 *
209                 * @param host an empty LRStruct.
210                 * @param inp  input object needed by this IAlgo.
211                 * @return an appropriate output object.
212                 */
213                public Object emptyCase(LRStruct host, Object inp) {
214                    return new LocalEnvironment(loc, dir);
215                }
216            }, null);
217        }
218    
219        /**
220         * Factory method for parsing a stream of tokens and creating a global environment from it.
221         *
222         * @param l lexer to use
223         * @return new global environment
224         */
225        protected AGlobalEnv parseEnvironment(Lexer l) {
226            UnboundedEnv env = new UnboundedEnv(_cmdFactory, _securityAdapter);
227            env.parseFish(l);
228            return env;
229        }
230    
231        /**
232         * Get the environment settings class.
233         *
234         * @return environment settings class
235         */
236        public AEnvFactory makeEnvFactory() {
237            return new AEnvFactory() {
238                public AGlobalEnv create() {
239                    return new UnboundedEnv(_cmdFactory, _securityAdapter);
240                }
241    
242                public String toString() {
243                    return UnboundedEnv.class.getName();
244                }
245            };
246        }
247    
248        /**
249         * Print file header.
250         *
251         * @param pw PrintWriter to use
252         */
253        protected void printHeader(PrintWriter pw) {
254            pw.println(getClass().getName());
255        }
256    
257        /**
258         * Get size of the display.
259         *
260         * @return size of the display in model coordinate units.
261         */
262        public Dimension getDisplaySize() {
263            return new Dimension(PAN_SIZE, PAN_SIZE);
264        }
265    
266        /**
267         * The action to be executed if the display should return home.
268         *
269         * @param sa scroll adapter
270         */
271        public void returnHome(IScrollAdapter sa) {
272            sa.setCorner((int) PAN_CENTER.x, (int) PAN_CENTER.y);
273            sa.resetScrolling();
274        }
275    
276        /**
277         * Ask the model where to scroll, given where the user has scrolled. If the environment just acts like a normal
278         * panal, it should return pos without modification. If the environment recenters, it should return a position in
279         * the middle of the pan area. All coordinates are in model coordinate units.
280         *
281         * @param pos position where the user scrolled to
282         * @return position where the environment wants the view to be
283         * @see IDisplayAdapter#getPanDelta
284         */
285        public Point.Double getViewPosition(Point.Double pos) {
286            // the panel always gets recentered after moving it, so return the center position
287            return PAN_CENTER;
288        }
289    
290        /**
291         * Ask the model how much to pan, given where the user scrolled. If the environment just acts like a normal panal,
292         * it should return (0,0). If the environment recenters, it should return delta without modification. All
293         * coordinates are in model coordinate units.
294         *
295         * @param delta how far the user scrolled
296         * @return how far the panel should scroll
297         * @see IDisplayAdapter#getViewPosition
298         */
299        public Point.Double getPanDelta(Point.Double delta) {
300            // we want the panel to keep track of the position, so return the delta as pan value
301            return delta;
302        }
303    
304        /*****************************************************************************************************************
305         * Tests follow
306         *****************************************************************************************************************/
307    
308        /**
309         * Test cases for UnboundedEnv.
310         *
311         * @author Mathias Ricken
312         */
313        public static class Test_UnboundedEnv extends TestCase {
314            private ICmdFactory _cmdFactory;
315            private ISecurityAdapter _sm;
316            private UnboundedEnv _env;
317            private IFishFactory _fishFactory;
318    
319            private static final ILambda _notify = new ILambda() {
320                public Object apply(Object param) {
321                    return "notifyCmd";
322                }
323            };
324            private static final ILambda _delete = new ILambda() {
325                public Object apply(Object param) {
326                    return "deleteCmd";
327                }
328            };
329            private static final ILambda _add = new ILambda() {
330                public Object apply(Object param) {
331                    return "addCmd";
332                }
333            };
334    
335            public void setUp() throws Exception {
336                super.setUp();
337                _cmdFactory = new ICmdFactory() {
338                    public ILambda makeNotifyCmd(ILambda lambda) {
339                        return _notify;
340                    }
341    
342                    public ILambda makeDeleteCmd(ALocalEnv env) {
343                        return _delete;
344                    }
345    
346                    public ILambda makeAddCmd(AFish fish) {
347                        return _add;
348                    }
349                };
350                _sm = new ISecurityAdapter() {
351                    public void setProtected(boolean _protected) {
352                    }
353                    public ThreadGroup getFishThreadGroup() {
354                        return null;
355                    }
356                    public ClassLoader getClassLoader() {
357                        return null;
358                    }
359                    public void handleException(Throwable t) {
360                    }
361                };
362    
363                _env = new UnboundedEnv(_cmdFactory, _sm);
364    
365                _fishFactory = new IFishFactory() {
366                    /**
367                     * Create a new fish.
368                     *
369                     * @return new fish
370                     */
371                    public AFish createFish() {
372                        return new GenericFish(Color.RED);
373                    }
374                };
375            }
376    
377            /**
378             * Test addFishToInternalData.
379             */
380            public void testAddFish() {
381                LRStruct lrs = _env._localEnvList;
382    
383                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
384    
385                ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
386                GenericFish fTop = new GenericFish(Color.RED);
387                fTop.setLocalEnvironment(lTop);
388                _env.addFishToInternalData(lTop, fTop);
389    
390                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
391    
392                ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
393                GenericFish fBottom = new GenericFish(Color.RED);
394                fBottom.setLocalEnvironment(lBottom);
395                _env.addFishToInternalData(lBottom, fBottom);
396    
397                assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
398            }
399    
400            /**
401             * Test editFish.
402             */
403            public void testEditFish() {
404                LRStruct lrs = _env._localEnvList;
405    
406                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
407    
408                ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
409                GenericFish f = new GenericFish(Color.RED);
410                f.setLocalEnvironment(l);
411                _env.addFishToInternalData(l, f);
412                Direction d = l.direction();
413    
414                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
415                assertEquals(0.0, d.getAngle(), 0.01);
416    
417                ILambda lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
418                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
419                assertEquals(Math.PI / 2.0, d.getAngle(), 0.01);
420                assertEquals(NoOpLambda.instance(), lambda);
421    
422                lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
423                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
424                assertEquals(Math.PI, d.getAngle(), 0.01);
425                assertEquals(NoOpLambda.instance(), lambda);
426    
427                lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
428                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
429                assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01);
430                assertEquals(NoOpLambda.instance(), lambda);
431    
432                lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1);
433                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
434                assertEquals(0, d.getAngle(), 0.01);
435                assertEquals(_delete, lambda);
436            }
437    
438            /**
439             * Test getViewPosition.
440             */
441            public void testGetViewPosition() {
442                Point.Double panCenter = _env.PAN_CENTER;
443    
444                assertTrue(_env.getViewPosition(new Point.Double(0, 0)).equals(panCenter));
445                assertTrue(_env.getViewPosition(new Point.Double(1.0, 0)).equals(panCenter));
446                assertTrue(_env.getViewPosition(new Point.Double(1.2, 0)).equals(panCenter));
447                assertTrue(_env.getViewPosition(new Point.Double(0, 1.0)).equals(panCenter));
448                assertTrue(_env.getViewPosition(new Point.Double(0, 1.3)).equals(panCenter));
449                assertTrue(_env.getViewPosition(new Point.Double(-2.5, 0)).equals(panCenter));
450                assertTrue(_env.getViewPosition(new Point.Double(-3.0, 0)).equals(panCenter));
451                assertTrue(_env.getViewPosition(new Point.Double(0, -2.5)).equals(panCenter));
452                assertTrue(_env.getViewPosition(new Point.Double(0, -3.0)).equals(panCenter));
453                assertTrue(_env.getViewPosition(new Point.Double(2.0, 1.0)).equals(panCenter));
454                assertTrue(_env.getViewPosition(new Point.Double(-4.0, -2.3)).equals(panCenter));
455            }
456    
457            /**
458             * Test getPanDelta.
459             */
460            public void testGetPanDelta() {
461                assertTrue(_env.getPanDelta(new Point.Double(0, 0)).equals(new Point.Double(0, 0)));
462                assertTrue(_env.getPanDelta(new Point.Double(1.0, 0)).equals(new Point.Double(1.0, 0)));
463                assertTrue(_env.getPanDelta(new Point.Double(1.2, 0)).equals(new Point.Double(1.2, 0)));
464                assertTrue(_env.getPanDelta(new Point.Double(0, 1.0)).equals(new Point.Double(0, 1.0)));
465                assertTrue(_env.getPanDelta(new Point.Double(0, 1.3)).equals(new Point.Double(0, 1.3)));
466                assertTrue(_env.getPanDelta(new Point.Double(-2.5, 0)).equals(new Point.Double(-2.5, 0)));
467                assertTrue(_env.getPanDelta(new Point.Double(-3.0, 0)).equals(new Point.Double(-3.0, 0)));
468                assertTrue(_env.getPanDelta(new Point.Double(0, -2.5)).equals(new Point.Double(0, -2.5)));
469                assertTrue(_env.getPanDelta(new Point.Double(0, -3.0)).equals(new Point.Double(0, -3.0)));
470                assertTrue(_env.getPanDelta(new Point.Double(2.0, 1.0)).equals(new Point.Double(2.0, 1.0)));
471                assertTrue(_env.getPanDelta(new Point.Double(-4.0, -2.3)).equals(new Point.Double(-4.0, -2.3)));
472            }
473        }
474    
475        /**
476         * Test cases for UnboundedEnv.LocalEnv.
477         *
478         * @author Mathias Ricken
479         */
480        public static class Test_UnboundedEnv_LocalEnv extends TestCase {
481            private ICmdFactory _cmdFactory;
482            private ISecurityAdapter _sm;
483            private UnboundedEnv _env;
484    
485            private static class SuccessException extends RuntimeException {
486                public SuccessException() {
487                    super();
488                }
489            }
490    
491            private static final ILambda _notify = new ILambda() {
492                public Object apply(Object param) {
493                    return "notifyCmd";
494                }
495            };
496            private static final ILambda _delete = new ILambda() {
497                public Object apply(Object param) {
498                    return "deleteCmd";
499                }
500            };
501            private static final ILambda _add = new ILambda() {
502                public Object apply(Object param) {
503                    return "addCmd";
504                }
505            };
506    
507            public void setUp() throws Exception {
508                super.setUp();
509                _cmdFactory = new ICmdFactory() {
510                    public ILambda makeNotifyCmd(ILambda lambda) {
511                        return _notify;
512                    }
513    
514                    public ILambda makeDeleteCmd(ALocalEnv env) {
515                        return _delete;
516                    }
517    
518                    public ILambda makeAddCmd(AFish fish) {
519                        return _add;
520                    }
521                };
522                _sm = new ISecurityAdapter() {
523                    public void setProtected(boolean _protected) {
524                    }
525                    public ThreadGroup getFishThreadGroup() {
526                        return null;
527                    }
528                    public ClassLoader getClassLoader() {
529                        return null;
530                    }
531                    public void handleException(Throwable t) {
532                    }
533                };
534    
535                _env = new UnboundedEnv(_cmdFactory, _sm);
536            }
537    
538            /**
539             * Test local environment's execute.
540             */
541            public void testExecute() {
542                ALocalEnv l = _env.makeLocalEnv(new Point.Double(1, 1));
543    
544                try {
545                    l.execute(new AGlobalEnv.ILocalEnvVisitor() {
546                        public Object emptyCase(ALocalEnv host, Object param) {
547                            // ok
548                            throw new SuccessException();
549                        }
550    
551                        public Object nonEmptyCase(ALocalEnv host, Object param) {
552                            throw new RuntimeException("Should be empty --");
553                        }
554                    }, null);
555                    fail("emptyCase should have been called --");
556                }
557                catch (SuccessException e) {
558                }
559    
560                GenericFish f = new GenericFish(Color.RED);
561                f.setLocalEnvironment(l);
562                _env.addFish(l, f);
563    
564                ALocalEnv l2 = _env.makeLocalEnv(new Point.Double(1, 1));
565                try {
566                    l2.execute(new AGlobalEnv.ILocalEnvVisitor() {
567                        public Object emptyCase(ALocalEnv host, Object param) {
568                            throw new RuntimeException("Should be non-empty --");
569                        }
570    
571                        public Object nonEmptyCase(ALocalEnv host, Object param) {
572                            // ok
573                            throw new SuccessException();
574                        }
575                    }, null);
576                    fail("nonEmptyCase should have been falled --");
577                }
578                catch (SuccessException e) {
579                }
580    
581                ALocalEnv l3 = _env.makeLocalEnv(new Point.Double(1.4, 1.6));
582                try {
583                    l3.execute(new AGlobalEnv.ILocalEnvVisitor() {
584                        public Object emptyCase(ALocalEnv host, Object param) {
585                            throw new RuntimeException("Should be non-empty --");
586                        }
587    
588                        public Object nonEmptyCase(ALocalEnv host, Object param) {
589                            // ok
590                            throw new SuccessException();
591                        }
592                    }, null);
593                    fail("nonEmptyCase should have been falled --");
594                }
595                catch (SuccessException e) {
596                }
597    
598                ALocalEnv l4 = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
599                try {
600                    l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
601                        public Object emptyCase(ALocalEnv host, Object param) {
602                            // ok
603                            throw new SuccessException();
604                        }
605    
606                        public Object nonEmptyCase(ALocalEnv host, Object param) {
607                            throw new RuntimeException("Should be empty --");
608                        }
609                    }, null);
610                    fail("emptyCase should have been falled --");
611                }
612                catch (SuccessException e) {
613                }
614    
615                GenericFish f4 = new GenericFish(Color.RED);
616                f4.setLocalEnvironment(l4);
617                _env.addFish(l4, f4);
618                try {
619                    l4.execute(new AGlobalEnv.ILocalEnvVisitor() {
620                        public Object emptyCase(ALocalEnv host, Object param) {
621                            throw new RuntimeException("Should be non-empty --");
622                        }
623    
624                        public Object nonEmptyCase(ALocalEnv host, Object param) {
625                            // ok
626                            throw new SuccessException();
627                        }
628                    }, null);
629                    fail("nonEmptyCase should have been falled --");
630                }
631                catch (SuccessException e) {
632                }
633    
634            }
635    
636            /**
637             * Test local environment's tryMoveFwd.
638             */
639            public void testTryMoveFwd() {
640                ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
641                GenericFish fTop = new GenericFish(Color.RED);
642                fTop.setLocalEnvironment(lTop);
643                _env.addFish(lTop, fTop);
644    
645                ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
646                GenericFish fBottom = new GenericFish(Color.RED);
647                fBottom.setLocalEnvironment(lBottom);
648                _env.addFish(lBottom, fBottom);
649    
650                // move lBottom into lTop --> blocked
651                Integer i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
652                    public Object apply(Object param) {
653                        // ok
654                        return new Integer(456);
655                    }
656                }, new IOpenCommand() {
657                    public Object apply(Object param) {
658                        throw new RuntimeException("Should be blocked --");
659                    }
660                });
661                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(456), i);
662    
663                // move lTop --> open, don't move
664                i = ((Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
665                    public Object apply(Object param) {
666                        throw new RuntimeException("Should be open --");
667                    }
668                }, new IOpenCommand() {
669                    public Object apply(Object param) {
670                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
671                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
672                                     DeactivatableLambda.class,
673                                     param.getClass());
674                        // ok
675                        return new Integer(123);
676                    }
677                }));
678                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
679                             new Integer(123),
680                             i);
681    
682                // move lBottom into lTop --> blocked
683                i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
684                    public Object apply(Object param) {
685                        // ok
686                        return new Integer(789);
687                    }
688                }, new IOpenCommand() {
689                    public Object apply(Object param) {
690                        throw new RuntimeException("Should be blocked --");
691                    }
692                });
693                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
694                             new Integer(789),
695                             i);
696    
697                // move lTop --> open, move
698                i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
699                    public Object apply(Object param) {
700                        throw new RuntimeException("Should be open --");
701                    }
702                }, new IOpenCommand() {
703                    public Object apply(Object param) {
704                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
705                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
706                                     DeactivatableLambda.class,
707                                     param.getClass());
708                        // ok, make move
709                        ((ILambda) param).apply(null);
710                        return new Integer(111);
711                    }
712                });
713                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(111), i);
714    
715                // move lBottom --> open
716                i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() {
717                    public Object apply(Object param) {
718                        throw new RuntimeException("Should be open --");
719                    }
720                }, new IOpenCommand() {
721                    public Object apply(Object param) {
722                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
723                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
724                                     DeactivatableLambda.class,
725                                     param.getClass());
726                        // ok
727                        return new Integer(222);
728                    }
729                });
730                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(222), i);
731    
732                // move lTop --> open, don't move
733                i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
734                    public Object apply(Object param) {
735                        throw new RuntimeException("Should be open --");
736                    }
737                }, new IOpenCommand() {
738                    public Object apply(Object param) {
739                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
740                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
741                                     DeactivatableLambda.class,
742                                     param.getClass());
743                        // ok
744                        return new Integer(333);
745                    }
746                });
747                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(333), i);
748    
749                // turn and move lTop --> open, don't move
750                lTop.turnRight(fTop, Math.PI / 2.0);
751                i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() {
752                    public Object apply(Object param) {
753                        throw new RuntimeException("Should be open --");
754                    }
755                }, new IOpenCommand() {
756                    public Object apply(Object param) {
757                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
758                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
759                                     DeactivatableLambda.class,
760                                     param.getClass());
761                        // ok
762                        return new Integer(444);
763                    }
764                });
765                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(444), i);
766            }
767    
768            /**
769             * Test local environment's tryBreedFwd.
770             */
771            public void testTryBreedFwd() {
772                LRStruct lrs = _env._localEnvList;
773    
774                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
775    
776                ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
777                GenericFish fTop = new GenericFish(Color.RED);
778                fTop.setLocalEnvironment(lTop);
779                _env.addFish(lTop, fTop);
780    
781                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
782    
783                ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
784                GenericFish fBottom = new GenericFish(Color.RED);
785                fBottom.setLocalEnvironment(lBottom);
786                _env.addFish(lBottom, fBottom);
787    
788                assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
789    
790                // breed lBottom into lTop --> blocked
791                Integer i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
792                    public Object apply(Object param) {
793                        // ok
794                        return new Integer(456);
795                    }
796                }, new IOpenCommand() {
797                    public Object apply(Object param) {
798                        throw new RuntimeException("Should be blocked --");
799                    }
800                });
801                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
802                             new Integer(456),
803                             i);
804    
805                // breed lTop --> open, don't breed
806                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
807                    public Object apply(Object param) {
808                        throw new RuntimeException("Should be open --");
809                    }
810                }, new IOpenCommand() {
811                    public Object apply(Object param) {
812                        assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
813                        assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
814                                     DeactivatableLambda.class,
815                                     param.getClass());
816                        // ok
817                        return new Integer(123);
818                    }
819                });
820                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
821    
822                assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
823    
824                // breed lBottom into lTop --> blocked
825                i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
826                    public Object apply(Object param) {
827                        // ok
828                        return new Integer(456);
829                    }
830                }, new IOpenCommand() {
831                    public Object apply(Object param) {
832                        throw new RuntimeException("Should be blocked --");
833                    }
834                });
835                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
836                             new Integer(456),
837                             i);
838    
839                // breed lTop --> open, breed
840                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
841                    public Object apply(Object param) {
842                        throw new RuntimeException("Should be open --");
843                    }
844                }, new IOpenCommand() {
845                    public Object apply(Object param) {
846                        assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
847                        assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
848                                     DeactivatableLambda.class,
849                                     param.getClass());
850                        // ok, breed
851                        ((ILambda) param).apply(null);
852                        return new Integer(123);
853                    }
854                });
855                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i);
856    
857                assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
858    
859                // breed lBottom into lTop --> blocked
860                i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() {
861                    public Object apply(Object param) {
862                        // ok
863                        return new Integer(456);
864                    }
865                }, new IOpenCommand() {
866                    public Object apply(Object param) {
867                        throw new RuntimeException("Should be blocked --");
868                    }
869                });
870                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
871                             new Integer(456),
872                             i);
873    
874                // breed lTop --> blocked
875                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
876                    public Object apply(Object param) {
877                        // ok
878                        return new Integer(456);
879                    }
880                }, new IOpenCommand() {
881                    public Object apply(Object param) {
882                        throw new RuntimeException("Should be blocked --");
883                    }
884                });
885                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
886                             new Integer(456),
887                             i);
888    
889                // turn and breed lTop --> open, don't breed
890                lTop.turnRight(fTop, Math.PI / 2.0);
891                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
892                    public Object apply(Object param) {
893                        throw new RuntimeException("Should be open --");
894                    }
895                }, new IOpenCommand() {
896                    public Object apply(Object param) {
897                        assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
898                        assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
899                                     DeactivatableLambda.class,
900                                     param.getClass());
901                        // ok
902                        return new Integer(789);
903                    }
904                });
905                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
906    
907                assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
908    
909                // turn and breed lTop --> open, breed
910                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
911                    public Object apply(Object param) {
912                        throw new RuntimeException("Should be open --");
913                    }
914                }, new IOpenCommand() {
915                    public Object apply(Object param) {
916                        assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param);
917                        assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --",
918                                     DeactivatableLambda.class,
919                                     param.getClass());
920                        // ok, breed
921                        ((ILambda) param).apply(null);
922                        return new Integer(789);
923                    }
924                });
925                assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i);
926    
927                assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
928    
929                // turn and breed lTop --> blocked
930                i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() {
931                    public Object apply(Object param) {
932                        // ok
933                        return new Integer(789);
934                    }
935                }, new IOpenCommand() {
936                    public Object apply(Object param) {
937                        throw new RuntimeException("Should be blocked --");
938                    }
939                });
940                assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --",
941                             new Integer(789),
942                             i);
943    
944                assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
945            }
946    
947            /**
948             * Test local environment's turnRight.
949             */
950            public void testTurnRight() {
951                ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0));
952                GenericFish f = new GenericFish(Color.RED);
953                f.setLocalEnvironment(l);
954                _env.addFish(l, f);
955                Direction d = l.direction();
956    
957                assertEquals(0.0, d.getAngle(), 0.01);
958                l.turnRight(f, Math.PI / 2);
959                assertEquals(Math.PI / 2.0, d.getAngle(), 0.01);
960                l.turnRight(f, Math.PI / 2);
961                assertEquals(Math.PI, d.getAngle(), 0.01);
962                l.turnRight(f, Math.PI / 2);
963                assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01);
964                l.turnRight(f, Math.PI / 2);
965                assertEquals(0, d.getAngle(), 0.01);
966            }
967    
968            /**
969             * Test local environment's removeFish.
970             */
971            public void testRemoveFish() {
972                LRStruct lrs = _env._localEnvList;
973    
974                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
975    
976                ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0));
977                GenericFish fTop = new GenericFish(Color.RED);
978                fTop.setLocalEnvironment(lTop);
979                _env.addFish(lTop, fTop);
980    
981                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
982    
983                ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0));
984                GenericFish fBottom = new GenericFish(Color.RED);
985                fBottom.setLocalEnvironment(lBottom);
986                _env.addFish(lBottom, fBottom);
987    
988                assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
989    
990                lTop.removeFish(fTop);
991    
992                assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
993    
994                lBottom.removeFish(fBottom);
995    
996                assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue());
997            }
998    
999    
1000            /**
1001             * Test to make sure only one move lambda can be executed.
1002             */
1003            public void testOnlyOneMove() {
1004                ASquareLocalEnvironment l = (ASquareLocalEnvironment) _env.makeLocalEnv(new Point.Double(5.0, 5.0));
1005                GenericFish f = new GenericFish(Color.RED);
1006                f.setLocalEnvironment(l);
1007                _env.addFish(l, f);
1008    
1009                final LinkedList<ILambda> lambdas = new LinkedList<ILambda>();
1010                l.tryMoveFwd(f, new IBlockedCommand() {
1011                    public Object apply(Object param) {
1012                        throw new RuntimeException("Should be open --");
1013                    }
1014                }, new IOpenCommand() {
1015                    public Object apply(Object param) {
1016                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1017                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1018                                     DeactivatableLambda.class,
1019                                     param.getClass());
1020                        lambdas.add((ILambda)param);
1021                        return null;
1022                    }
1023                });
1024                f.turnRight();
1025                l.tryMoveFwd(f, new IBlockedCommand() {
1026                    public Object apply(Object param) {
1027                        throw new RuntimeException("Should be open --");
1028                    }
1029                }, new IOpenCommand() {
1030                    public Object apply(Object param) {
1031                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1032                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1033                                     DeactivatableLambda.class,
1034                                     param.getClass());
1035                        lambdas.add((ILambda)param);
1036                        return null;
1037                    }
1038                });
1039    
1040                assertEquals(2, lambdas.size());
1041    
1042                lambdas.get(0).apply(null);
1043                Location loc = l.location();
1044                assertTrue("Should have moved -- ", loc.same(_env.makeLocation(5.0, 4.0)));
1045    
1046                lambdas.get(1).apply(null);
1047                loc = l.location();
1048                assertTrue("Should not have moved, move lambda was not deactivated -- ",
1049                           loc.same(_env.makeLocation(5.0, 4.0)));
1050    
1051                lambdas.clear();
1052                l.tryMoveFwd(f, new IBlockedCommand() {
1053                    public Object apply(Object param) {
1054                        throw new RuntimeException("Should be open --");
1055                    }
1056                }, new IOpenCommand() {
1057                    public Object apply(Object param) {
1058                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1059                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1060                                     DeactivatableLambda.class,
1061                                     param.getClass());
1062                        lambdas.add((ILambda)param);
1063                        return null;
1064                    }
1065                });
1066                f.turnRight();
1067                l.tryMoveFwd(f, new IBlockedCommand() {
1068                    public Object apply(Object param) {
1069                        throw new RuntimeException("Should be open --");
1070                    }
1071                }, new IOpenCommand() {
1072                    public Object apply(Object param) {
1073                        assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param);
1074                        assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --",
1075                                     DeactivatableLambda.class,
1076                                     param.getClass());
1077                        lambdas.add((ILambda)param);
1078                        return null;
1079                    }
1080                });
1081    
1082                assertEquals(2, lambdas.size());
1083    
1084                lambdas.get(1).apply(null);
1085                loc = l.location();
1086                assertTrue("Should have moved -- ", loc.same(_env.makeLocation(6.0, 4.0)));
1087    
1088                lambdas.get(0).apply(null);
1089                loc = l.location();
1090                assertTrue("Should not have moved, move lambda was not deactivated -- ",
1091                           loc.same(_env.makeLocation(6.0, 4.0)));
1092            }
1093        }
1094    }