1 /* 2 * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package org.jemmy; 27 28 29 import java.io.Serializable; 30 31 32 /** 33 * Replacement for java.awt.Rectangle 34 * @author Alexander Kouznetsov <mrkam@mail.ru> 35 */ 36 public class Rectangle implements Serializable { 37 38 /** 39 * The X coordinate of the upper-left corner of the <code>Rectangle</code>. 40 * 41 * @serial 42 * @see #setLocation(int, int) 43 * @see #getLocation() 44 */ 45 public int x; 46 47 /** 48 * The Y coordinate of the upper-left corner of the <code>Rectangle</code>. 49 * 50 * @serial 51 * @see #setLocation(int, int) 52 * @see #getLocation() 53 */ 54 public int y; 55 56 /** 57 * The width of the <code>Rectangle</code>. 58 * @serial 59 * @see #setSize(int, int) 60 * @see #getSize() 61 */ 62 public int width; 63 64 /** 65 * The height of the <code>Rectangle</code>. 66 * 67 * @serial 68 * @see #setSize(int, int) 69 * @see #getSize() 70 */ 71 public int height; 72 73 /* 74 * JDK 1.1 serialVersionUID 75 */ 76 private static final long serialVersionUID = -4345857070255674764L; 77 78 79 /** 80 * Constructs a new <code>Rectangle</code> whose upper-left corner 81 * is at (0, 0) in the coordinate space, and whose width and 82 * height are both zero. 83 */ 84 public Rectangle() { 85 this(0, 0, 0, 0); 86 } 87 88 /** 89 * Constructs a new <code>Rectangle</code>, initialized to match 90 * the values of the specified <code>Rectangle</code>. 91 * @param r the <code>Rectangle</code> from which to copy initial values 92 * to a newly constructed <code>Rectangle</code> 93 */ 94 public Rectangle(Rectangle r) { 95 this(r.x, r.y, r.width, r.height); 96 } 97 98 /** 99 * Constructs a new <code>Rectangle</code> whose upper-left corner is 100 * specified as 101 * {@code (x,y)} and whose width and height 102 * are specified by the arguments of the same name. 103 * @param x the specified X coordinate 104 * @param y the specified Y coordinate 105 * @param width the width of the <code>Rectangle</code> 106 * @param height the height of the <code>Rectangle</code> 107 */ 108 public Rectangle(int x, int y, int width, int height) { 109 this.x = x; 110 this.y = y; 111 this.width = width; 112 this.height = height; 113 } 114 115 /** 116 * Constructs a new <code>Rectangle</code> whose upper-left corner 117 * is at (0, 0) in the coordinate space, and whose width and 118 * height are specified by the arguments of the same name. 119 * @param width the width of the <code>Rectangle</code> 120 * @param height the height of the <code>Rectangle</code> 121 */ 122 public Rectangle(int width, int height) { 123 this(0, 0, width, height); 124 } 125 126 /** 127 * Constructs a new <code>Rectangle</code> whose upper-left corner is 128 * specified by the {@link Point} argument, and 129 * whose width and height are specified by the 130 * {@link Dimension} argument. 131 * @param p a <code>Point</code> that is the upper-left corner of 132 * the <code>Rectangle</code> 133 * @param d a <code>Dimension</code>, representing the 134 * width and height of the <code>Rectangle</code> 135 */ 136 public Rectangle(Point p, Dimension d) { 137 this(p.x, p.y, d.width, d.height); 138 } 139 140 /** 141 * Constructs a new <code>Rectangle</code> whose upper-left corner is the 142 * specified <code>Point</code>, and whose width and height are both zero. 143 * @param p a <code>Point</code> that is the top left corner 144 * of the <code>Rectangle</code> 145 */ 146 public Rectangle(Point p) { 147 this(p.x, p.y, 0, 0); 148 } 149 150 /** 151 * Constructs a new <code>Rectangle</code> whose top left corner is 152 * (0, 0) and whose width and height are specified 153 * by the <code>Dimension</code> argument. 154 * @param d a <code>Dimension</code>, specifying width and height 155 */ 156 public Rectangle(Dimension d) { 157 this(0, 0, d.width, d.height); 158 } 159 160 /** 161 * Constructs a new <code>Rectangle</code> whose upper-left corner is 162 * specified as {@code (x,y)} and whose width and height 163 * are specified by the arguments of the same name. All {@code double} 164 * values are rounded and stored as {@code int} values. 165 * @param x the specified X coordinate 166 * @param y the specified Y coordinate 167 * @param width the width of the <code>Rectangle</code> 168 * @param height the height of the <code>Rectangle</code> 169 */ 170 public Rectangle(double x, double y, double width, double height) { 171 this((int) Math.round(x), (int) Math.round(y), 172 (int) Math.round(width), (int) Math.round(height)); 173 } 174 175 /** 176 * Returns the X coordinate of the bounding <code>Rectangle</code> in 177 * <code>double</code> precision. 178 * @return the X coordinate of the bounding <code>Rectangle</code>. 179 */ 180 public double getX() { 181 return x; 182 } 183 184 /** 185 * Returns the Y coordinate of the bounding <code>Rectangle</code> in 186 * <code>double</code> precision. 187 * @return the Y coordinate of the bounding <code>Rectangle</code>. 188 */ 189 public double getY() { 190 return y; 191 } 192 193 /** 194 * Returns the width of the bounding <code>Rectangle</code> in 195 * <code>double</code> precision. 196 * @return the width of the bounding <code>Rectangle</code>. 197 */ 198 public double getWidth() { 199 return width; 200 } 201 202 /** 203 * Returns the height of the bounding <code>Rectangle</code> in 204 * <code>double</code> precision. 205 * @return the height of the bounding <code>Rectangle</code>. 206 */ 207 public double getHeight() { 208 return height; 209 } 210 211 /** 212 * Gets the bounding <code>Rectangle</code> of this <code>Rectangle</code>. 213 * <p> 214 * @return a new <code>Rectangle</code>, equal to the 215 * bounding <code>Rectangle</code> for this <code>Rectangle</code>. 216 * @see #setBounds(Rectangle) 217 * @see #setBounds(int, int, int, int) 218 */ 219 public Rectangle getBounds() { 220 return new Rectangle(x, y, width, height); 221 } 222 223 /** 224 * Sets the bounding <code>Rectangle</code> of this <code>Rectangle</code> 225 * to match the specified <code>Rectangle</code>. 226 * <p> 227 * @param r the specified <code>Rectangle</code> 228 * @see #getBounds 229 */ 230 public void setBounds(Rectangle r) { 231 setBounds(r.x, r.y, r.width, r.height); 232 } 233 234 /** 235 * Sets the bounding <code>Rectangle</code> of this 236 * <code>Rectangle</code> to the specified 237 * <code>x</code>, <code>y</code>, <code>width</code>, 238 * and <code>height</code>. 239 * <p> 240 * @param x the new X coordinate for the upper-left 241 * corner of this <code>Rectangle</code> 242 * @param y the new Y coordinate for the upper-left 243 * corner of this <code>Rectangle</code> 244 * @param width the new width for this <code>Rectangle</code> 245 * @param height the new height for this <code>Rectangle</code> 246 * @see #getBounds 247 */ 248 public void setBounds(int x, int y, int width, int height) { 249 this.x = x; 250 this.y = y; 251 this.width = width; 252 this.height = height; 253 } 254 255 /** 256 * Sets the bounds of this {@code Rectangle} to the integer bounds 257 * which encompass the specified {@code x}, {@code y}, {@code width}, 258 * and {@code height}. 259 * If the parameters specify a {@code Rectangle} that exceeds the 260 * maximum range of integers, the result will be the best 261 * representation of the specified {@code Rectangle} intersected 262 * with the maximum integer bounds. 263 * @param x the X coordinate of the upper-left corner of 264 * the specified rectangle 265 * @param y the Y coordinate of the upper-left corner of 266 * the specified rectangle 267 * @param width the width of the specified rectangle 268 * @param height the new height of the specified rectangle 269 */ 270 public void setRect(double x, double y, double width, double height) { 271 int newx, newy, neww, newh; 272 273 if (x > 2.0 * Integer.MAX_VALUE) { 274 // Too far in positive X direction to represent... 275 // We cannot even reach the left side of the specified 276 // rectangle even with both x & width set to MAX_VALUE. 277 // The intersection with the "maximal integer rectangle" 278 // is non-existant so we should use a width < 0. 279 // REMIND: Should we try to determine a more "meaningful" 280 // adjusted value for neww than just "-1"? 281 newx = Integer.MAX_VALUE; 282 neww = -1; 283 } else { 284 newx = clip(x, false); 285 if (width >= 0) width += x-newx; 286 neww = clip(width, width >= 0); 287 } 288 289 if (y > 2.0 * Integer.MAX_VALUE) { 290 // Too far in positive Y direction to represent... 291 newy = Integer.MAX_VALUE; 292 newh = -1; 293 } else { 294 newy = clip(y, false); 295 if (height >= 0) height += y-newy; 296 newh = clip(height, height >= 0); 297 } 298 299 setBounds(newx, newy, neww, newh); 300 } 301 // Return best integer representation for v, clipped to integer 302 // range and floor-ed or ceiling-ed, depending on the boolean. 303 private static int clip(double v, boolean doceil) { 304 if (v <= Integer.MIN_VALUE) { 305 return Integer.MIN_VALUE; 306 } 307 if (v >= Integer.MAX_VALUE) { 308 return Integer.MAX_VALUE; 309 } 310 return (int) (doceil ? Math.ceil(v) : Math.floor(v)); 311 } 312 313 /** 314 * Returns the location of this <code>Rectangle</code>. 315 * <p> 316 * @return the <code>Point</code> that is the upper-left corner of 317 * this <code>Rectangle</code>. 318 * @see #setLocation(Point) 319 * @see #setLocation(int, int) 320 */ 321 public Point getLocation() { 322 return new Point(x, y); 323 } 324 325 /** 326 * Moves this <code>Rectangle</code> to the specified location. 327 * <p> 328 * @param p the <code>Point</code> specifying the new location 329 * for this <code>Rectangle</code> 330 * @see #getLocation 331 */ 332 public void setLocation(Point p) { 333 setLocation(p.x, p.y); 334 } 335 336 /** 337 * Moves this <code>Rectangle</code> to the specified location. 338 * <p> 339 * @param x the X coordinate of the new location 340 * @param y the Y coordinate of the new location 341 * @see #getLocation 342 */ 343 public void setLocation(int x, int y) { 344 this.x = x; 345 this.y = y; 346 } 347 348 /** 349 * Translates this <code>Rectangle</code> the indicated distance, 350 * to the right along the X coordinate axis, and 351 * downward along the Y coordinate axis. 352 * @param dx the distance to move this <code>Rectangle</code> 353 * along the X axis 354 * @param dy the distance to move this <code>Rectangle</code> 355 * along the Y axis 356 * @see #setLocation(int, int) 357 * @see #setLocation(org.jemmy.Point) 358 */ 359 public void translate(int dx, int dy) { 360 int oldv = this.x; 361 int newv = oldv + dx; 362 if (dx < 0) { 363 // moving leftward 364 if (newv > oldv) { 365 // negative overflow 366 // Only adjust width if it was valid (>= 0). 367 if (width >= 0) { 368 // The right edge is now conceptually at 369 // newv+width, but we may move newv to prevent 370 // overflow. But we want the right edge to 371 // remain at its new location in spite of the 372 // clipping. Think of the following adjustment 373 // conceptually the same as: 374 // width += newv; newv = MIN_VALUE; width -= newv; 375 width += newv - Integer.MIN_VALUE; 376 // width may go negative if the right edge went past 377 // MIN_VALUE, but it cannot overflow since it cannot 378 // have moved more than MIN_VALUE and any non-negative 379 // number + MIN_VALUE does not overflow. 380 } 381 newv = Integer.MIN_VALUE; 382 } 383 } else { 384 // moving rightward (or staying still) 385 if (newv < oldv) { 386 // positive overflow 387 if (width >= 0) { 388 // Conceptually the same as: 389 // width += newv; newv = MAX_VALUE; width -= newv; 390 width += newv - Integer.MAX_VALUE; 391 // With large widths and large displacements 392 // we may overflow so we need to check it. 393 if (width < 0) width = Integer.MAX_VALUE; 394 } 395 newv = Integer.MAX_VALUE; 396 } 397 } 398 this.x = newv; 399 400 oldv = this.y; 401 newv = oldv + dy; 402 if (dy < 0) { 403 // moving upward 404 if (newv > oldv) { 405 // negative overflow 406 if (height >= 0) { 407 height += newv - Integer.MIN_VALUE; 408 // See above comment about no overflow in this case 409 } 410 newv = Integer.MIN_VALUE; 411 } 412 } else { 413 // moving downward (or staying still) 414 if (newv < oldv) { 415 // positive overflow 416 if (height >= 0) { 417 height += newv - Integer.MAX_VALUE; 418 if (height < 0) height = Integer.MAX_VALUE; 419 } 420 newv = Integer.MAX_VALUE; 421 } 422 } 423 this.y = newv; 424 } 425 426 /** 427 * Gets the size of this <code>Rectangle</code>, represented by 428 * the returned <code>Dimension</code>. 429 * <p> 430 * @return a <code>Dimension</code>, representing the size of 431 * this <code>Rectangle</code>. 432 * @see #setSize(Dimension) 433 * @see #setSize(int, int) 434 */ 435 public Dimension getSize() { 436 return new Dimension(width, height); 437 } 438 439 /** 440 * Sets the size of this <code>Rectangle</code> to match the 441 * specified <code>Dimension</code>. 442 * <p> 443 * @param d the new size for the <code>Dimension</code> object 444 * @see #getSize 445 */ 446 public void setSize(Dimension d) { 447 setSize(d.width, d.height); 448 } 449 450 /** 451 * Sets the size of this <code>Rectangle</code> to the specified 452 * width and height. 453 * <p> 454 * @param width the new width for this <code>Rectangle</code> 455 * @param height the new height for this <code>Rectangle</code> 456 * @see #getSize 457 */ 458 public void setSize(int width, int height) { 459 this.width = width; 460 this.height = height; 461 } 462 463 /** 464 * Checks whether or not this <code>Rectangle</code> contains the 465 * specified <code>Point</code>. 466 * @param p the <code>Point</code> to test 467 * @return <code>true</code> if the specified <code>Point</code> 468 * is inside this <code>Rectangle</code>; 469 * <code>false</code> otherwise. 470 */ 471 public boolean contains(Point p) { 472 return contains(p.x, p.y); 473 } 474 475 /** 476 * Checks whether or not this <code>Rectangle</code> contains the 477 * point at the specified location {@code (x,y)}. 478 * 479 * @param x the specified X coordinate 480 * @param y the specified Y coordinate 481 * @return <code>true</code> if the point 482 * {@code (x,y)} is inside this 483 * <code>Rectangle</code>; 484 * <code>false</code> otherwise. 485 */ 486 public boolean contains(int x, int y) { 487 return contains(x, y, 1, 1); 488 } 489 490 /** 491 * Checks whether or not this <code>Rectangle</code> entirely contains 492 * the specified <code>Rectangle</code>. 493 * 494 * @param r the specified <code>Rectangle</code> 495 * @return <code>true</code> if the <code>Rectangle</code> 496 * is contained entirely inside this <code>Rectangle</code>; 497 * <code>false</code> otherwise 498 */ 499 public boolean contains(Rectangle r) { 500 return contains(r.x, r.y, r.width, r.height); 501 } 502 503 /** 504 * Checks whether this <code>Rectangle</code> entirely contains 505 * the <code>Rectangle</code> 506 * at the specified location {@code (X,Y)} with the 507 * specified dimensions {@code (W,H)}. 508 * @param X the specified X coordinate 509 * @param Y the specified Y coordinate 510 * @param W the width of the <code>Rectangle</code> 511 * @param H the height of the <code>Rectangle</code> 512 * @return <code>true</code> if the <code>Rectangle</code> specified by 513 * {@code (X, Y, W, H)} 514 * is entirely enclosed inside this <code>Rectangle</code>; 515 * <code>false</code> otherwise. 516 */ 517 public boolean contains(int X, int Y, int W, int H) { 518 int w = this.width; 519 int h = this.height; 520 if ((w | h | W | H) < 0) { 521 // At least one of the dimensions is negative... 522 return false; 523 } 524 // Note: if any dimension is zero, tests below must return false... 525 int xx = this.x; 526 int yy = this.y; 527 if (X < xx || Y < yy) { 528 return false; 529 } 530 w += xx; 531 W += X; 532 if (W <= X) { 533 // X+W overflowed or W was zero, return false if... 534 // either original w or W was zero or 535 // x+w did not overflow or 536 // the overflowed x+w is smaller than the overflowed X+W 537 if (w >= xx || W > w) return false; 538 } else { 539 // X+W did not overflow and W was not zero, return false if... 540 // original w was zero or 541 // x+w did not overflow and x+w is smaller than X+W 542 if (w >= xx && W > w) return false; 543 } 544 h += yy; 545 H += Y; 546 if (H <= Y) { 547 if (h >= yy || H > h) return false; 548 } else { 549 if (h >= yy && H > h) return false; 550 } 551 return true; 552 } 553 554 /** 555 * Determines whether or not this <code>Rectangle</code> and the specified 556 * <code>Rectangle</code> intersect. Two rectangles intersect if 557 * their intersection is nonempty. 558 * 559 * @param r the specified <code>Rectangle</code> 560 * @return <code>true</code> if the specified <code>Rectangle</code> 561 * and this <code>Rectangle</code> intersect; 562 * <code>false</code> otherwise. 563 */ 564 public boolean intersects(Rectangle r) { 565 int tw = this.width; 566 int th = this.height; 567 int rw = r.width; 568 int rh = r.height; 569 if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) { 570 return false; 571 } 572 int tx = this.x; 573 int ty = this.y; 574 int rx = r.x; 575 int ry = r.y; 576 rw += rx; 577 rh += ry; 578 tw += tx; 579 th += ty; 580 // overflow || intersect 581 return ((rw < rx || rw > tx) && 582 (rh < ry || rh > ty) && 583 (tw < tx || tw > rx) && 584 (th < ty || th > ry)); 585 } 586 587 /** 588 * Computes the intersection of this <code>Rectangle</code> with the 589 * specified <code>Rectangle</code>. Returns a new <code>Rectangle</code> 590 * that represents the intersection of the two rectangles. 591 * If the two rectangles do not intersect, the result will be 592 * an empty rectangle. 593 * 594 * @param r the specified <code>Rectangle</code> 595 * @return the largest <code>Rectangle</code> contained in both the 596 * specified <code>Rectangle</code> and in 597 * this <code>Rectangle</code>; or if the rectangles 598 * do not intersect, an empty rectangle. 599 */ 600 public Rectangle intersection(Rectangle r) { 601 int tx1 = this.x; 602 int ty1 = this.y; 603 int rx1 = r.x; 604 int ry1 = r.y; 605 long tx2 = tx1; tx2 += this.width; 606 long ty2 = ty1; ty2 += this.height; 607 long rx2 = rx1; rx2 += r.width; 608 long ry2 = ry1; ry2 += r.height; 609 if (tx1 < rx1) tx1 = rx1; 610 if (ty1 < ry1) ty1 = ry1; 611 if (tx2 > rx2) tx2 = rx2; 612 if (ty2 > ry2) ty2 = ry2; 613 tx2 -= tx1; 614 ty2 -= ty1; 615 // tx2,ty2 will never overflow (they will never be 616 // larger than the smallest of the two source w,h) 617 // they might underflow, though... 618 if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE; 619 if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE; 620 return new Rectangle(tx1, ty1, (int) tx2, (int) ty2); 621 } 622 623 /** 624 * Computes the union of this <code>Rectangle</code> with the 625 * specified <code>Rectangle</code>. Returns a new 626 * <code>Rectangle</code> that 627 * represents the union of the two rectangles. 628 * <p> 629 * If either {@code Rectangle} has any dimension less than zero 630 * the rules for <a href=#NonExistant>non-existant</a> rectangles 631 * apply. 632 * If only one has a dimension less than zero, then the result 633 * will be a copy of the other {@code Rectangle}. 634 * If both have dimension less than zero, then the result will 635 * have at least one dimension less than zero. 636 * <p> 637 * If the resulting {@code Rectangle} would have a dimension 638 * too large to be expressed as an {@code int}, the result 639 * will have a dimension of {@code Integer.MAX_VALUE} along 640 * that dimension. 641 * @param r the specified <code>Rectangle</code> 642 * @return the smallest <code>Rectangle</code> containing both 643 * the specified <code>Rectangle</code> and this 644 * <code>Rectangle</code>. 645 */ 646 public Rectangle union(Rectangle r) { 647 long tx2 = this.width; 648 long ty2 = this.height; 649 if ((tx2 | ty2) < 0) { 650 // This rectangle has negative dimensions... 651 // If r has non-negative dimensions then it is the answer. 652 // If r is non-existant (has a negative dimension), then both 653 // are non-existant and we can return any non-existant rectangle 654 // as an answer. Thus, returning r meets that criterion. 655 // Either way, r is our answer. 656 return new Rectangle(r); 657 } 658 long rx2 = r.width; 659 long ry2 = r.height; 660 if ((rx2 | ry2) < 0) { 661 return new Rectangle(this); 662 } 663 int tx1 = this.x; 664 int ty1 = this.y; 665 tx2 += tx1; 666 ty2 += ty1; 667 int rx1 = r.x; 668 int ry1 = r.y; 669 rx2 += rx1; 670 ry2 += ry1; 671 if (tx1 > rx1) tx1 = rx1; 672 if (ty1 > ry1) ty1 = ry1; 673 if (tx2 < rx2) tx2 = rx2; 674 if (ty2 < ry2) ty2 = ry2; 675 tx2 -= tx1; 676 ty2 -= ty1; 677 // tx2,ty2 will never underflow since both original rectangles 678 // were already proven to be non-empty 679 // they might overflow, though... 680 if (tx2 > Integer.MAX_VALUE) tx2 = Integer.MAX_VALUE; 681 if (ty2 > Integer.MAX_VALUE) ty2 = Integer.MAX_VALUE; 682 return new Rectangle(tx1, ty1, (int) tx2, (int) ty2); 683 } 684 685 /** 686 * Adds a point, specified by the integer arguments {@code newx,newy} 687 * to the bounds of this {@code Rectangle}. 688 * <p> 689 * If this {@code Rectangle} has any dimension less than zero, 690 * the rules for <a href=#NonExistant>non-existant</a> 691 * rectangles apply. 692 * In that case, the new bounds of this {@code Rectangle} will 693 * have a location equal to the specified coordinates and 694 * width and height equal to zero. 695 * <p> 696 * After adding a point, a call to <code>contains</code> with the 697 * added point as an argument does not necessarily return 698 * <code>true</code>. The <code>contains</code> method does not 699 * return <code>true</code> for points on the right or bottom 700 * edges of a <code>Rectangle</code>. Therefore, if the added point 701 * falls on the right or bottom edge of the enlarged 702 * <code>Rectangle</code>, <code>contains</code> returns 703 * <code>false</code> for that point. 704 * If the specified point must be contained within the new 705 * {@code Rectangle}, a 1x1 rectangle should be added instead: 706 * <pre> 707 * r.add(newx, newy, 1, 1); 708 * </pre> 709 * @param newx the X coordinate of the new point 710 * @param newy the Y coordinate of the new point 711 */ 712 public void add(int newx, int newy) { 713 if ((width | height) < 0) { 714 this.x = newx; 715 this.y = newy; 716 this.width = this.height = 0; 717 return; 718 } 719 int x1 = this.x; 720 int y1 = this.y; 721 long x2 = this.width; 722 long y2 = this.height; 723 x2 += x1; 724 y2 += y1; 725 if (x1 > newx) x1 = newx; 726 if (y1 > newy) y1 = newy; 727 if (x2 < newx) x2 = newx; 728 if (y2 < newy) y2 = newy; 729 x2 -= x1; 730 y2 -= y1; 731 if (x2 > Integer.MAX_VALUE) x2 = Integer.MAX_VALUE; 732 if (y2 > Integer.MAX_VALUE) y2 = Integer.MAX_VALUE; 733 setBounds(x1, y1, (int) x2, (int) y2); 734 } 735 736 /** 737 * Adds the specified {@code Point} to the bounds of this 738 * {@code Rectangle}. 739 * <p> 740 * If this {@code Rectangle} has any dimension less than zero, 741 * the rules for <a href=#NonExistant>non-existant</a> 742 * rectangles apply. 743 * In that case, the new bounds of this {@code Rectangle} will 744 * have a location equal to the coordinates of the specified 745 * {@code Point} and width and height equal to zero. 746 * <p> 747 * After adding a <code>Point</code>, a call to <code>contains</code> 748 * with the added <code>Point</code> as an argument does not 749 * necessarily return <code>true</code>. The <code>contains</code> 750 * method does not return <code>true</code> for points on the right 751 * or bottom edges of a <code>Rectangle</code>. Therefore if the added 752 * <code>Point</code> falls on the right or bottom edge of the 753 * enlarged <code>Rectangle</code>, <code>contains</code> returns 754 * <code>false</code> for that <code>Point</code>. 755 * If the specified point must be contained within the new 756 * {@code Rectangle}, a 1x1 rectangle should be added instead: 757 * <pre> 758 * r.add(pt.x, pt.y, 1, 1); 759 * </pre> 760 * @param pt the new <code>Point</code> to add to this 761 * <code>Rectangle</code> 762 */ 763 public void add(Point pt) { 764 add(pt.x, pt.y); 765 } 766 767 /** 768 * Adds a <code>Rectangle</code> to this <code>Rectangle</code>. 769 * The resulting <code>Rectangle</code> is the union of the two 770 * rectangles. 771 * <p> 772 * If either {@code Rectangle} has any dimension less than 0, the 773 * result will have the dimensions of the other {@code Rectangle}. 774 * If both {@code Rectangle}s have at least one dimension less 775 * than 0, the result will have at least one dimension less than 0. 776 * <p> 777 * If either {@code Rectangle} has one or both dimensions equal 778 * to 0, the result along those axes with 0 dimensions will be 779 * equivalent to the results obtained by adding the corresponding 780 * origin coordinate to the result rectangle along that axis, 781 * similar to the operation of the {@link #add(Point)} method, 782 * but contribute no further dimension beyond that. 783 * <p> 784 * If the resulting {@code Rectangle} would have a dimension 785 * too large to be expressed as an {@code int}, the result 786 * will have a dimension of {@code Integer.MAX_VALUE} along 787 * that dimension. 788 * @param r the specified <code>Rectangle</code> 789 */ 790 public void add(Rectangle r) { 791 long tx2 = this.width; 792 long ty2 = this.height; 793 if ((tx2 | ty2) < 0) { 794 setBounds(r.x, r.y, r.width, r.height); 795 } 796 long rx2 = r.width; 797 long ry2 = r.height; 798 if ((rx2 | ry2) < 0) { 799 return; 800 } 801 int tx1 = this.x; 802 int ty1 = this.y; 803 tx2 += tx1; 804 ty2 += ty1; 805 int rx1 = r.x; 806 int ry1 = r.y; 807 rx2 += rx1; 808 ry2 += ry1; 809 if (tx1 > rx1) tx1 = rx1; 810 if (ty1 > ry1) ty1 = ry1; 811 if (tx2 < rx2) tx2 = rx2; 812 if (ty2 < ry2) ty2 = ry2; 813 tx2 -= tx1; 814 ty2 -= ty1; 815 // tx2,ty2 will never underflow since both original 816 // rectangles were non-empty 817 // they might overflow, though... 818 if (tx2 > Integer.MAX_VALUE) tx2 = Integer.MAX_VALUE; 819 if (ty2 > Integer.MAX_VALUE) ty2 = Integer.MAX_VALUE; 820 setBounds(tx1, ty1, (int) tx2, (int) ty2); 821 } 822 823 /** 824 * Resizes the <code>Rectangle</code> both horizontally and vertically. 825 * <p> 826 * This method modifies the <code>Rectangle</code> so that it is 827 * <code>h</code> units larger on both the left and right side, 828 * and <code>v</code> units larger at both the top and bottom. 829 * <p> 830 * The new <code>Rectangle</code> has {@code (x - h, y - v)} 831 * as its upper-left corner, 832 * width of {@code (width + 2h)}, 833 * and a height of {@code (height + 2v)}. 834 * <p> 835 * If negative values are supplied for <code>h</code> and 836 * <code>v</code>, the size of the <code>Rectangle</code> 837 * decreases accordingly. 838 * The {@code grow} method will check for integer overflow 839 * and underflow, but does not check whether the resulting 840 * values of {@code width} and {@code height} grow 841 * from negative to non-negative or shrink from non-negative 842 * to negative. 843 * @param h the horizontal expansion 844 * @param v the vertical expansion 845 */ 846 public void grow(int h, int v) { 847 long x0 = this.x; 848 long y0 = this.y; 849 long x1 = this.width; 850 long y1 = this.height; 851 x1 += x0; 852 y1 += y0; 853 854 x0 -= h; 855 y0 -= v; 856 x1 += h; 857 y1 += v; 858 859 if (x1 < x0) { 860 // Non-existant in X direction 861 // Final width must remain negative so subtract x0 before 862 // it is clipped so that we avoid the risk that the clipping 863 // of x0 will reverse the ordering of x0 and x1. 864 x1 -= x0; 865 if (x1 < Integer.MIN_VALUE) x1 = Integer.MIN_VALUE; 866 if (x0 < Integer.MIN_VALUE) x0 = Integer.MIN_VALUE; 867 else if (x0 > Integer.MAX_VALUE) x0 = Integer.MAX_VALUE; 868 } else { // (x1 >= x0) 869 // Clip x0 before we subtract it from x1 in case the clipping 870 // affects the representable area of the rectangle. 871 if (x0 < Integer.MIN_VALUE) x0 = Integer.MIN_VALUE; 872 else if (x0 > Integer.MAX_VALUE) x0 = Integer.MAX_VALUE; 873 x1 -= x0; 874 // The only way x1 can be negative now is if we clipped 875 // x0 against MIN and x1 is less than MIN - in which case 876 // we want to leave the width negative since the result 877 // did not intersect the representable area. 878 if (x1 < Integer.MIN_VALUE) x1 = Integer.MIN_VALUE; 879 else if (x1 > Integer.MAX_VALUE) x1 = Integer.MAX_VALUE; 880 } 881 882 if (y1 < y0) { 883 // Non-existant in Y direction 884 y1 -= y0; 885 if (y1 < Integer.MIN_VALUE) y1 = Integer.MIN_VALUE; 886 if (y0 < Integer.MIN_VALUE) y0 = Integer.MIN_VALUE; 887 else if (y0 > Integer.MAX_VALUE) y0 = Integer.MAX_VALUE; 888 } else { // (y1 >= y0) 889 if (y0 < Integer.MIN_VALUE) y0 = Integer.MIN_VALUE; 890 else if (y0 > Integer.MAX_VALUE) y0 = Integer.MAX_VALUE; 891 y1 -= y0; 892 if (y1 < Integer.MIN_VALUE) y1 = Integer.MIN_VALUE; 893 else if (y1 > Integer.MAX_VALUE) y1 = Integer.MAX_VALUE; 894 } 895 896 setBounds((int) x0, (int) y0, (int) x1, (int) y1); 897 } 898 899 /** 900 * {@inheritDoc} 901 * @return 902 */ 903 public boolean isEmpty() { 904 return (width <= 0) || (height <= 0); 905 } 906 907 /** 908 * Checks whether two rectangles are equal. 909 * <p> 910 * The result is <code>true</code> if and only if the argument is not 911 * <code>null</code> and is a <code>Rectangle</code> object that has the 912 * same upper-left corner, width, and height as 913 * this <code>Rectangle</code>. 914 * @param obj the <code>Object</code> to compare with 915 * this <code>Rectangle</code> 916 * @return <code>true</code> if the objects are equal; 917 * <code>false</code> otherwise. 918 */ 919 @Override 920 public boolean equals(Object obj) { 921 if (obj instanceof Rectangle) { 922 Rectangle r = (Rectangle)obj; 923 return ((x == r.x) && 924 (y == r.y) && 925 (width == r.width) && 926 (height == r.height)); 927 } 928 return super.equals(obj); 929 } 930 931 /** 932 * {@inheritDoc } 933 * @return 934 */ 935 @Override 936 public int hashCode() { 937 int hash = 7; 938 hash = 29 * hash + this.x; 939 hash = 29 * hash + this.y; 940 hash = 29 * hash + this.width; 941 hash = 29 * hash + this.height; 942 return hash; 943 } 944 945 /** 946 * Returns a <code>String</code> representing this 947 * <code>Rectangle</code> and its values. 948 * @return a <code>String</code> representing this 949 * <code>Rectangle</code> object's coordinate and size values. 950 */ 951 @Override 952 public String toString() { 953 return getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + "]"; 954 } 955 956 /** 957 * Parses given string to restore Rectangle instance from the string. 958 * String assumed to be previously created using 959 * {@linkplain #toString() Rectangle.toString()} method. 960 * @param str String to parse. 961 * @return Recreated Rectangle instance. 962 */ 963 public static Rectangle parseRectangle(String str) { 964 if (str != null && str.startsWith(Rectangle.class.getName())) { 965 try { 966 String[] t = 967 str.substring(Rectangle.class.getName().length() + 1) 968 .split("\\,|\\]"); 969 Rectangle res = new Rectangle(); 970 for(String pair : t) { 971 String[] p = pair.split("\\="); 972 String var = p[0]; 973 int value = Integer.parseInt(p[1]); 974 res.getClass().getDeclaredField(var).setInt(res, value); 975 } 976 return res; 977 } catch (Exception ex) { 978 throw new JemmyException( 979 "Failed to parse Rectangle '" + str + "'", ex); 980 } 981 } 982 return null; 983 } 984 }