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 }