1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.oracle.javafx.scenebuilder.kit.fxom; 33 34 import com.oracle.javafx.scenebuilder.kit.fxom.glue.GlueElement; 35 import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue; 36 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName; 37 import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage; 38 import com.oracle.javafx.scenebuilder.kit.util.URLUtils; 39 import java.util.ArrayList; 40 import java.util.HashSet; 41 import java.util.LinkedHashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import javafx.scene.Node; 46 import javafx.scene.Scene; 47 48 /** 49 * 50 * 51 */ 52 public abstract class FXOMObject extends FXOMNode { 53 54 private final GlueElement glueElement; 55 private FXOMPropertyC parentProperty; 56 private FXOMCollection parentCollection; 57 private Object sceneGraphObject; 58 59 FXOMObject(FXOMDocument fxomDocument, GlueElement glueElement, Object sceneGraphObject) { 60 super(fxomDocument); 61 62 assert glueElement != null; 63 assert glueElement.getDocument() == fxomDocument.getGlue(); 64 65 this.glueElement = glueElement; 66 this.sceneGraphObject = sceneGraphObject; 67 } 68 69 FXOMObject(FXOMDocument fxomDocument, String tagName) { 70 super(fxomDocument); 71 this.glueElement = new GlueElement(fxomDocument.getGlue(), tagName); 72 } 73 74 public GlueElement getGlueElement() { 75 return glueElement; 76 } 77 78 public FXOMPropertyC getParentProperty() { 79 return parentProperty; 80 } 81 82 public FXOMCollection getParentCollection() { 83 return parentCollection; 84 } 85 86 public void addToParentProperty(int index, FXOMPropertyC newParentProperty) { 87 88 assert newParentProperty != null; 89 assert -1 <= index; 90 assert index <= newParentProperty.getValues().size(); 91 92 if (parentProperty != null) { 93 if (parentProperty.getValues().size() == 1) { 94 // it's the last value -> we remove the whole property 95 if (parentProperty.getParentInstance() != null) { 96 parentProperty.removeFromParentInstance(); 97 } 98 } else { 99 removeFromParentProperty(); 100 } 101 } else if (parentCollection != null) { 102 removeFromParentCollection(); 103 } 104 105 parentProperty = newParentProperty; 106 newParentProperty.addValue(index, this); 107 108 final GlueElement newParentElement = parentProperty.getGlueElement(); 109 glueElement.addToParent(index, newParentElement); 110 111 // May be this object was a root : properties like fx:controller must 112 // be reset to preserve FXML validity. 113 resetRootProperties(); 114 } 115 116 public void removeFromParentProperty() { 117 assert parentProperty != null; 118 assert parentProperty.getParentInstance() == null 119 || parentProperty.getValues().size() >= 2; 120 121 assert glueElement.getParent() == parentProperty.getGlueElement(); 122 glueElement.removeFromParent(); 123 124 final FXOMPropertyC keepParentProperty = parentProperty; 125 parentProperty = null; 126 keepParentProperty.removeValue(this); 127 } 128 129 public int getIndexInParentProperty() { 130 final int result; 131 132 if (parentProperty == null) { 133 result = -1; 134 } else { 135 result = parentProperty.getValues().indexOf(this); 136 assert result != -1; 137 } 138 139 return result; 140 } 141 142 143 public void addToParentCollection(int index, FXOMCollection newParentCollection) { 144 145 assert newParentCollection != null; 146 assert -1 <= index; 147 assert index <= newParentCollection.getItems().size(); 148 149 if (parentProperty != null) { 150 removeFromParentProperty(); 151 } else if (parentCollection != null) { 152 removeFromParentCollection(); 153 } 154 155 parentCollection = newParentCollection; 156 newParentCollection.addValue(index, this); 157 158 final GlueElement newParentElement = parentCollection.getGlueElement(); 159 glueElement.addToParent(index, newParentElement); 160 161 // May be this object was a root : properties like fx:controller must 162 // be reset to preserve FXML validity. 163 resetRootProperties(); 164 } 165 166 public void removeFromParentCollection() { 167 assert parentCollection != null; 168 169 assert glueElement.getParent() == parentCollection.getGlueElement(); 170 glueElement.removeFromParent(); 171 172 final FXOMCollection keepParentCollection = parentCollection; 173 parentCollection = null; 174 keepParentCollection.removeValue(this); 175 } 176 177 public int getIndexInParentCollection() { 178 final int result; 179 180 if (parentCollection == null) { 181 result = -1; 182 } else { 183 result = parentCollection.getItems().indexOf(this); 184 assert result != -1; 185 } 186 187 return result; 188 } 189 190 public Object getSceneGraphObject() { 191 return sceneGraphObject; 192 } 193 194 public void setSceneGraphObject(Object sceneGraphObject) { 195 this.sceneGraphObject = sceneGraphObject; 196 } 197 198 public FXOMObject getNextSlibing() { 199 final FXOMObject result; 200 201 if (parentProperty != null) { 202 final int index = getIndexInParentProperty(); 203 assert index != -1; 204 if (index+1 < parentProperty.getValues().size()) { 205 result = parentProperty.getValues().get(index+1); 206 } else { 207 result = null; 208 } 209 } else if (parentCollection != null) { 210 final int index = getIndexInParentCollection(); 211 assert index != -1; 212 if (index+1 < parentCollection.getItems().size()) { 213 result = parentCollection.getItems().get(index+1); 214 } else { 215 result = null; 216 } 217 } else { 218 result = null; 219 } 220 221 return result; 222 } 223 224 public FXOMObject getPreviousSlibing() { 225 final FXOMObject result; 226 227 if (parentProperty != null) { 228 final int index = getIndexInParentProperty(); 229 assert index != -1; 230 if (index-1 >= 0) { 231 result = parentProperty.getValues().get(index-1); 232 } else { 233 result = null; 234 } 235 } else if (parentCollection != null) { 236 final int index = getIndexInParentCollection(); 237 assert index != -1; 238 if (index-1 >= 0) { 239 result = parentCollection.getItems().get(index-1); 240 } else { 241 result = null; 242 } 243 } else { 244 result = null; 245 } 246 247 return result; 248 } 249 250 public void moveBeforeSibling(FXOMObject sibling) { 251 assert sibling != this; 252 assert (parentProperty != null) || (parentCollection != null); 253 254 if (parentProperty != null) { 255 assert (sibling == null) || (sibling.getParentProperty() == parentProperty); 256 257 final FXOMPropertyC oldParentProperty = parentProperty; 258 removeFromParentProperty(); 259 assert parentProperty == null; 260 261 final int index; 262 if (sibling == null) { 263 index = -1; 264 } else { 265 index = sibling.getIndexInParentProperty(); 266 } 267 addToParentProperty(index, oldParentProperty); 268 269 } else if (parentCollection != null) { 270 assert (sibling == null) || (sibling.getParentCollection() == parentCollection); 271 272 final FXOMCollection oldParentCollection = parentCollection; 273 removeFromParentCollection(); 274 assert parentCollection == null; 275 276 final int index; 277 if (sibling == null) { 278 index = -1; 279 } else { 280 index = sibling.getIndexInParentCollection(); 281 } 282 addToParentCollection(index, oldParentCollection); 283 } else { 284 assert false; 285 } 286 } 287 288 public Scene getScene() { 289 final Scene result; 290 291 if (sceneGraphObject instanceof Node) { 292 final Node sceneGraphNode = (Node) sceneGraphObject; 293 result = sceneGraphNode.getScene(); 294 } else { 295 result = null; 296 } 297 298 return result; 299 } 300 301 public FXOMObject getFirstAncestorWithNonNullScene() { 302 FXOMObject result = this; 303 304 while ((result != null) && (result.getScene() == null)) { 305 result = result.getParentObject(); 306 } 307 308 return result; 309 } 310 311 /* 312 * Utilities 313 */ 314 315 public FXOMObject searchWithSceneGraphObject(Object sceneGraphObject) { 316 final FXOMObject result; 317 318 if (this.sceneGraphObject == sceneGraphObject) { 319 result = this; 320 } else { 321 result = null; 322 } 323 324 return result; 325 } 326 327 public FXOMObject searchWithFxId(String fxId) { 328 final FXOMObject result; 329 330 assert fxId != null; 331 332 if (fxId.equals(getFxId())) { 333 result = this; 334 } else { 335 result = null; 336 } 337 338 return result; 339 } 340 341 public Set<Class<?>> collectDeclaredClasses() { 342 final Set<Class<?>> result = new HashSet<>(); 343 344 collectDeclaredClasses(result); 345 346 return result; 347 } 348 349 protected abstract void collectDeclaredClasses(Set<Class<?>> result); 350 351 352 public List<FXOMProperty> collectProperties(PropertyName propertyName) { 353 final List<FXOMProperty> result = new ArrayList<>(); 354 355 collectProperties(propertyName, result); 356 357 return result; 358 } 359 360 protected abstract void collectProperties(PropertyName propertyName, List<FXOMProperty> result); 361 362 363 public List<FXOMPropertyT> collectNullProperties() { 364 final List<FXOMPropertyT> result = new ArrayList<>(); 365 366 collectNullProperties(result); 367 368 return result; 369 } 370 371 protected abstract void collectNullProperties(List<FXOMPropertyT> result); 372 373 374 public List<FXOMPropertyT> collectPropertiesT() { 375 final List<FXOMPropertyT> result = new ArrayList<>(); 376 377 collectPropertiesT(result); 378 379 return result; 380 } 381 382 protected abstract void collectPropertiesT(List<FXOMPropertyT> result); 383 384 385 public List<FXOMIntrinsic> collectReferences(String source) { 386 final List<FXOMIntrinsic> result = new ArrayList<>(); 387 388 collectReferences(source, result); 389 390 return result; 391 } 392 393 protected abstract void collectReferences(String source, List<FXOMIntrinsic> result); 394 395 public List<FXOMNode> collectReferences(String source, FXOMObject scope) { 396 assert source != null; 397 398 final List<FXOMNode> result = new ArrayList<>(); 399 400 collectReferences(source, scope, result); 401 402 return result; 403 } 404 405 protected abstract void collectReferences(String source, FXOMObject scope, List<FXOMNode> result); 406 407 public List<FXOMIntrinsic> collectIncludes(String source) { 408 final List<FXOMIntrinsic> result = new ArrayList<>(); 409 410 collectIncludes(source, result); 411 412 return result; 413 } 414 415 protected abstract void collectIncludes(String source, List<FXOMIntrinsic> result); 416 417 public Map<String, FXOMObject> collectFxIds() { 418 final Map<String, FXOMObject> result = new LinkedHashMap<>(); 419 420 collectFxIds(result); 421 422 return result; 423 } 424 425 protected abstract void collectFxIds(Map<String, FXOMObject> result); 426 427 public List<FXOMObject> collectObjectWithSceneGraphObjectClass(Class<?> sceneGraphObjectClass) { 428 final List<FXOMObject> result = new ArrayList<>(); 429 430 collectObjectWithSceneGraphObjectClass(sceneGraphObjectClass, result); 431 432 return result; 433 } 434 435 protected abstract void collectObjectWithSceneGraphObjectClass(Class<?> sceneGraphObjectClass, List<FXOMObject> result); 436 437 public List<FXOMPropertyT> collectEventHandlers() { 438 final List<FXOMPropertyT> result = new ArrayList<>(); 439 440 collectEventHandlers(result); 441 442 return result; 443 } 444 445 protected abstract void collectEventHandlers(List<FXOMPropertyT> result); 446 447 /* 448 * Utilities 449 */ 450 451 public FXOMObject getParentObject() { 452 final FXOMObject result; 453 if (parentProperty != null) { 454 assert parentCollection == null; 455 result = parentProperty.getParentInstance(); 456 } else if (parentCollection != null) { 457 result = parentCollection; 458 } else { 459 result = null; 460 } 461 return result; 462 } 463 464 public void removeFromParentObject() { 465 if (parentProperty != null) { 466 if (parentProperty.getValues().size() == 1) { 467 // This object is the last value of its parent property 468 // We remove the property from the parent instance 469 if (parentProperty.getParentInstance() != null) { 470 parentProperty.removeFromParentInstance(); 471 } 472 } else { 473 removeFromParentProperty(); 474 } 475 } else if (parentCollection != null) { 476 removeFromParentCollection(); 477 } 478 } 479 480 public abstract List<FXOMObject> getChildObjects(); 481 482 public boolean isDescendantOf(FXOMObject other) { 483 final boolean result; 484 485 if (other == null) { 486 result = true; 487 } else { 488 FXOMObject ancestor = getParentObject(); 489 while ((ancestor != other) && (ancestor != null)) { 490 ancestor = ancestor.getParentObject(); 491 } 492 result = (ancestor != null); 493 } 494 495 return result; 496 } 497 498 public boolean isNode() { 499 return sceneGraphObject instanceof Node; 500 } 501 502 public FXOMObject getClosestNode() { 503 FXOMObject result; 504 505 result = this; 506 while ((result.isNode() == false) && (result.getParentObject() != null)) { 507 result = result.getParentObject(); 508 } 509 510 return result.isNode() ? result : null; 511 } 512 513 public String getFxId() { 514 return glueElement.getAttributes().get("fx:id"); 515 } 516 517 518 public void setFxId(String fxId) { 519 assert (fxId == null) || JavaLanguage.isIdentifier(fxId); 520 if (fxId == null) { 521 glueElement.getAttributes().remove("fx:id"); 522 } else { 523 glueElement.getAttributes().put("fx:id", fxId); 524 } 525 } 526 527 528 public String getFxValue() { 529 return glueElement.getAttributes().get("fx:value"); 530 } 531 532 public void setFxValue(String fxValue) { 533 if (fxValue == null) { 534 glueElement.getAttributes().remove("fx:value"); 535 } else { 536 glueElement.getAttributes().put("fx:value", fxValue); 537 } 538 } 539 540 541 public String getFxConstant() { 542 return glueElement.getAttributes().get("fx:constant"); 543 } 544 545 public void setFxConstant(String fxConstant) { 546 if (fxConstant == null) { 547 glueElement.getAttributes().remove("fx:constant"); 548 } else { 549 glueElement.getAttributes().put("fx:constant", fxConstant); 550 } 551 } 552 553 public String getFxController() { 554 return glueElement.getAttributes().get("fx:controller"); 555 } 556 557 public void setFxController(String fxController) { 558 if (fxController == null) { 559 glueElement.getAttributes().remove("fx:controller"); 560 } else { 561 glueElement.getAttributes().put("fx:controller", fxController); 562 } 563 } 564 565 public String getFxFactory() { 566 return glueElement.getAttributes().get("fx:factory"); 567 } 568 569 public void setFxFactory(String fxFactory) { 570 if (fxFactory == null) { 571 glueElement.getAttributes().remove("fx:factory"); 572 } else { 573 glueElement.getAttributes().put("fx:factory", fxFactory); 574 } 575 } 576 577 public String getNameSpaceFX() { 578 return glueElement.getAttributes().get("xmlns"); 579 } 580 581 public void setNameSpaceFX(String nameSpace) { 582 if (nameSpace == null) { 583 glueElement.getAttributes().remove("xmlns"); 584 } else { 585 glueElement.getAttributes().put("xmlns", nameSpace); 586 } 587 } 588 589 public String getNameSpaceFXML() { 590 return glueElement.getAttributes().get("xmlns:fx"); 591 } 592 593 public void setNameSpaceFXML(String nameSpace) { 594 if (nameSpace == null) { 595 glueElement.getAttributes().remove("xmlns:fx"); 596 } else { 597 glueElement.getAttributes().put("xmlns:fx", nameSpace); 598 } 599 } 600 601 /* 602 * FXOMNode 603 */ 604 605 @Override 606 public void moveToFxomDocument(FXOMDocument destination) { 607 assert destination != null; 608 assert destination != getFxomDocument(); 609 assert (parentProperty == null) 610 || (parentProperty.getParentInstance() == null) 611 || (parentProperty.getValues().size() >= 2); 612 613 if (URLUtils.equals(getFxomDocument().getLocation(), destination.getLocation()) == false) { 614 documentLocationWillChange(destination.getLocation()); 615 } 616 617 final Map<String, FXOMObject> destinationFxIds = destination.collectFxIds(); 618 final Map<String, FXOMObject> importedFxIds = collectFxIds(); 619 final FXOMFxIdMerger merger = new FXOMFxIdMerger(destinationFxIds.keySet(), importedFxIds.keySet()); 620 for (Map.Entry<String, FXOMObject> e : importedFxIds.entrySet()) { 621 final String originalFxId = e.getKey(); 622 final FXOMObject fxomObject = e.getValue(); 623 assert originalFxId.equals(fxomObject.getFxId()); 624 final String renamedFxId = merger.getRenamedFxId(originalFxId); 625 assert renamedFxId != null; 626 627 if (renamedFxId.equals(originalFxId) == false) { 628 /* 629 * Apply the renaming: 630 * 1) the declaration 631 * <Button fx:id="toto" .../> 632 * 2) expressions that reference the fx:id 633 * ... labelFor="$toto" ... 634 * 3) intrinsics that reference the fx:id 635 * <fx:reference source="toto"/> 636 */ 637 638 // #1 639 fxomObject.setFxId(renamedFxId); 640 // #2 641 final PrefixedValue pv = new PrefixedValue(PrefixedValue.Type.EXPRESSION, renamedFxId); 642 final String newValue = pv.toString(); 643 for (FXOMPropertyT p : FXOMNodes.collectReferenceExpression(this, originalFxId)) { 644 p.setValue(newValue); 645 } 646 // #3 647 for (FXOMObject o : FXOMNodes.serializeObjects(this)) { 648 if (o instanceof FXOMIntrinsic) { 649 final FXOMIntrinsic i = (FXOMIntrinsic) o; 650 switch(i.getType()) { 651 case FX_REFERENCE: 652 case FX_COPY: 653 if (i.getSource().equals(originalFxId)) { 654 i.setSource(renamedFxId); 655 } 656 break; 657 default: 658 // "source" does not contain an fx:id 659 break; 660 } 661 } 662 } 663 } 664 } 665 666 if (parentProperty != null) { 667 assert parentProperty.getFxomDocument() == getFxomDocument(); 668 removeFromParentProperty(); 669 } else if (parentCollection != null) { 670 assert parentCollection.getFxomDocument() == getFxomDocument(); 671 removeFromParentCollection(); 672 } else if (getFxomDocument().getFxomRoot() == this) { 673 getFxomDocument().setFxomRoot(null); 674 } 675 676 assert parentProperty == null; 677 assert parentCollection == null; 678 assert glueElement.getParent() == null; 679 680 glueElement.moveToDocument(destination.getGlue()); 681 changeFxomDocument(destination); 682 } 683 684 @Override 685 protected void changeFxomDocument(FXOMDocument destination) { 686 assert destination != null; 687 assert destination != getFxomDocument(); 688 assert destination.getGlue() == glueElement.getDocument(); 689 assert (parentProperty == null) || (destination == parentProperty.getFxomDocument()); 690 assert (parentCollection == null) || (destination == parentCollection.getFxomDocument()); 691 692 super.changeFxomDocument(destination); 693 } 694 695 696 /* 697 * Object 698 */ 699 700 @Override 701 public String toString() { 702 final StringBuilder result = new StringBuilder(); 703 704 result.append(getClass().getSimpleName()); 705 result.append("[tagName="); 706 result.append(glueElement.getTagName()); 707 if (getFxId() != null) { 708 result.append(",fx:id="); 709 result.append(getFxId()); 710 } 711 result.append(']'); 712 713 return result.toString(); 714 } 715 716 /* 717 * Package 718 */ 719 720 /* For FXOMPropertyC constructor private use */ 721 void setParentProperty(FXOMPropertyC newParentProperty) { 722 assert parentProperty == null; 723 assert parentCollection == null; 724 assert newParentProperty.getValues().contains(this); 725 parentProperty = newParentProperty; 726 } 727 728 /* For FXOMCollection constructor private use */ 729 void setParentCollection(FXOMCollection newParentCollection) { 730 assert parentProperty == null; 731 assert parentCollection == null; 732 assert newParentCollection.getItems().contains(this); 733 parentCollection = newParentCollection; 734 } 735 736 737 738 /* 739 * Private 740 */ 741 742 private void resetRootProperties() { 743 setFxController(null); 744 setNameSpaceFX(null); 745 setNameSpaceFXML(null); 746 } 747 }