1 /* 2 * Copyright (c) 2011, 2018, 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 test.javafx.scene; 27 28 import javafx.collections.ObservableList; 29 import com.sun.javafx.geom.BaseBounds; 30 import com.sun.javafx.geom.transform.BaseTransform; 31 import com.sun.javafx.sg.prism.NGGroup; 32 import com.sun.javafx.sg.prism.NGNode; 33 import javafx.scene.Group; 34 import javafx.scene.Node; 35 import javafx.scene.NodeShim; 36 import javafx.scene.Parent; 37 import javafx.scene.ParentShim; 38 import javafx.scene.Scene; 39 import org.junit.Rule; 40 import org.junit.Test; 41 import org.junit.rules.ExpectedException; 42 import static org.junit.Assert.assertEquals; 43 import static org.junit.Assert.assertNull; 44 import static org.junit.Assert.assertSame; 45 import static org.junit.Assert.assertTrue; 46 import static org.junit.Assert.fail; 47 import test.com.sun.javafx.scene.StubNodeHelper; 48 import test.com.sun.javafx.scene.StubParentHelper; 49 50 51 /** 52 * Tests structural aspects of scene graph manipulation. See RT-4095. 53 * 54 * The following notation is used in test names to indicate various 55 * relationships: 56 * CL = clip child 57 * G = Group child 58 * S = Scene child 59 * 60 * Several relationships are checked in each test, typically in 61 * the following order: 62 * 63 * parent's clip 64 * parent's children (for Group and Region) 65 * relationships of second parent (if any) 66 * child's clipParent 67 * child's parent 68 * child's scene 69 */ 70 71 public class StructureTest { 72 @Rule 73 public ExpectedException thrown = ExpectedException.none(); 74 75 // TODO: 76 // - various nasty observableArrayList updates to Group.content and Scene.content. 77 // - various bind expressions. 78 79 ///////////////////////// 80 // Setup and teardown. // 81 ///////////////////////// 82 83 ////////////////////// 84 // Helper Functions // 85 ////////////////////// 86 87 int occurs(Node child, ObservableList<Node> content) { 88 int count = 0; 89 if (content != null) { 90 for (Node node : content) { 91 if (node == child) { 92 count++; 93 } 94 } 95 } 96 return count; 97 } 98 99 /** 100 * Checks whether child occurs exactly once in the content observableArrayList. 101 */ 102 boolean occursOnce(Node child, ObservableList<Node> content) { 103 return 1 == occurs(child, content); 104 } 105 106 /** 107 * Checks whether child does not occur in the content observableArrayList. 108 */ 109 boolean occursZero(Node child, ObservableList<Node> content) { 110 return 0 == occurs(child, content); 111 } 112 113 /** 114 * Checks whether the child node appears exactly once in the 115 * Group's content observableArrayList. 116 */ 117 boolean isChild(Node child, Group group) { 118 return occursOnce(child, ParentShim.getChildren(group)); 119 } 120 121 /** 122 * Checks whether the child node is the root of the Scene. 123 */ 124 boolean isRoot(Parent root, Scene scene) { 125 return root == scene.getRoot(); 126 } 127 128 /** 129 * Checks whether the child node does not appear in the 130 * Group's content observableArrayList. 131 * 132 * We use assertTrue(notChild(child, parent)) instead of 133 * assertFalse(isChild(child, parent)) because we want 134 * to catch cases where child occurs more than once in 135 * the parent's content observableArrayList. 136 */ 137 boolean notChild(Node child, Group group) { 138 return occursZero(child, ParentShim.getChildren(group)); 139 } 140 141 /** 142 * Checks whether the child node does not appear as the root of the 143 * Scene. 144 */ 145 boolean notRoot(Parent root, Scene scene) { 146 return root != scene.getRoot(); 147 } 148 149 ///////////////////////////////////// 150 // Simple Structural Relationships // 151 ///////////////////////////////////// 152 153 @Test 154 public void testOrphan() { 155 Node n = new StubNode(); 156 157 assertNull("clipParent is null", NodeShim.getClipParent(n)); 158 assertNull("parent is null", n.getParent()); 159 assertNull("scene is null", n.getScene()); 160 } 161 162 @Test 163 public void testSimpleCL() { 164 StubNode parent = new StubNode(); 165 StubNode child = new StubNode(); 166 parent.setClip(child); 167 168 assertSame("parent.clip is child", child, parent.getClip()); 169 assertSame("child.clipParent is parent", parent, NodeShim.getClipParent(child)); 170 assertNull("child.parent is null", child.getParent()); 171 assertNull("scene is null", child.getScene()); 172 } 173 174 @Test 175 public void testSimpleG() { 176 StubNode child = new StubNode(); 177 Group group = new Group(child); 178 179 assertNull("group.clip is null", group.getClip()); 180 assertTrue("isChild of group", isChild(child, group)); 181 assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 182 assertSame("child.parent is parent", group, child.getParent()); 183 assertNull("child.getScene() is null", child.getScene()); 184 } 185 186 @Test 187 public void testSimpleS() { 188 StubParent root = new StubParent(); 189 Scene scene = new Scene(root); 190 191 assertTrue("isChild of scene", isRoot(root, scene)); 192 assertNull("child.clipParent is null", NodeShim.getClipParent(root)); 193 assertSame("child.getScene() is scene", scene, root.getScene()); 194 } 195 196 @Test 197 public void testSceneInsertGroup1() { 198 StubNode child = new StubNode(); 199 Group group = new Group(); 200 Scene scene = new Scene(group); 201 ParentShim.getChildren(group).add(child); 202 203 assertSame("group.getScene() is scene", scene, group.getScene()); 204 assertSame("child.getScene() is scene", scene, child.getScene()); 205 } 206 207 @Test 208 public void testSceneInsertGroup2() { 209 StubNode child = new StubNode(); 210 Group group = new Group(); 211 Scene scene = new Scene(group); 212 ParentShim.getChildren(group).add(child); 213 214 215 assertSame("group.getScene() is scene", scene, group.getScene()); 216 assertSame("child.getScene() is scene", scene, child.getScene()); 217 } 218 219 @Test public void testUnparentCL() { 220 StubNode child = new StubNode(); 221 StubNode parent = new StubNode(); 222 parent.setClip(child); 223 parent.setClip(null); 224 225 assertNull("parent.clip is null", parent.getClip()); 226 assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 227 } 228 229 @Test public void testUnparentG() { 230 StubNode child = new StubNode(); 231 Group parent = new Group(child); 232 233 ParentShim.getChildren(parent).remove(child); 234 235 236 assertEquals("parent.content is zero size", 0, ParentShim.getChildren(parent).size()); 237 assertNull("child.parent is null", child.getParent()); 238 } 239 240 //////////////////////////////////// 241 // Illegal Structure Change Tests // 242 //////////////////////////////////// 243 244 // Test attempts to switch from one part of the scene graph to another. 245 // This is the cross product: {CL,CU,G,S}x{CL,CU,G,S} so there 246 // are sixteen cases. 247 248 @Test public void testSwitchCLCL() { 249 StubNode child = new StubNode(); 250 StubNode p1 = new StubNode(); 251 p1.setClip(child); 252 StubNode p2 = new StubNode(); 253 thrown.expect(IllegalArgumentException.class); 254 try { 255 p2.setClip(child); 256 } catch (final IllegalArgumentException e) { 257 assertSame("p1.clip is child", child, p1.getClip()); 258 assertNull("p2.clip is null", p2.getClip()); 259 assertSame("child.clipParent is p1", 260 p1, NodeShim.getClipParent(child)); 261 assertNull("child.parent is null", child.getParent()); 262 assertNull("child.getScene() is null", child.getScene()); 263 throw e; 264 } 265 } 266 267 @Test public void testSwitchCLG() { 268 StubNode child = new StubNode(); 269 StubNode p1 = new StubNode(); 270 p1.setClip(child); 271 Group p2 = new Group(); 272 ObservableList<Node> content = ParentShim.getChildren(p2); 273 try { 274 content.add(child); 275 fail("IllegalArgument should have been thrown."); 276 } catch (IllegalArgumentException iae) { 277 // expected 278 } 279 280 assertSame("p1.clip is child", child, p1.getClip()); 281 assertNull("p2.clip is null", p2.getClip()); 282 assertTrue("notChild of p2", notChild(child, p2)); 283 assertSame("child.clipParent is p1", p1, NodeShim.getClipParent(child)); 284 assertNull("child.parent is null", child.getParent()); 285 assertNull("child.getScene() is null", child.getScene()); 286 } 287 288 @Test public void testSwitchCLS() { 289 StubParent clipNode = new StubParent(); 290 StubNode p1 = new StubNode(); 291 p1.setClip(clipNode); 292 try { 293 Scene p2 = new Scene(clipNode); 294 fail("IllegalArgument should have been thrown."); 295 } catch (Throwable t) { 296 //expected 297 } 298 assertSame("p1.clip is child", clipNode, p1.getClip()); 299 assertSame("child.clipParent is p1", p1, NodeShim.getClipParent(clipNode)); 300 assertNull("child.parent is null", clipNode.getParent()); 301 assertNull("child.getScene() is null", clipNode.getScene()); 302 } 303 304 @Test public void testSwitchGCL() { 305 StubNode child = new StubNode(); 306 Group p1 = new Group(child); 307 StubNode p2 = new StubNode(); 308 thrown.expect(IllegalArgumentException.class); 309 try { 310 p2.setClip(child); 311 } catch (final IllegalArgumentException e) { 312 assertNull("p1.clip is null", p1.getClip()); 313 assertTrue("isChild of p1", isChild(child, p1)); 314 assertNull("p2.clip is null", p2.getClip()); 315 assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 316 assertSame("child.parent is p1", p1, child.getParent()); 317 assertNull("child.getScene() is null", child.getScene()); 318 throw e; 319 } 320 } 321 322 // TODO XXX TEMPORARY STOPGAP POLICY RT-4095 -- TEST DISABLED 323 324 // @Test public void testSwitchGG() { 325 // var child = new StubNode(); 326 // var p1 = Group { content: [ child ] }; 327 // setHandler(); 328 // var p2 = Group { content: [ child ] }; 329 // 330 // assertTrue("caught IllegalArgumentException", caught instanceof IllegalArgumentException); 331 // assertNull("p1.clip is null", p1.clip); 332 // assertTrue("isChild of p1", isChild(child, p1)); 333 // assertNull("p2.clip is null", p2.clip); 334 // assertTrue("notChild of p2", notChild(child, p2)); 335 // assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 336 // assertSame("child.parent is p1", p1, child.parent); 337 // assertNull("child.getScene() is null", child.getScene()); 338 // } 339 340 // TODO XXX TEMPORARY STOPGAP POLICY RT-4095 -- TEST DISABLED 341 342 // @Test public void testSwitchGS() { 343 // var child = new StubNode(); 344 // var p1 = Group { content: [ child ] }; 345 // setHandler(); 346 // var p2 = Scene { content: [ child ] }; 347 // 348 // assertTrue("caught IllegalArgumentException", caught instanceof IllegalArgumentException); 349 // assertNull("p1.clip is null", p1.clip); 350 // assertTrue("isChild of p1", isChild(child, p1)); 351 // assertTrue("notChild of p2", notChild(child, p2)); 352 // assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 353 // assertSame("child.parent is p1", p1, child.parent); 354 // assertNull("child.getScene() is null", child.getScene()); 355 // } 356 357 // TODO XXX TEMPORARY STOPGAP POLICY RT-4095 -- TEST OF STOPGAP POLICY 358 359 @Test public void testSwitchGGStopgap() { 360 StubNode child = new StubNode(); 361 Group p1 = new Group(child); 362 Group p2 = new Group(child); 363 364 assertTrue("notChild of p1", notChild(child, p1)); 365 assertTrue("isChild of p2", isChild(child, p2)); 366 assertSame("child.parent is p2", p2, child.getParent()); 367 } 368 369 @Test public void testSwitchSCL() { 370 StubParent root = new StubParent(); 371 Scene scene = new Scene(root); 372 StubNode p2 = new StubNode(); 373 thrown.expect(IllegalArgumentException.class); 374 try { 375 p2.setClip(root); 376 } catch (final IllegalArgumentException e) { 377 assertTrue("isRoot of scene", isRoot(root, scene)); 378 assertNull("p2.clip is null", p2.getClip()); 379 assertNull("root.clipParent is null", NodeShim.getClipParent(root)); 380 assertSame("root.getScene() is scene", scene, root.getScene()); 381 throw e; 382 } 383 } 384 385 386 // TODO XXX TEMPORARY STOPGAP POLICY RT-4095 -- TEST DISABLED 387 388 // @Test public void testSwitchSG() { 389 // var child = new StubNode(); 390 // var p1 = Scene { content: [ child ] }; 391 // setHandler(); 392 // var p2 = Group { content: [ child ] }; 393 // 394 // assertTrue("caught IllegalArgumentException", caught instanceof IllegalArgumentException); 395 // assertTrue("isChild of p1", isChild(child, p1)); 396 // assertNull("p2.clip is null", p2.clip); 397 // assertTrue("notChild of p2", notChild(child, p2)); 398 // assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 399 // assertSame("child.parent is p1.impl_root", p1.impl_root, child.parent); 400 // assertSame("child.getScene() is p1", p1, child.getScene()); 401 // } 402 403 // TODO XXX TEMPORARY STOPGAP POLICY RT-4095 -- TEST DISABLED 404 405 // @Test public void testSwitchSS() { 406 // var child = new StubNode(); 407 // var p1 = Scene { content: [ child ] }; 408 // setHandler(); 409 // var p2 = Scene { content: [ child ] }; 410 // 411 // assertTrue("isChild of p1", isChild(child, p1)); 412 // assertTrue("notChild of p2", notChild(child, p2)); 413 // assertNull("child.clipParent is null", NodeShim.getClipParent(child)); 414 // assertSame("child.parent is p1.impl_root", p1.impl_root, child.parent); 415 // assertSame("child.getScene() is p1", p1, child.getScene()); 416 // } 417 418 @Test public void testGroupInsert() { 419 StubNode n0 = new StubNode(); 420 n0.setId("n0"); 421 StubNode n1 = new StubNode(); 422 n1.setId("n1"); 423 StubNode n2 = new StubNode(); 424 n2.setId("n2"); 425 Group g = new Group(n0, n1, n2); 426 427 ObservableList<Node> content = ParentShim.getChildren(g); 428 try { 429 content.add(n1); 430 fail("IllegalArgument should have been thrown."); 431 } catch (IllegalArgumentException iae) { 432 // expected 433 } 434 435 assertEquals("g.content is size 3", 3, ParentShim.getChildren(g).size()); 436 assertSame("g.content[0] is n0", n0, ParentShim.getChildren(g).get(0)); 437 assertSame("g.content[1] is n1", n1, ParentShim.getChildren(g).get(1)); 438 assertSame("g.content[2] is n2", n2, ParentShim.getChildren(g).get(2)); 439 440 } 441 442 443 @Test public void testGroupReplace1() { 444 StubNode n0 = new StubNode(); 445 n0.setId("n0"); 446 StubNode n1 = new StubNode(); 447 n1.setId("n1"); 448 StubNode n2 = new StubNode(); 449 n2.setId("n2"); 450 Group g = new Group(n0, n1, n2); 451 452 ParentShim.getChildren(g).remove(1); 453 ObservableList<Node> n = javafx.collections.FXCollections.<Node>observableArrayList(); 454 n.addAll(n1,n1); 455 ObservableList<Node> content = ParentShim.getChildren(g); 456 try { 457 content.addAll(1, n); 458 fail("IllegalArgument should have been thrown."); 459 } catch (IllegalArgumentException iae) { 460 // expected 461 } 462 463 assertEquals("g.content is size 2", 2, ParentShim.getChildren(g).size()); 464 assertSame("g.content[0] is n0", n0, ParentShim.getChildren(g).get(0)); 465 assertSame("g.content[1] is n2", n2, ParentShim.getChildren(g).get(1)); 466 } 467 468 @Test public void testGroupReplace2() { 469 StubNode n0 = new StubNode(); 470 n0.setId("n0"); 471 StubNode n1 = new StubNode(); 472 n1.setId("n1"); 473 StubNode n2 = new StubNode(); 474 n2.setId("n2"); 475 Group g = new Group(n0, n1, n2); 476 477 try { 478 ParentShim.getChildren(g).set(1, n0); 479 fail("No exception thrown."); 480 } catch (IllegalArgumentException e) { 481 //Expected 482 } 483 484 assertEquals("g.content is size 3", 3, ParentShim.getChildren(g).size()); 485 assertSame("g.content[0] is n0", n0, ParentShim.getChildren(g).get(0)); 486 assertSame("g.content[1] is n1", n1, ParentShim.getChildren(g).get(1)); 487 assertSame("g.content[2] is n2", n2, ParentShim.getChildren(g).get(2)); 488 } 489 490 @Test public void testGroupReplace3() { 491 StubNode n0 = new StubNode(); 492 n0.setId("n0"); 493 StubNode n1 = new StubNode(); 494 n1.setId("n1"); 495 StubNode n2 = new StubNode(); 496 n2.setId("n2"); 497 Group g = new Group(n0, n1, n2); 498 ParentShim.getChildren(g).set(1, n1); 499 ParentShim.getChildren(g).set(2, n2); 500 501 assertEquals("g.content is size 3", 3, ParentShim.getChildren(g).size()); 502 assertSame("g.content[0] is n0", n0, ParentShim.getChildren(g).get(0)); 503 assertSame("g.content[1] is n1", n1, ParentShim.getChildren(g).get(1)); 504 assertSame("g.content[2] is n2", n2, ParentShim.getChildren(g).get(2)); 505 } 506 507 /////////////////////// 508 // Circularity Tests // 509 /////////////////////// 510 511 // General form is: given an existing relationship of one kind, add 512 // another relationship of some kind that would cause a circularity. 513 // This is the cross product: {CL,CU,G}x{CL,CU,G}. Also test degenerate 514 // circularities where a node is its own clip, its own child. 515 // The Scene relationship does not occur here, because a Scene cannot 516 // participate in any circular relationship as it has no parent. 517 518 // Test only {CL,G}x{CL,G} for now. 519 520 @Test public void testCircularCLCL() { 521 StubNode node1 = new StubNode(); 522 StubNode node2 = new StubNode(); 523 node2.setClip(node1); 524 thrown.expect(IllegalArgumentException.class); 525 try { 526 node1.setClip(node2); 527 } catch (final IllegalArgumentException e) { 528 assertNull("node1.clip is null", node1.getClip()); 529 assertSame("node1.clipParent is node2", 530 node2, 531 NodeShim.getClipParent(node1)); 532 assertSame("node2.clip is node1", node1, node2.getClip()); 533 assertNull("node2.clipParent is null", NodeShim.getClipParent(node2)); 534 throw e; 535 } 536 } 537 538 @Test public void testCircularCLG() { 539 StubNode node1 = new StubNode(); 540 Group node2 = new Group(node1); 541 thrown.expect(IllegalArgumentException.class); 542 try { 543 node1.setClip(node2); 544 } catch (final IllegalArgumentException e) { 545 assertNull("node1.clip is null", node1.getClip()); 546 assertNull("node1.clipParent is null", NodeShim.getClipParent(node1)); 547 assertSame("node1.parent is node2", node2, node1.getParent()); 548 assertNull("node2.clip is null", node2.getClip()); 549 assertNull("node2.clipParent is null", NodeShim.getClipParent(node2)); 550 assertTrue("node1 is child of node2", isChild(node1, node2)); 551 throw e; 552 } 553 } 554 555 @Test public void testCircularGCL() { 556 Group node1 = new Group(); 557 StubNode node2 = new StubNode(); 558 node2.setClip(node1); 559 560 ObservableList<Node> content = ParentShim.getChildren(node1); 561 try { 562 content.add(node2); 563 fail("IllegalArgument should have been thrown."); 564 } catch (IllegalArgumentException iae) { 565 // expected 566 } 567 568 assertNull("node1.clip is null", node1.getClip()); 569 assertSame("node1.clipParent is node2", node2, NodeShim.getClipParent(node1)); 570 assertTrue("node2 is not child of node1", notChild(node2, node1)); 571 assertSame("node2.clip is node1", node1, node2.getClip()); 572 assertNull("node2.clipParent is null", NodeShim.getClipParent(node2)); 573 assertNull("node2.parent is null", node2.getParent()); 574 } 575 576 @Test public void testCircularGG() { 577 Group node1 = new Group(); 578 Group node2 = new Group(node1); 579 580 ObservableList<Node> content = ParentShim.getChildren(node1); 581 try { 582 content.add(node2); 583 fail("IllegalArgument should have been thrown."); 584 } catch (IllegalArgumentException iae) { 585 // expected 586 } 587 588 assertSame("node1.parent is node2", node2, node1.getParent()); 589 assertTrue("node2 is not a child of node1", notChild(node2, node1)); 590 assertNull("node2.parent is null", node2.getParent()); 591 assertTrue("node1 is child of node2", isChild(node1, node2)); 592 } 593 594 @Test public void testCircularSelfCL() { 595 StubNode node1 = new StubNode(); 596 thrown.expect(IllegalArgumentException.class); 597 try { 598 node1.setClip(node1); 599 } catch (final IllegalArgumentException e) { 600 assertNull("node1.clip is null", node1.getClip()); 601 assertNull("node1.clipParent is null", NodeShim.getClipParent(node1)); 602 throw e; 603 } 604 } 605 606 @Test public void testCircularSelfG() { 607 Group node1 = new Group(); 608 609 ObservableList<Node> content = ParentShim.getChildren(node1); 610 try { 611 content.add(node1); 612 fail("IllegalArgument should have been thrown."); 613 } catch (IllegalArgumentException iae) { 614 // expected 615 } 616 617 assertTrue("node1 is not a child of itself", notChild(node1, node1)); 618 assertNull("node1.parent is null", node1.getParent()); 619 } 620 621 ////////////////////////// 622 // Bound Variable Tests // 623 ////////////////////////// 624 625 // Test various cases where a structure variable (Node.clip, 626 // Group.content, Scene.content) is initialized to a bind-expression. 627 // If the trigger attempts to roll back a change to a variable 628 // initialized this way, the attempt will fail and will throw an 629 // exception. This will leave the invariant violation in place! 630 // We can't do anything about this without language support, so 631 // don't test these cases for now. 632 633 // FAILS: 634 // @Test public void testBindClip() { 635 // var c:Node = null; 636 // var p1 = StubNode { clip: bind c id: "p1" }; 637 // var p2 = StubNode { clip: bind c id: "p2" }; 638 // c = StubNode { id: "c" }; 639 // 640 // println("testBindClip"); 641 // println("p1 = {p1}"); 642 // println("p2 = {p2}"); 643 // println("c = {c}"); 644 // println("p1.clip = {p1.clip}"); 645 // println("p2.clip = {p2.clip}"); 646 // println("c.clipParent = {c.getClipParent()}"); 647 // } 648 649 //////////////////// 650 // Helper Classes // 651 //////////////////// 652 653 // 654 // * A stub node that contains as little functionality as possible. 655 // * 656 public static final class StubNode extends Node { 657 static { 658 StubNodeHelper.setStubNodeAccessor(new StubNodeHelper.StubNodeAccessor() { 659 @Override 660 public NGNode doCreatePeer(Node node) { 661 return ((StubNode) node).doCreatePeer(); 662 } 663 @Override 664 public BaseBounds doComputeGeomBounds(Node node, 665 BaseBounds bounds, BaseTransform tx) { 666 return ((StubNode) node).doComputeGeomBounds(bounds, tx); 667 } 668 @Override 669 public boolean doComputeContains(Node node, double localX, double localY) { 670 return ((StubNode) node).doComputeContains(localX, localY); 671 } 672 }); 673 } 674 675 { 676 // To initialize the class helper at the begining each constructor of this class 677 StubNodeHelper.initHelper(this); 678 } 679 public StubNode() { 680 super(); 681 } 682 683 // * Returning null causes crashes so return a NGGroup. 684 private NGNode doCreatePeer() { 685 return new NGGroup(); 686 } 687 688 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) { 689 return bounds; 690 } 691 692 /* 693 * Note: This method MUST only be called via its accessor method. 694 */ 695 private boolean doComputeContains(double localX, double localY) { 696 // TODO: Missing code. 697 return false; 698 } 699 } 700 701 public static final class StubParent extends Parent { 702 static { 703 StubParentHelper.setStubParentAccessor(new StubParentHelper.StubParentAccessor() { 704 @Override 705 public NGNode doCreatePeer(Node node) { 706 return ((StubParent) node).doCreatePeer(); 707 } 708 709 @Override 710 public BaseBounds doComputeGeomBounds(Node node, 711 BaseBounds bounds, BaseTransform tx) { 712 return ((StubParent) node).doComputeGeomBounds(bounds, tx); 713 } 714 715 @Override 716 public boolean doComputeContains(Node node, double localX, double localY) { 717 return ((StubParent) node).doComputeContains(localX, localY); 718 } 719 }); 720 } 721 722 { 723 // To initialize the class helper at the begining each constructor of this class 724 StubParentHelper.initHelper(this); 725 } 726 public StubParent() { 727 super(); 728 } 729 730 // * Returning null causes crashes so return a PGGroup. 731 private NGNode doCreatePeer() { 732 return new NGGroup(); 733 } 734 735 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) { 736 return bounds; 737 } 738 739 /* 740 * Note: This method MUST only be called via its accessor method. 741 */ 742 private boolean doComputeContains(double localX, double localY) { 743 // TODO: Missing code. 744 return false; 745 } 746 } 747 748 }