001 package sysModel.env; 002 003 import controller.IScrollAdapter; 004 import controller.IDisplayAdapter; 005 import junit.framework.TestCase; 006 import lrs.IAlgo; 007 import lrs.LRStruct; 008 import lrs.visitor.GetLength; 009 import lrs.visitor.Remove; 010 import model.ILambda; 011 import model.RandNumGenerator; 012 import model.fish.GenericFish; 013 import sysModel.ICmdFactory; 014 import sysModel.ISecurityAdapter; 015 import sysModel.NoOpLambda; 016 import sysModel.fish.IFishFactory; 017 import sysModel.fish.AFish; 018 import sysModel.parser.DefaultTokenVisitor; 019 import sysModel.parser.Lexer; 020 import sysModel.parser.ParserException; 021 022 import javax.swing.*; 023 import java.awt.*; 024 import java.awt.event.MouseEvent; 025 import java.awt.geom.AffineTransform; 026 import java.lang.reflect.Constructor; 027 import java.util.LinkedList; 028 import java.util.Random; 029 import java.io.PrintWriter; 030 031 /** 032 * An environment that does not use grids to place fish. 033 * 034 * @author Mathias Ricken 035 */ 036 public class NoGridEnv extends AGlobalEnv { 037 /** 038 * Concrete direction class. 039 */ 040 public static class Direction { 041 /// floating point epsilon for comparisons 042 private static double EPSILON = 1e-10; 043 044 /** 045 * Direction delta x. 046 */ 047 private double _dx; 048 049 /** 050 * Direction delta y. 051 */ 052 private double _dy; 053 054 /** 055 * Return the direction delta x. 056 * 057 * @return double direction delta x 058 */ 059 public double getDeltaX() { 060 return _dx; 061 } 062 063 /** 064 * Return the direction delta y. 065 * 066 * @return double direction delta y 067 */ 068 public double getDeltaY() { 069 return _dy; 070 } 071 072 /** 073 * Return a new object which has the same direction. 074 * 075 * @return new object with same direction 076 */ 077 public Direction duplicate() { 078 return new Direction(_dx, _dy); 079 } 080 081 /** 082 * Reverse this direction. 083 */ 084 public void reverseDirection() { 085 _dx = -_dx; 086 _dy = -_dy; 087 } 088 089 /** 090 * Return true of this direction is the same as the other. 091 * 092 * @param other other direction 093 * @return true if the directions are the same 094 */ 095 public boolean same(Direction other) { 096 double diffx = _dx - other.getDeltaX(); 097 double diffy = _dy - other.getDeltaY(); 098 return (Math.abs(diffx) < EPSILON && Math.abs(diffy) < EPSILON); 099 } 100 101 /** 102 * Turn this direction to the left. 103 * 104 * @param radians radians to turn 105 */ 106 public void turnLeft(double radians) { 107 turnRight(-radians); 108 } 109 110 /** 111 * Turn this direction PI/2 radians to the left. 112 */ 113 public void turnLeft() { 114 turnLeft(Math.PI / 2); 115 } 116 117 /** 118 * Turn this direction to the right. 119 * 120 * @param radians radians to turn 121 */ 122 public void turnRight(double radians) { 123 double dx = _dx * Math.cos(radians) - _dy * Math.sin(radians); 124 double dy = _dx * Math.sin(radians) + _dy * Math.cos(radians); 125 _dx = dx; 126 _dy = dy; 127 } 128 129 /** 130 * Turn this direction PI/2 radians to the right. 131 */ 132 public void turnRight() { 133 turnRight(Math.PI / 2); 134 } 135 136 /** 137 * Constructor. 138 * 139 * @param dx delta x 140 * @param dy delta y 141 */ 142 public Direction(double dx, double dy) { 143 _dx = dx; 144 _dy = dy; 145 } 146 147 /** 148 * Copy constructor. 149 * 150 * @param other other direction 151 */ 152 public Direction(Direction other) { 153 _dx = other.getDeltaX(); 154 _dy = other.getDeltaY(); 155 } 156 157 /** 158 * Creates a new direction facing north. 159 */ 160 public Direction() { 161 _dx = 0; 162 _dy = -1; 163 } 164 165 /** 166 * Overridden toString method. 167 * 168 * @return string representation 169 */ 170 public String toString() { 171 return "(" + _dx + ", " + _dy + ')'; 172 } 173 174 /** 175 * Parses a direction. 176 * 177 * @param l parser to read from 178 * @return parsed direction 179 */ 180 public static Direction parse(final Lexer l) { 181 // read ( 182 return (Direction) l.nextToken().execute(new DefaultTokenVisitor() { 183 public Object defaultCase() { 184 throw new ParserException("Invalid token"); 185 } 186 187 public Object openCase() { 188 // read x 189 return l.nextToken().execute(new DefaultTokenVisitor() { 190 public Object defaultCase() { 191 throw new ParserException("Invalid token"); 192 } 193 194 public Object numCase(final double x) { 195 // read , 196 return l.nextToken().execute(new DefaultTokenVisitor() { 197 public Object defaultCase() { 198 throw new ParserException("Invalid token"); 199 } 200 201 public Object commaCase() { 202 // read y 203 return l.nextToken().execute(new DefaultTokenVisitor() { 204 public Object defaultCase() { 205 throw new ParserException("Invalid token"); 206 } 207 208 public Object numCase(final double y) { 209 // read ) 210 return l.nextToken().execute(new DefaultTokenVisitor() { 211 public Object defaultCase() { 212 throw new ParserException("Invalid token"); 213 } 214 215 public Object closeCase() { 216 return new Direction(x, y); 217 } 218 }); 219 } 220 }); 221 } 222 }); 223 } 224 }); 225 } 226 }); 227 } 228 229 /** 230 * Rotate the supplied Graphics2D object to match this direction. 231 * 232 * @param g graphics object to rotate 233 */ 234 public void rotateGraphics(Graphics2D g) { 235 /* 236 Find the angle between the current direction dir and (1,0) in a clockwise direction. 237 dir . (0, -1) = |dir|*|(0,-1)|*cos(theta). But |dir| = |(0, -1)| = 1, so 238 cos(theta) = dir . (0, -1) = -dir.y 239 This is always the smaller angle, though. Therefore 240 theta = arccos(-dir.y) if dir.x >= 0 241 = 2 PI - arccos(-dir.y) if dir.x < 0 242 */ 243 double theta = getAngle(); 244 245 if (null != g) { 246 g.rotate(theta); 247 } 248 } 249 250 /** 251 * Return the angle between (0,-1) and this direction. 252 * 253 * @return angle in radians 254 */ 255 public double getAngle() { 256 // keeping argument to acos in range [-1,1] because acos produced a NaN on one system 257 double theta = Math.acos(Math.max(-1,Math.min(1,-_dy))); 258 if (0 > _dx) { 259 theta = 2 * Math.PI - theta; 260 } 261 return theta; 262 } 263 } 264 265 /** 266 * Concrete location class. 267 * 268 * @author Mathias G. Ricken 269 */ 270 public static class Location { 271 /** 272 * Column. 273 */ 274 private double _x; 275 276 /** 277 * Row. 278 */ 279 private double _y; 280 281 /** 282 * Return column. 283 * 284 * @return double column 285 */ 286 public double getX() { 287 return _x; 288 } 289 290 /** 291 * Return row. 292 * 293 * @return double row 294 */ 295 public double getY() { 296 return _y; 297 } 298 299 /** 300 * Set column. 301 * 302 * @param _x New column 303 */ 304 public void setX(double _x) { 305 this._x = _x; 306 } 307 308 /** 309 * Set row. 310 * 311 * @param _y New row 312 */ 313 public void setY(double _y) { 314 this._y = _y; 315 } 316 317 /** 318 * Constructor. 319 * 320 * @param x column 321 * @param y row 322 */ 323 public Location(double x, double y) { 324 _x = x; 325 _y = y; 326 } 327 328 /** 329 * Return true of this location is the same as the other. 330 * 331 * @param other other location 332 * @return true if the locations are the same 333 */ 334 public boolean same(Location other) { 335 return (_x == other.getX()) && 336 (_y == other.getY()); 337 } 338 339 /** 340 * Return true of the other location is in a square with this location at the center, facing in the given 341 * direction, with sides of the specified length. 342 * 343 * @param other location to compare to 344 * @param forward direction the square is facing (this direction is perpendicular to two of the side walls) 345 * @param side side length 346 * @return true if the other point is inside 347 */ 348 public boolean inSquare(Location other, Direction forward, double side) { 349 // distante center-to-other 350 double deltaX = other.getX() - _x; 351 double deltaY = other.getY() - _y; 352 353 // point at center front of square 354 double frontX = forward.getDeltaX(); 355 double frontY = forward.getDeltaY(); 356 357 // project center-to-other onto center-to-front 358 double toFront = deltaX * frontX + deltaY * frontY; 359 if (Math.abs(toFront) > side / 2) { 360 // beyond front or back side 361 return false; 362 } 363 364 // point at center right of square 365 double rightX = -forward.getDeltaY(); 366 double rightY = forward.getDeltaX(); 367 368 // project center-to-other onto center-to-right 369 double toRight = deltaX * rightX + deltaY * rightY; 370 return Math.abs(toRight) <= side / 2; 371 372 } 373 374 /** 375 * Return the location of a neighbor in the given direction. 376 * 377 * @param dir the direction of the neighbor to be returned 378 * @return neighbor in that direction 379 */ 380 public Location getNeighbor(Direction dir) { 381 return new Location(_x + dir.getDeltaX(), 382 _y + dir.getDeltaY()); 383 } 384 385 /** 386 * Overridden toString method. 387 * 388 * @return string representation 389 */ 390 public String toString() { 391 return "(" + _x + ", " + _y + ')'; 392 } 393 394 /** 395 * Parses a location. 396 * 397 * @param l parser to read from 398 * @return parsed location 399 */ 400 public static Location parse(final Lexer l) { 401 // read ( 402 return (Location) l.nextToken().execute(new DefaultTokenVisitor() { 403 public Object defaultCase() { 404 throw new ParserException("Invalid token"); 405 } 406 407 public Object openCase() { 408 // read x 409 return l.nextToken().execute(new DefaultTokenVisitor() { 410 public Object defaultCase() { 411 throw new ParserException("Invalid token"); 412 } 413 414 public Object numCase(final double x) { 415 // read , 416 return l.nextToken().execute(new DefaultTokenVisitor() { 417 public Object defaultCase() { 418 throw new ParserException("Invalid token"); 419 } 420 421 public Object commaCase() { 422 // read y 423 return l.nextToken().execute(new DefaultTokenVisitor() { 424 public Object defaultCase() { 425 throw new ParserException("Invalid token"); 426 } 427 428 public Object numCase(final double y) { 429 // read ) 430 return l.nextToken().execute(new DefaultTokenVisitor() { 431 public Object defaultCase() { 432 throw new ParserException("Invalid token"); 433 } 434 435 public Object closeCase() { 436 return new Location(x, y); 437 } 438 }); 439 } 440 }); 441 } 442 }); 443 } 444 }); 445 } 446 }); 447 } 448 } 449 450 /** 451 * Concrete local environment for the square unbounded environment. 452 */ 453 protected class LocalEnvironment extends ALocalEnv { 454 /** 455 * Location. 456 */ 457 Location _loc; 458 459 /** 460 * Direction. 461 */ 462 Direction _dir; 463 464 /** 465 * State. 466 */ 467 ILocalEnvState _state = EmptyLocalEnvState.Singleton; 468 469 /** 470 * Lambda to execute a move. 471 */ 472 private class MoveLambda implements ILambda { 473 /// target direction 474 private Direction _newDir; 475 /// target location 476 private Location _newLoc; 477 478 /** 479 * Constructor. 480 * 481 * @param le target local environment 482 */ 483 public MoveLambda(LocalEnvironment le) { 484 _newLoc = new Location(le._loc.getX(), le._loc.getY()); 485 _newDir = new Direction(le._dir); 486 } 487 488 /** 489 * Execute the move. 490 * 491 * @param param not used 492 * @return null 493 */ 494 public Object apply(Object param) { 495 // execute the movement 496 _loc = _newLoc; 497 _dir = _newDir; 498 499 // deactivate all lambdas 500 deactivateMoveLambdas(); 501 return null; 502 } 503 } 504 505 /** 506 * Construct a new local environment. 507 * 508 * @param loc location 509 * @param dir direction 510 */ 511 public LocalEnvironment(Location loc, Direction dir) { 512 _loc = loc; 513 _dir = dir; 514 } 515 516 /** 517 * Accessor for the location. 518 * 519 * @return location 520 */ 521 public Location location() { 522 return _loc; 523 } 524 525 /** 526 * Accessor for the direction. 527 * 528 * @return direction 529 */ 530 public Direction direction() { 531 return _dir; 532 } 533 534 /** 535 * Make local environment in forward direction. Do not block yourself. 536 * 537 * @return new local environment in forward direction 538 */ 539 protected ALocalEnv makeMoveFwdLocalEnv() { 540 // remove this local environment to prevent collision with itself 541 _localEnvList.execute(Remove.Singleton, this); 542 ALocalEnv le = makeLocalEnv(_loc.getNeighbor(_dir), _dir); 543 // add this local environment back in 544 _localEnvList.insertFront(this); 545 return le; 546 } 547 548 /** 549 * Factory method for a move lambda. 550 * 551 * @param le local environment for the target 552 * @return move lambda to execute the move to the target 553 */ 554 protected ILambda makeMoveLambda(ALocalEnv le) { 555 return new MoveLambda((LocalEnvironment) le); 556 } 557 558 /** 559 * Draw the fish on the graphics object. The graphics object still has to be translated and rotated properly, 560 * 561 * @param fish AFish to drawFish 562 * @param g graphics object to drawFish on 563 * @param comp component to drawFish on 564 */ 565 public void drawFish(AFish fish, Graphics2D g, Component comp) { 566 double centerX = _loc.getX(); 567 double centerY = _loc.getY(); 568 569 // save transformation 570 AffineTransform oldTransform = g.getTransform(); 571 // translate to center of field 572 g.translate(centerX, centerY); 573 574 // set up the correct rotation 575 _dir.rotateGraphics(g); 576 577 // makeDrawCmd the fish 578 fish.paint(g, comp); 579 580 // restore transformation 581 g.setTransform(oldTransform); 582 } 583 584 /** 585 * Turn the fish radians to the right. 586 * 587 * @param fish AFish to turn 588 * @param radians radians to turn 589 */ 590 public void turnRight(AFish fish, double radians) { 591 _dir.turnRight(radians); 592 } 593 594 /** 595 * String representation of the local environment. Should be "(x, y) (dx, dy)". 596 * 597 * @return string representation 598 */ 599 public String toString() { 600 return _loc.toString() + ' ' + _dir.toString(); 601 } 602 } 603 604 private static int PAN_SIZE = 2000; 605 private static Point.Double PAN_CENTER = new Point.Double(PAN_SIZE / 2, PAN_SIZE / 2); 606 607 /** 608 * List of local environments in this global environment. 609 */ 610 private LRStruct _localEnvList; 611 612 /** 613 * Number of steps for complete rotation. 614 */ 615 private int _rotSteps; 616 617 /** 618 * Create a new environment without grids. Note: This constructor needs to exist and be public for the "environment 619 * selection" dialog to work. 620 * 621 * @param cmdFactory command factory to use 622 * @param sm security manager to control fish actions 623 */ 624 public NoGridEnv(ICmdFactory cmdFactory, ISecurityAdapter sm) { 625 super(cmdFactory, sm); 626 _localEnvList = new LRStruct(); 627 } 628 629 /** 630 * Create a new environment without grids. 631 * 632 * @param cmdFactory command factory to use 633 * @param sm security manager to control fish actions 634 * @param rotSteps rotation steps 635 * @param waterColor color of the water 636 */ 637 public NoGridEnv(ICmdFactory cmdFactory, ISecurityAdapter sm, int rotSteps, Color waterColor) { 638 super(cmdFactory, sm); 639 _localEnvList = new LRStruct(); 640 _rotSteps = rotSteps; 641 _waterColor = waterColor; 642 } 643 644 /** 645 * Add the fish to the global environment. 646 * 647 * @param localEnv local environment 648 * @param fish fish to add 649 */ 650 protected void addFishToInternalData(ALocalEnv localEnv, AFish fish) { 651 _localEnvList.insertFront(localEnv); 652 } 653 654 /** 655 * Remove the fish from the global environment. 656 * 657 * @param localEnv local environment 658 */ 659 protected void removeFishFromInternalData(ALocalEnv localEnv) { 660 _localEnvList.execute(Remove.Singleton, localEnv); 661 } 662 663 /** 664 * Create a local environment for the position. 665 * 666 * @param p position 667 * @return local environment 668 */ 669 public ALocalEnv makeLocalEnv(Point.Double p) { 670 return makeLocalEnv(new Location(p.getX(), p.getY()), new Direction()); 671 } 672 673 /** 674 * Create a local environment for the position. 675 * 676 * @param loc location 677 * @param dir direction 678 * @return local environment 679 */ 680 private ALocalEnv makeLocalEnv(final Location loc, final Direction dir) { 681 return (ALocalEnv) _localEnvList.execute(new IAlgo() { 682 /** 683 * Operates on a non-empty LRStruct host, given an input object. 684 * 685 * @param host a non-empty LRStruct. 686 * @param inp input object needed by this IAlgo. 687 * @return an appropriate output object. 688 */ 689 public Object nonEmptyCase(LRStruct host, Object inp) { 690 LocalEnvironment localEnv = (LocalEnvironment) host.getFirst(); 691 if (localEnv.location().inSquare(loc, localEnv.direction(), 1)) { 692 return localEnv; 693 } 694 else { 695 return host.getRest().execute(this, inp); 696 } 697 } 698 699 /** 700 * Operates on an empty LRStruct host, given an input object. 701 * 702 * @param host an empty LRStruct. 703 * @param inp input object needed by this IAlgo. 704 * @return an appropriate output object. 705 */ 706 public Object emptyCase(LRStruct host, Object inp) { 707 return new LocalEnvironment(loc, dir); 708 } 709 }, null); 710 } 711 712 /** 713 * Create a local environment with the given data. 714 * 715 * @param loc location 716 * @param dir direction 717 * @return new local environment 718 */ 719 protected LocalEnvironment createLocalEnvironment(Location loc, Direction dir) { 720 return new LocalEnvironment(loc, dir); 721 } 722 723 /** 724 * Edit the fish. 725 * 726 * @param le local environment 727 * @param fishFactory 728 * @param button 729 * @return lambda to edit the fish 730 */ 731 public ILambda editFish(ALocalEnv le, final IFishFactory fishFactory, int button) { 732 // by default, the control does not need to do anything 733 ILambda lambdaForControl = NoOpLambda.instance(); 734 735 // rotate the fish 736 final LocalEnvironment localEnv = (LocalEnvironment) le; 737 738 double initialAngle = localEnv.direction().getAngle(); 739 localEnv.direction().turnRight(2 * Math.PI / _rotSteps); 740 741 // check if the fish should be removed 742 if (initialAngle > localEnv.direction().getAngle()) { 743 // rotated back to initial position, remove 744 745 // set up lambda for simulation control 746 lambdaForControl = _cmdFactory.makeDeleteCmd(localEnv); 747 748 // and remove it from the environment's data 749 ILambda deleteLambda = removeFish(le); 750 751 // delete the fish from the simulation by executing the deleteLambda 752 deleteLambda.apply(null); 753 } 754 755 return lambdaForControl; 756 } 757 758 /** 759 * Factory method for parsing a stream of tokens and creating a global environment from it. 760 * 761 * @param l lexer to use 762 * @return new global environment 763 */ 764 protected AGlobalEnv parseEnvironment(final Lexer l) { 765 // have to read number of steps 766 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() { 767 public Object defaultCase() { 768 throw new ParserException("Invalid token"); 769 } 770 771 public Object numCase(final double rotSteps) { 772 // rotation steps was read 773 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() { 774 public Object defaultCase() { 775 throw new ParserException("Invalid token"); 776 } 777 778 public Object numCase(final double red) { 779 // red was read 780 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() { 781 public Object defaultCase() { 782 throw new ParserException("Invalid token"); 783 } 784 785 public Object numCase(final double green) { 786 // green was read 787 return (AGlobalEnv) l.nextToken().execute(new DefaultTokenVisitor() { 788 public Object defaultCase() { 789 throw new ParserException("Invalid token"); 790 } 791 792 public Object numCase(double blue) { 793 // blue was read 794 NoGridEnv env = new NoGridEnv(_cmdFactory, _securityAdapter, (int) rotSteps, 795 new Color((int) red, (int) green, (int) blue)); 796 797 // parse fish 798 env.parseFish(l); 799 800 return env; 801 } 802 }); 803 804 } 805 }); 806 } 807 }); 808 } 809 }); 810 811 } 812 813 /** 814 * Parse fish and add them to the environment. 815 * 816 * @param l parser to read from 817 */ 818 protected void parseFish(final Lexer l) { 819 while (l.nextToken().execute(new DefaultTokenVisitor() { 820 public Object defaultCase() { 821 throw new ParserException("Invalid token"); 822 } 823 824 public Object endCase() { 825 // end of stream 826 // return false 827 return Boolean.FALSE; 828 } 829 830 public Object wordCase(String className) { 831 // read class name 832 try { 833 // NOTE: Introduced class loader here 834 // Class fishClass = Class.forName(className); 835 Class fishClass = _securityAdapter.getClassLoader().loadClass(className); 836 Constructor fishCtor = fishClass.getConstructor(new Class[]{Color.class}); 837 Random rng = RandNumGenerator.instance(); 838 AFish fish = (AFish) fishCtor.newInstance(new Object[]{new Color(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256))}); 839 840 // read location 841 Location loc = Location.parse(l); 842 Direction dir = Direction.parse(l); 843 LocalEnvironment localEnv = createLocalEnvironment(loc, dir); 844 ILambda addLambda = addFish(localEnv, fish); 845 addLambda.apply(null); 846 847 return Boolean.TRUE; 848 } 849 catch (Exception e) { 850 e.printStackTrace(); 851 throw new ParserException(e.toString(),e); 852 } 853 } 854 }) == Boolean.TRUE) { 855 } 856 } 857 858 /** 859 * Get the environment settings class. 860 * 861 * @return environment settings class 862 */ 863 public AEnvFactory makeEnvFactory() { 864 return new AEnvFactory() { 865 private JTextField _rotStepsField; 866 private JColorChooser _colorChooser; 867 868 { 869 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 870 add(new JLabel("rotation steps: ")); 871 add(_rotStepsField = new JTextField("24")); 872 add(new JLabel("water color: ")); 873 _colorChooser = new JColorChooser(OCEAN_BLUE); 874 add(_colorChooser); 875 } 876 877 public AGlobalEnv create() { 878 return new NoGridEnv(_cmdFactory, 879 _securityAdapter, 880 Integer.parseInt(_rotStepsField.getText()), 881 _colorChooser.getColor()); 882 } 883 884 public String toString() { 885 return NoGridEnv.class.getName(); 886 } 887 }; 888 } 889 890 /** 891 * Print file header. 892 * 893 * @param pw PrintWriter to use 894 */ 895 protected void printHeader(PrintWriter pw) { 896 pw.println(getClass().getName() + ' ' + _rotSteps + ' ' + 897 _waterColor.getRed() + ' ' + _waterColor.getGreen() + ' ' + _waterColor.getBlue()); 898 } 899 900 /** 901 * Get size of the display. 902 * 903 * @return size of the display in model coordinate units. 904 */ 905 public Dimension getDisplaySize() { 906 return new Dimension(PAN_SIZE, PAN_SIZE); 907 } 908 909 /** 910 * The action to be executed if the display should return home. 911 * 912 * @param sa scroll adapter 913 */ 914 public void returnHome(IScrollAdapter sa) { 915 sa.setCorner((int) PAN_CENTER.x, (int) PAN_CENTER.y); 916 sa.resetScrolling(); 917 } 918 919 /** 920 * Ask the model where to scroll, given where the user has scrolled. If the environment just acts like a normal 921 * panal, it should return pos without modification. If the environment recenters, it should return a position in 922 * the middle of the pan area. All coordinates are in model coordinate units. 923 * 924 * @param pos position where the user scrolled to 925 * @return position where the environment wants the view to be 926 * @see IDisplayAdapter#getPanDelta 927 */ 928 public Point.Double getViewPosition(Point.Double pos) { 929 // the panel always gets recentered after moving it, so return the center position 930 return PAN_CENTER; 931 } 932 933 /** 934 * Ask the model how much to pan, given where the user scrolled. If the environment just acts like a normal panal, 935 * it should return (0,0). If the environment recenters, it should return delta without modification. All 936 * coordinates are in model coordinate units. 937 * 938 * @param delta how far the user scrolled 939 * @return how far the panel should scroll 940 * @see IDisplayAdapter#getViewPosition 941 */ 942 public Point.Double getPanDelta(Point.Double delta) { 943 // we want the panel to keep track of the position, so return the delta as pan value 944 return delta; 945 } 946 947 /** 948 * Get a tool tip description for a specific place in the environment. 949 * 950 * @param p mouse coordinates 951 * @return lambda for the simulation controller to execute. Must return the tooltip string. 952 */ 953 public ILambda getToolTipText(final Point.Double p) { 954 final LocalEnvironment l = createLocalEnvironment(new Location(p.x, p.y), new Direction()); 955 return ((ILambda) l.execute(new ILocalEnvVisitor() { 956 public Object emptyCase(ALocalEnv host, Object param) { 957 // this field is empty 958 959 // return an ILambda that returns the string for an empty field 960 return new ILambda() { 961 public Object apply(Object param) { 962 return "(" + p.x + ',' + p.y + ") is empty"; 963 } 964 }; 965 } 966 967 public Object nonEmptyCase(ALocalEnv host, Object param) { 968 // this field is occupied 969 970 // return an ILambda that returns the string for an occupied 971 return new ILambda() { 972 String _fishName; 973 ALocalEnv _localEnv; 974 975 public Object apply(Object param) { 976 _fishName = ""; 977 _cmdFactory.makeNotifyCmd(new ILambda() { 978 public Object apply(Object param) { 979 FishApplyParams fap = (FishApplyParams) param; 980 LocalEnvironment lenv = (LocalEnvironment) fap.localEnv(); 981 if (lenv.location().inSquare(l.location(), lenv.direction(), 1)) { 982 _fishName = fap.fish().toString(); 983 _localEnv = fap.localEnv(); 984 } 985 return null; 986 } 987 }).apply(null); 988 989 return _fishName + " at " + _localEnv; 990 } 991 }; 992 } 993 }, null)); 994 } 995 996 997 /***************************************************************************************************************** 998 * Tests follow 999 *****************************************************************************************************************/ 1000 1001 /** 1002 * Test cases for NoGridEnv. 1003 * 1004 * @author Mathias Ricken 1005 */ 1006 public static class Test_NoGridEnv extends TestCase { 1007 private ICmdFactory _cmdFactory; 1008 private ISecurityAdapter _sm; 1009 private NoGridEnv _env; 1010 private IFishFactory _fishFactory; 1011 1012 private static final ILambda _notify = new ILambda() { 1013 public Object apply(Object param) { 1014 return "notifyCmd"; 1015 } 1016 }; 1017 private static final ILambda _delete = new ILambda() { 1018 public Object apply(Object param) { 1019 return "deleteCmd"; 1020 } 1021 }; 1022 private static final ILambda _add = new ILambda() { 1023 public Object apply(Object param) { 1024 return "addCmd"; 1025 } 1026 }; 1027 1028 public void setUp() throws Exception { 1029 super.setUp(); 1030 _cmdFactory = new ICmdFactory() { 1031 public ILambda makeNotifyCmd(ILambda lambda) { 1032 return _notify; 1033 } 1034 1035 public ILambda makeDeleteCmd(ALocalEnv env) { 1036 return _delete; 1037 } 1038 1039 public ILambda makeAddCmd(AFish fish) { 1040 return _add; 1041 } 1042 }; 1043 _sm = new ISecurityAdapter() { 1044 public void setProtected(boolean _protected) { 1045 } 1046 public ThreadGroup getFishThreadGroup() { 1047 return null; 1048 } 1049 public ClassLoader getClassLoader() { 1050 return null; 1051 } 1052 public void handleException(Throwable t) { 1053 } 1054 }; 1055 1056 _env = new NoGridEnv(_cmdFactory, _sm, 24, OCEAN_BLUE); 1057 1058 1059 _fishFactory = new IFishFactory() { 1060 /** 1061 * Create a new fish. 1062 * 1063 * @return new fish 1064 */ 1065 public AFish createFish() { 1066 return new GenericFish(Color.RED); 1067 } 1068 }; 1069 } 1070 1071 /** 1072 * Test addFishToInternalData. 1073 */ 1074 public void testAddFish() { 1075 LRStruct lrs = _env._localEnvList; 1076 1077 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1078 1079 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1080 GenericFish fTop = new GenericFish(Color.RED); 1081 fTop.setLocalEnvironment(lTop); 1082 _env.addFishToInternalData(lTop, fTop); 1083 1084 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1085 1086 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0)); 1087 GenericFish fBottom = new GenericFish(Color.RED); 1088 fBottom.setLocalEnvironment(lBottom); 1089 _env.addFishToInternalData(lBottom, fBottom); 1090 1091 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1092 } 1093 1094 /** 1095 * Test editFish. 1096 */ 1097 public void testEditFish() { 1098 LRStruct lrs = _env._localEnvList; 1099 1100 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1101 1102 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1103 GenericFish f = new GenericFish(Color.RED); 1104 f.setLocalEnvironment(l); 1105 _env.addFishToInternalData(l, f); 1106 NoGridEnv.Direction d = l.direction(); 1107 1108 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1109 assertEquals(0.0, d.getAngle(), 0.01); 1110 1111 ILambda lambda; 1112 for (int i = 1; 24 >= i; ++i) { 1113 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1); 1114 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1115 assertEquals(i * Math.PI / 12.0, d.getAngle(), 0.01); 1116 assertEquals(NoOpLambda.instance(), lambda); 1117 } 1118 1119 lambda = _env.editFish(l, _fishFactory, MouseEvent.BUTTON1); 1120 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1121 assertEquals(Math.PI / 12.0, d.getAngle(), 0.01); 1122 assertEquals(_delete, lambda); 1123 } 1124 1125 /** 1126 * Test getViewPosition. 1127 */ 1128 public void testGetViewPosition() { 1129 Point.Double panCenter = PAN_CENTER; 1130 1131 assertTrue(_env.getViewPosition(new Point.Double(0, 0)).equals(panCenter)); 1132 assertTrue(_env.getViewPosition(new Point.Double(1.0, 0)).equals(panCenter)); 1133 assertTrue(_env.getViewPosition(new Point.Double(1.2, 0)).equals(panCenter)); 1134 assertTrue(_env.getViewPosition(new Point.Double(0, 1.0)).equals(panCenter)); 1135 assertTrue(_env.getViewPosition(new Point.Double(0, 1.3)).equals(panCenter)); 1136 assertTrue(_env.getViewPosition(new Point.Double(-2.5, 0)).equals(panCenter)); 1137 assertTrue(_env.getViewPosition(new Point.Double(-3.0, 0)).equals(panCenter)); 1138 assertTrue(_env.getViewPosition(new Point.Double(0, -2.5)).equals(panCenter)); 1139 assertTrue(_env.getViewPosition(new Point.Double(0, -3.0)).equals(panCenter)); 1140 assertTrue(_env.getViewPosition(new Point.Double(2.0, 1.0)).equals(panCenter)); 1141 assertTrue(_env.getViewPosition(new Point.Double(-4.0, -2.3)).equals(panCenter)); 1142 } 1143 1144 /** 1145 * Test getPanDelta. 1146 */ 1147 public void testGetPanDelta() { 1148 assertTrue(_env.getPanDelta(new Point.Double(0, 0)).equals(new Point.Double(0, 0))); 1149 assertTrue(_env.getPanDelta(new Point.Double(1.0, 0)).equals(new Point.Double(1.0, 0))); 1150 assertTrue(_env.getPanDelta(new Point.Double(1.2, 0)).equals(new Point.Double(1.2, 0))); 1151 assertTrue(_env.getPanDelta(new Point.Double(0, 1.0)).equals(new Point.Double(0, 1.0))); 1152 assertTrue(_env.getPanDelta(new Point.Double(0, 1.3)).equals(new Point.Double(0, 1.3))); 1153 assertTrue(_env.getPanDelta(new Point.Double(-2.5, 0)).equals(new Point.Double(-2.5, 0))); 1154 assertTrue(_env.getPanDelta(new Point.Double(-3.0, 0)).equals(new Point.Double(-3.0, 0))); 1155 assertTrue(_env.getPanDelta(new Point.Double(0, -2.5)).equals(new Point.Double(0, -2.5))); 1156 assertTrue(_env.getPanDelta(new Point.Double(0, -3.0)).equals(new Point.Double(0, -3.0))); 1157 assertTrue(_env.getPanDelta(new Point.Double(2.0, 1.0)).equals(new Point.Double(2.0, 1.0))); 1158 assertTrue(_env.getPanDelta(new Point.Double(-4.0, -2.3)).equals(new Point.Double(-4.0, -2.3))); 1159 } 1160 } 1161 1162 /** 1163 * Test cases for NoGridEnv.LocalEnv. 1164 * 1165 * @author Mathias Ricken 1166 */ 1167 public static class Test_NoGridEnv_LocalEnv extends TestCase { 1168 private ICmdFactory _cmdFactory; 1169 private ISecurityAdapter _sm; 1170 private NoGridEnv _env; 1171 1172 private static class SuccessException extends RuntimeException { 1173 public SuccessException() { 1174 super(); 1175 } 1176 } 1177 1178 private static final ILambda _notify = new ILambda() { 1179 public Object apply(Object param) { 1180 return "notifyCmd"; 1181 } 1182 }; 1183 private static final ILambda _delete = new ILambda() { 1184 public Object apply(Object param) { 1185 return "deleteCmd"; 1186 } 1187 }; 1188 private static final ILambda _add = new ILambda() { 1189 public Object apply(Object param) { 1190 return "addCmd"; 1191 } 1192 }; 1193 1194 public void setUp() throws Exception { 1195 super.setUp(); 1196 _cmdFactory = new ICmdFactory() { 1197 public ILambda makeNotifyCmd(ILambda lambda) { 1198 return _notify; 1199 } 1200 1201 public ILambda makeDeleteCmd(ALocalEnv env) { 1202 return _delete; 1203 } 1204 1205 public ILambda makeAddCmd(AFish fish) { 1206 return _add; 1207 } 1208 }; 1209 _sm = new ISecurityAdapter() { 1210 public void setProtected(boolean _protected) { 1211 } 1212 public ThreadGroup getFishThreadGroup() { 1213 return null; 1214 } 1215 public ClassLoader getClassLoader() { 1216 return null; 1217 } 1218 public void handleException(Throwable t) { 1219 } 1220 }; 1221 1222 _env = new NoGridEnv(_cmdFactory, _sm); 1223 } 1224 1225 /** 1226 * Test local environment's execute. 1227 */ 1228 public void testExecute() { 1229 ALocalEnv l = _env.makeLocalEnv(new Point.Double(1, 1)); 1230 1231 try { 1232 l.execute(new AGlobalEnv.ILocalEnvVisitor() { 1233 public Object emptyCase(ALocalEnv host, Object param) { 1234 // ok 1235 throw new SuccessException(); 1236 } 1237 1238 public Object nonEmptyCase(ALocalEnv host, Object param) { 1239 throw new RuntimeException("Should be empty"); 1240 } 1241 }, null); 1242 fail("emptyCase should have been called --"); 1243 } 1244 catch (SuccessException e) { 1245 } 1246 1247 1248 GenericFish f = new GenericFish(Color.RED); 1249 f.setLocalEnvironment(l); 1250 _env.addFish(l, f); 1251 try { 1252 l.execute(new AGlobalEnv.ILocalEnvVisitor() { 1253 public Object emptyCase(ALocalEnv host, Object param) { 1254 throw new RuntimeException("Should be non-empty"); 1255 } 1256 1257 public Object nonEmptyCase(ALocalEnv host, Object param) { 1258 // ok 1259 throw new SuccessException(); 1260 } 1261 }, null); 1262 fail("nonEmptyCase should have been called --"); 1263 } 1264 catch (SuccessException e) { 1265 } 1266 1267 ALocalEnv l2 = _env.makeLocalEnv(new Point.Double(1, 1)); 1268 try { 1269 l2.execute(new AGlobalEnv.ILocalEnvVisitor() { 1270 public Object emptyCase(ALocalEnv host, Object param) { 1271 throw new RuntimeException("Should be non-empty"); 1272 } 1273 1274 public Object nonEmptyCase(ALocalEnv host, Object param) { 1275 // ok 1276 throw new SuccessException(); 1277 } 1278 }, null); 1279 fail("nonEmptyCase should have been called --"); 1280 } 1281 catch (SuccessException e) { 1282 } 1283 1284 ALocalEnv l3 = _env.makeLocalEnv(new Point.Double(1.4, 1.4)); 1285 try { 1286 l3.execute(new AGlobalEnv.ILocalEnvVisitor() { 1287 public Object emptyCase(ALocalEnv host, Object param) { 1288 throw new RuntimeException("Should be non-empty"); 1289 } 1290 1291 public Object nonEmptyCase(ALocalEnv host, Object param) { 1292 // ok 1293 throw new SuccessException(); 1294 } 1295 }, null); 1296 fail("nonEmptyCase should have been called --"); 1297 } 1298 catch (SuccessException e) { 1299 } 1300 1301 1302 ALocalEnv l3b = _env.makeLocalEnv(new Point.Double(1.4, 1.6)); 1303 try { 1304 l3b.execute(new AGlobalEnv.ILocalEnvVisitor() { 1305 public Object emptyCase(ALocalEnv host, Object param) { 1306 // ok 1307 throw new SuccessException(); 1308 } 1309 1310 public Object nonEmptyCase(ALocalEnv host, Object param) { 1311 throw new RuntimeException("Should be empty"); 1312 } 1313 }, null); 1314 fail("emptyCase should have been called --"); 1315 } 1316 catch (SuccessException e) { 1317 } 1318 1319 1320 ALocalEnv l4 = _env.makeLocalEnv(new Point.Double(1.0, 2.0)); 1321 try { 1322 l4.execute(new AGlobalEnv.ILocalEnvVisitor() { 1323 public Object emptyCase(ALocalEnv host, Object param) { 1324 // ok 1325 throw new SuccessException(); 1326 } 1327 1328 public Object nonEmptyCase(ALocalEnv host, Object param) { 1329 throw new RuntimeException("Should be empty"); 1330 } 1331 }, null); 1332 fail("emptyCase should have been called --"); 1333 } 1334 catch (SuccessException e) { 1335 } 1336 1337 1338 GenericFish f4 = new GenericFish(Color.RED); 1339 f4.setLocalEnvironment(l4); 1340 _env.addFish(l4, f4); 1341 try { 1342 l4.execute(new AGlobalEnv.ILocalEnvVisitor() { 1343 public Object emptyCase(ALocalEnv host, Object param) { 1344 throw new RuntimeException("Should be non-empty"); 1345 } 1346 1347 public Object nonEmptyCase(ALocalEnv host, Object param) { 1348 // ok 1349 throw new SuccessException(); 1350 } 1351 }, null); 1352 fail("nonEmptyCase should have been called --"); 1353 } 1354 catch (SuccessException e) { 1355 } 1356 } 1357 1358 /** 1359 * Test local environment's tryMoveFwd. 1360 */ 1361 public void testTryMoveFwd() { 1362 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1363 GenericFish fTop = new GenericFish(Color.RED); 1364 fTop.setLocalEnvironment(lTop); 1365 _env.addFish(lTop, fTop); 1366 1367 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0)); 1368 GenericFish fBottom = new GenericFish(Color.RED); 1369 fBottom.setLocalEnvironment(lBottom); 1370 _env.addFish(lBottom, fBottom); 1371 1372 // move lBottom into lTop --> blocked 1373 Integer i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() { 1374 public Object apply(Object param) { 1375 // ok 1376 return new Integer(456); 1377 } 1378 }, new IOpenCommand() { 1379 public Object apply(Object param) { 1380 throw new RuntimeException("Should be blocked"); 1381 } 1382 }); 1383 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1384 new Integer(456), 1385 i); 1386 1387 // move lTop --> open, don't move 1388 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() { 1389 public Object apply(Object param) { 1390 throw new RuntimeException("Should be open"); 1391 } 1392 }, new IOpenCommand() { 1393 public Object apply(Object param) { 1394 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param); 1395 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --", 1396 DeactivatableLambda.class, 1397 param.getClass()); 1398 // ok 1399 return new Integer(123); 1400 } 1401 }); 1402 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i); 1403 1404 // move lBottom into lTop --> blocked 1405 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() { 1406 public Object apply(Object param) { 1407 // ok 1408 return new Integer(789); 1409 } 1410 }, new IOpenCommand() { 1411 public Object apply(Object param) { 1412 throw new RuntimeException("Should be blocked"); 1413 } 1414 }); 1415 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1416 new Integer(789), 1417 i); 1418 1419 // move lTop --> open, move 1420 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() { 1421 public Object apply(Object param) { 1422 throw new RuntimeException("Should be open"); 1423 } 1424 }, new IOpenCommand() { 1425 public Object apply(Object param) { 1426 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param); 1427 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --", 1428 DeactivatableLambda.class, 1429 param.getClass()); 1430 // ok, make move 1431 ((ILambda) param).apply(null); 1432 return new Integer(111); 1433 } 1434 }); 1435 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(111), i); 1436 1437 // move lBottom --> open 1438 i = (Integer) lBottom.tryMoveFwd(fBottom, new IBlockedCommand() { 1439 public Object apply(Object param) { 1440 throw new RuntimeException("Should be open"); 1441 } 1442 }, new IOpenCommand() { 1443 public Object apply(Object param) { 1444 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param); 1445 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --", 1446 DeactivatableLambda.class, 1447 param.getClass()); 1448 // ok 1449 return new Integer(222); 1450 } 1451 }); 1452 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(222), i); 1453 1454 // move lTop --> open, don't move 1455 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() { 1456 public Object apply(Object param) { 1457 throw new RuntimeException("Should be open"); 1458 } 1459 }, new IOpenCommand() { 1460 public Object apply(Object param) { 1461 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param); 1462 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --", 1463 DeactivatableLambda.class, 1464 param.getClass()); 1465 // ok 1466 return new Integer(333); 1467 } 1468 }); 1469 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(333), i); 1470 1471 // turn and move lTop --> open, don't move 1472 lTop.turnRight(fTop, Math.PI / 2.0); 1473 i = (Integer) lTop.tryMoveFwd(fTop, new IBlockedCommand() { 1474 public Object apply(Object param) { 1475 throw new RuntimeException("Should be open"); 1476 } 1477 }, new IOpenCommand() { 1478 public Object apply(Object param) { 1479 assertNotNull("Error, deactivatable move lambda needs to be passed to openCmd --", param); 1480 assertEquals("Error, deactivatable move lambda needs to be passed to openCmd --", 1481 DeactivatableLambda.class, 1482 param.getClass()); 1483 // ok 1484 return new Integer(444); 1485 } 1486 }); 1487 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(444), i); 1488 } 1489 1490 /** 1491 * Test local environment's tryBreedFwd. 1492 */ 1493 public void testTryBreedFwd() { 1494 LRStruct lrs = _env._localEnvList; 1495 1496 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1497 1498 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1499 GenericFish fTop = new GenericFish(Color.RED); 1500 fTop.setLocalEnvironment(lTop); 1501 _env.addFish(lTop, fTop); 1502 1503 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1504 1505 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0)); 1506 GenericFish fBottom = new GenericFish(Color.RED); 1507 fBottom.setLocalEnvironment(lBottom); 1508 _env.addFish(lBottom, fBottom); 1509 1510 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1511 1512 // breed lBottom into lTop --> blocked 1513 Integer i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() { 1514 public Object apply(Object param) { 1515 // ok 1516 return new Integer(456); 1517 } 1518 }, new IOpenCommand() { 1519 public Object apply(Object param) { 1520 throw new RuntimeException("Should be blocked"); 1521 } 1522 }); 1523 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1524 new Integer(456), 1525 i); 1526 1527 // breed lTop --> open, don't breed 1528 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1529 public Object apply(Object param) { 1530 throw new RuntimeException("Should be open"); 1531 } 1532 }, new IOpenCommand() { 1533 public Object apply(Object param) { 1534 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param); 1535 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --", 1536 DeactivatableLambda.class, 1537 param.getClass()); 1538 // ok 1539 return new Integer(123); 1540 } 1541 }); 1542 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i); 1543 1544 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1545 1546 // breed lBottom into lTop --> blocked 1547 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() { 1548 public Object apply(Object param) { 1549 // ok 1550 return new Integer(456); 1551 } 1552 }, new IOpenCommand() { 1553 public Object apply(Object param) { 1554 throw new RuntimeException("Should be blocked"); 1555 } 1556 }); 1557 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1558 new Integer(456), 1559 i); 1560 1561 // breed lTop --> open, breed 1562 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1563 public Object apply(Object param) { 1564 throw new RuntimeException("Should be open"); 1565 } 1566 }, new IOpenCommand() { 1567 public Object apply(Object param) { 1568 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param); 1569 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --", 1570 DeactivatableLambda.class, 1571 param.getClass()); 1572 // ok, breed 1573 ((ILambda) param).apply(null); 1574 return new Integer(123); 1575 } 1576 }); 1577 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(123), i); 1578 1579 assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1580 1581 // breed lBottom into lTop --> blocked 1582 i = (Integer) lBottom.tryBreedFwd(fBottom, new IBlockedCommand() { 1583 public Object apply(Object param) { 1584 // ok 1585 return new Integer(456); 1586 } 1587 }, new IOpenCommand() { 1588 public Object apply(Object param) { 1589 throw new RuntimeException("Should be blocked"); 1590 } 1591 }); 1592 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1593 new Integer(456), 1594 i); 1595 1596 // breed lTop --> blocked 1597 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1598 public Object apply(Object param) { 1599 // ok 1600 return new Integer(456); 1601 } 1602 }, new IOpenCommand() { 1603 public Object apply(Object param) { 1604 throw new RuntimeException("Should be blocked"); 1605 } 1606 }); 1607 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1608 new Integer(456), 1609 i); 1610 1611 // turn and breed lTop --> open, don't breed 1612 lTop.turnRight(fTop, Math.PI / 2.0); 1613 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1614 public Object apply(Object param) { 1615 throw new RuntimeException("Should be open"); 1616 } 1617 }, new IOpenCommand() { 1618 public Object apply(Object param) { 1619 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param); 1620 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --", 1621 DeactivatableLambda.class, 1622 param.getClass()); 1623 // ok 1624 return new Integer(789); 1625 } 1626 }); 1627 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i); 1628 1629 assertEquals(3, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1630 1631 // turn and breed lTop --> open, breed 1632 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1633 public Object apply(Object param) { 1634 throw new RuntimeException("Should be open"); 1635 } 1636 }, new IOpenCommand() { 1637 public Object apply(Object param) { 1638 assertNotNull("Error, deactivatable breed lambda needs to be passed to openCmd --", param); 1639 assertEquals("Error, deactivatable breed lambda needs to be passed to openCmd --", 1640 DeactivatableLambda.class, 1641 param.getClass()); 1642 // ok, breed 1643 ((ILambda) param).apply(null); 1644 return new Integer(789); 1645 } 1646 }); 1647 assertEquals("Error in delegation, openCmd not called, or incorrect return value --", new Integer(789), i); 1648 1649 assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1650 1651 // turn and breed lTop --> blocked 1652 i = (Integer) lTop.tryBreedFwd(fTop, new IBlockedCommand() { 1653 public Object apply(Object param) { 1654 // ok 1655 return new Integer(789); 1656 } 1657 }, new IOpenCommand() { 1658 public Object apply(Object param) { 1659 throw new RuntimeException("Should be blocked"); 1660 } 1661 }); 1662 assertEquals("Error in delegation, blockedCmd not called, or incorrect return value --", 1663 new Integer(789), 1664 i); 1665 1666 assertEquals(4, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1667 } 1668 1669 /** 1670 * Test local environment's turnRight. 1671 */ 1672 public void testTurnRight() { 1673 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1674 GenericFish f = new GenericFish(Color.RED); 1675 f.setLocalEnvironment(l); 1676 _env.addFish(l, f); 1677 NoGridEnv.Direction d = l.direction(); 1678 1679 assertEquals(0.0, d.getAngle(), 0.01); 1680 l.turnRight(f, Math.PI / 2); 1681 assertEquals(Math.PI / 2.0, d.getAngle(), 0.01); 1682 l.turnRight(f, Math.PI / 2); 1683 assertEquals(Math.PI, d.getAngle(), 0.01); 1684 l.turnRight(f, Math.PI / 2); 1685 assertEquals(3 * Math.PI / 2.0, d.getAngle(), 0.01); 1686 l.turnRight(f, Math.PI / 2); 1687 assertEquals(2 * Math.PI, d.getAngle(), 0.01); 1688 } 1689 1690 /** 1691 * Test local environment's removeFish. 1692 */ 1693 public void testRemoveFish() { 1694 LRStruct lrs = _env._localEnvList; 1695 1696 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1697 1698 ALocalEnv lTop = _env.makeLocalEnv(new Point.Double(1.0, 1.0)); 1699 GenericFish fTop = new GenericFish(Color.RED); 1700 fTop.setLocalEnvironment(lTop); 1701 _env.addFish(lTop, fTop); 1702 1703 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1704 1705 ALocalEnv lBottom = _env.makeLocalEnv(new Point.Double(1.0, 2.0)); 1706 GenericFish fBottom = new GenericFish(Color.RED); 1707 fBottom.setLocalEnvironment(lBottom); 1708 _env.addFish(lBottom, fBottom); 1709 1710 assertEquals(2, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1711 1712 lTop.removeFish(fTop); 1713 1714 assertEquals(1, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1715 1716 lBottom.removeFish(fBottom); 1717 1718 assertEquals(0, ((Integer) lrs.execute(GetLength.Singleton, null)).intValue()); 1719 } 1720 1721 /** 1722 * Test to make sure only one move lambda can be executed. 1723 */ 1724 public void testOnlyOneMove() { 1725 LocalEnvironment l = (LocalEnvironment) _env.makeLocalEnv(new Point.Double(5.0, 5.0)); 1726 GenericFish f = new GenericFish(Color.RED); 1727 f.setLocalEnvironment(l); 1728 _env.addFish(l, f); 1729 1730 final LinkedList<ILambda> lambdas = new LinkedList<ILambda>(); 1731 l.tryMoveFwd(f, new IBlockedCommand() { 1732 public Object apply(Object param) { 1733 throw new RuntimeException("Should be open"); 1734 } 1735 }, new IOpenCommand() { 1736 public Object apply(Object param) { 1737 lambdas.add((ILambda)param); 1738 return null; 1739 } 1740 }); 1741 f.turnRight(); 1742 l.tryMoveFwd(f, new IBlockedCommand() { 1743 public Object apply(Object param) { 1744 throw new RuntimeException("Should be open"); 1745 } 1746 }, new IOpenCommand() { 1747 public Object apply(Object param) { 1748 lambdas.add((ILambda)param); 1749 return null; 1750 } 1751 }); 1752 1753 assertEquals(2, lambdas.size()); 1754 1755 lambdas.get(0).apply(null); 1756 NoGridEnv.Location loc = l.location(); 1757 assertTrue(loc.same(new NoGridEnv.Location(5.0, 4.0))); 1758 1759 lambdas.get(1).apply(null); 1760 loc = l.location(); 1761 assertTrue(loc.same(new NoGridEnv.Location(5.0, 4.0))); 1762 1763 lambdas.clear(); 1764 l.tryMoveFwd(f, new IBlockedCommand() { 1765 public Object apply(Object param) { 1766 throw new RuntimeException("Should be open"); 1767 } 1768 }, new IOpenCommand() { 1769 public Object apply(Object param) { 1770 lambdas.add((ILambda)param); 1771 return null; 1772 } 1773 }); 1774 f.turnRight(); 1775 l.tryMoveFwd(f, new IBlockedCommand() { 1776 public Object apply(Object param) { 1777 throw new RuntimeException("Should be open"); 1778 } 1779 }, new IOpenCommand() { 1780 public Object apply(Object param) { 1781 lambdas.add((ILambda)param); 1782 return null; 1783 } 1784 }); 1785 1786 assertEquals(2, lambdas.size()); 1787 1788 lambdas.get(1).apply(null); 1789 loc = l.location(); 1790 assertTrue(loc.same(new NoGridEnv.Location(6.0, 4.0))); 1791 1792 lambdas.get(0).apply(null); 1793 loc = l.location(); 1794 assertTrue(loc.same(new NoGridEnv.Location(6.0, 4.0))); 1795 } 1796 } 1797 1798 }