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    }