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