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.GlueDocument; 35 import com.oracle.javafx.scenebuilder.kit.fxom.glue.GlueInstruction; 36 import com.oracle.javafx.scenebuilder.kit.metadata.Metadata; 37 import com.oracle.javafx.scenebuilder.kit.metadata.klass.ComponentClassMetadata; 38 import com.oracle.javafx.scenebuilder.kit.metadata.property.PropertyMetadata; 39 import com.oracle.javafx.scenebuilder.kit.metadata.property.value.DoublePropertyMetadata; 40 import com.oracle.javafx.scenebuilder.kit.metadata.property.value.ImagePropertyMetadata; 41 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignImage; 42 import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue; 43 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName; 44 import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage; 45 import java.io.File; 46 import java.io.IOException; 47 import java.net.URL; 48 import java.util.ArrayList; 49 import java.util.Collections; 50 import java.util.HashSet; 51 import java.util.List; 52 import java.util.Map; 53 import java.util.Set; 54 import javafx.fxml.FXMLLoader; 55 import javafx.scene.control.ToggleGroup; 56 import javafx.scene.image.Image; 57 import javafx.scene.image.ImageView; 58 import javafx.scene.media.Media; 59 import javafx.scene.media.MediaException; 60 import javafx.scene.media.MediaPlayer; 61 import javafx.scene.media.MediaView; 62 63 /** 64 * This class groups static utility methods which operate on FXOMNode and 65 * subclasses (a bit like Collection and Collections). 66 * 67 * 68 */ 69 public class FXOMNodes { 70 71 72 /** 73 * Sorts the specified set of objects according their location in 74 * the fxom document. Objets are sorted according depth first order. 75 * In particular, if objects all have the same parent, then the resulting 76 * list will be sorted by indexes. 77 * 78 * @param objects a set of fxom objects (never null but possibly empty) 79 * @return the list of objets sorted by position in the fxom document 80 */ 81 public static List<FXOMObject> sort(Set<FXOMObject> objects) { 82 final List<FXOMObject> result; 83 84 assert objects != null; 85 86 if (objects.isEmpty()) { 87 result = Collections.emptyList(); 88 } else if (objects.size() == 1) { 89 result = Collections.singletonList(objects.iterator().next()); 90 } else { 91 final FXOMObject object0 = objects.iterator().next(); 92 final FXOMDocument fxomDocument = object0.getFxomDocument(); 93 assert fxomDocument != null; 94 result = new ArrayList<>(); 95 sort(fxomDocument.getFxomRoot(), objects, result); 96 } 97 98 return result; 99 } 100 101 102 /** 103 * Flattens a set of fxom objects. 104 * A set of fxom objects is declared "flat" if each object member 105 * of the set has no ancestor member of the set. 106 * 107 * @param objects a set of fxom objects (never null) 108 * @return the flat set of objects. 109 */ 110 public static Set<FXOMObject> flatten(Set<FXOMObject> objects) { 111 final Set<FXOMObject> result = new HashSet<>(); 112 113 assert objects != null; 114 115 for (FXOMObject o : objects) { 116 if (lookupAncestor(o, objects) == null) { 117 result.add(o); 118 } 119 } 120 121 return result; 122 } 123 124 125 /** 126 * Returns null or the first ancestor of "obj" which belongs to "candidates". 127 * @param obj an fxom object (never null) 128 * @param candidates a set of fxom object (not null and not empty) 129 * @return null or the first ancestor of "obj" which belongs to "candidates". 130 */ 131 public static FXOMObject lookupAncestor(FXOMObject obj, Set<FXOMObject> candidates) { 132 assert obj != null; 133 assert candidates != null; 134 assert candidates.isEmpty() == false; 135 136 FXOMObject result = obj.getParentObject(); 137 while ((result != null) && (candidates.contains(result) == false)) { 138 result = result.getParentObject(); 139 } 140 141 return result; 142 } 143 144 145 146 public static FXOMObject newObject(FXOMDocument targetDocument, File file) 147 throws IOException { 148 assert targetDocument != null; 149 assert file != null; 150 FXOMObject result = null; 151 if (file.getAbsolutePath().endsWith(".fxml")) { //NOI18N 152 final String fxmlText 153 = FXOMDocument.readContentFromURL(file.toURI().toURL()); 154 final FXOMDocument transientDoc = new FXOMDocument( 155 fxmlText, 156 targetDocument.getLocation(), 157 targetDocument.getClassLoader(), 158 targetDocument.getResources()); 159 result = transientDoc.getFxomRoot(); 160 if (result != null) { 161 result.moveToFxomDocument(targetDocument); 162 } 163 } else { 164 // Try load the file as an image 165 final String fileURL = file.toURI().toURL().toString(); 166 final Image image = new Image(fileURL); 167 if (image.isError() == false) { 168 final FXOMDocument transientDoc 169 = makeFxomDocumentFromImageURL(image, 200.0); 170 result = transientDoc.getFxomRoot(); 171 if (result != null) { 172 result.moveToFxomDocument(targetDocument); 173 } 174 } else { 175 try { 176 final Media media = new Media(fileURL); 177 if (media.getError() == null) { 178 final FXOMDocument transientDoc 179 = makeFxomDocumentFromMedia(media, 200.0); 180 result = transientDoc.getFxomRoot(); 181 if (result != null) { 182 result.moveToFxomDocument(targetDocument); 183 } 184 } else { 185 throw new IOException(media.getError()); 186 } 187 } catch(MediaException x) { 188 throw new IOException(x); 189 } 190 } 191 } 192 193 return result; 194 } 195 196 public static FXOMIntrinsic newInclude(FXOMDocument targetDocument, File file) 197 throws IOException { 198 assert targetDocument != null; 199 assert targetDocument.getLocation() != null; 200 assert file != null; 201 FXOMIntrinsic result = null; 202 if (file.getAbsolutePath().endsWith(".fxml")) { //NOI18N 203 final URL fxmlURL = file.toURI().toURL(); 204 final String fxmlText = FXOMDocument.readContentFromURL(fxmlURL); 205 final FXOMDocument transientDoc = new FXOMDocument( 206 fxmlText, 207 fxmlURL, 208 targetDocument.getClassLoader(), 209 targetDocument.getResources()); 210 if (transientDoc.getFxomRoot() != null) { 211 final PrefixedValue pv 212 = PrefixedValue.makePrefixedValue(fxmlURL, targetDocument.getLocation()); 213 assert pv.isDocumentRelativePath(); 214 assert pv.toString().startsWith(FXMLLoader.RELATIVE_PATH_PREFIX); 215 final String includeRef 216 = pv.toString().substring(FXMLLoader.RELATIVE_PATH_PREFIX.length()); 217 result = new FXOMIntrinsic(targetDocument, FXOMIntrinsic.Type.FX_INCLUDE, includeRef); 218 result.setSourceSceneGraphObject(transientDoc.getFxomRoot().getSceneGraphObject()); 219 } 220 } 221 222 return result; 223 } 224 225 public static FXOMDocument newDocument(FXOMObject source) { 226 assert source != null; 227 228 final FXOMDocument result = new FXOMDocument(); 229 230 /* 231 * If source's document contains unresolved objects, 232 * then clones import instructions from the source document 233 * to the new document. 234 */ 235 final FXOMDocument sourceDocument 236 = source.getFxomDocument(); 237 assert sourceDocument.getFxomRoot() != null; // contains at least source 238 final List<FXOMObject> unresolvedObjects 239 = collectUnresolvedObjects(sourceDocument.getFxomRoot()); 240 if (unresolvedObjects.isEmpty() == false) { 241 // Copy all the imports from the source document to the new document 242 final GlueDocument sourceGlue = sourceDocument.getGlue(); 243 final GlueDocument resultGlue = result.getGlue(); 244 for (GlueInstruction i : sourceGlue.collectInstructions("import")) { 245 final GlueInstruction ci = new GlueInstruction(resultGlue, i.getTarget(), i.getData()); 246 resultGlue.getHeader().add(ci); 247 } 248 } 249 250 /* 251 * Clones source to the new document 252 */ 253 final FXOMCloner cloner = new FXOMCloner(result); 254 final FXOMObject sourceClone = cloner.clone(source); 255 256 /* 257 * Setup new document : sourceClone is the root, 258 * same location, same class loader. 259 */ 260 result.beginUpdate(); 261 result.setLocation(sourceDocument.getLocation()); 262 result.setClassLoader(sourceDocument.getClassLoader()); 263 result.setFxomRoot(sourceClone); 264 if (result.getFxomRoot() instanceof FXOMInstance) { 265 trimStaticProperties((FXOMInstance) result.getFxomRoot()); 266 } 267 result.endUpdate(); 268 269 return result; 270 } 271 272 273 public static void updateProperty(FXOMInstance fxomInstance, FXOMProperty sourceProperty) { 274 assert fxomInstance != null; 275 assert sourceProperty != null; 276 assert sourceProperty.getFxomDocument() == fxomInstance.getFxomDocument(); 277 278 final FXOMProperty currentProperty = fxomInstance.getProperties().get(sourceProperty.getName()); 279 if (currentProperty == null) { 280 sourceProperty.addToParentInstance(-1, fxomInstance); 281 } else if ((currentProperty instanceof FXOMPropertyT) 282 && (sourceProperty instanceof FXOMPropertyT)) { 283 final FXOMPropertyT currentPropertyT = (FXOMPropertyT) currentProperty; 284 final FXOMPropertyT newPropertyT = (FXOMPropertyT) sourceProperty; 285 updateProperty(currentPropertyT, newPropertyT); 286 } else if ((currentProperty instanceof FXOMPropertyC) 287 && (sourceProperty instanceof FXOMPropertyC)) { 288 final FXOMPropertyC currentPropertyC = (FXOMPropertyC) currentProperty; 289 final FXOMPropertyC newPropertyC = (FXOMPropertyC) sourceProperty; 290 updateProperty(currentPropertyC, newPropertyC); 291 } else { 292 final int index = currentProperty.getIndexInParentInstance(); 293 currentProperty.removeFromParentInstance(); 294 sourceProperty.addToParentInstance(index, fxomInstance); 295 } 296 } 297 298 299 public static void updateProperty(FXOMPropertyT fxomProperty, FXOMPropertyT sourceProperty) { 300 assert fxomProperty != null; 301 assert sourceProperty != null; 302 assert fxomProperty.getName().equals(sourceProperty.getName()); 303 fxomProperty.setValue(sourceProperty.getValue()); 304 } 305 306 307 public static void updateProperty(FXOMPropertyC fxomProperty, FXOMPropertyC sourceProperty) { 308 assert fxomProperty != null; 309 assert sourceProperty != null; 310 assert fxomProperty.getName().equals(sourceProperty.getName()); 311 312 final List<FXOMObject> currentValues = new ArrayList<>(); 313 currentValues.addAll(fxomProperty.getValues()); 314 final List<FXOMObject> sourceValues = new ArrayList<>(); 315 sourceValues.addAll(sourceProperty.getValues()); 316 317 final int currentCount = currentValues.size(); 318 final int newCount = sourceValues.size(); 319 final int updateCount = Math.min(currentCount, newCount); 320 321 // Update items 322 for (int i = 0; i < updateCount; i++) { 323 final FXOMObject currentValue = currentValues.get(i); 324 final FXOMObject newValue = sourceValues.get(i); 325 if ((currentValue instanceof FXOMInstance) && 326 (newValue instanceof FXOMInstance)) { 327 final FXOMInstance currentInstance = (FXOMInstance) currentValue; 328 final FXOMInstance newInstance = (FXOMInstance) newValue; 329 if (currentInstance.getDeclaredClass() == newInstance.getDeclaredClass()) { 330 updateInstance(currentInstance, newInstance); 331 } else { 332 replacePropertyValue(currentValue, newValue); 333 } 334 } else if ((currentValue instanceof FXOMCollection) && 335 (newValue instanceof FXOMCollection)) { 336 final FXOMCollection currentCollection = (FXOMCollection) currentValue; 337 final FXOMCollection newCollection = (FXOMCollection) newValue; 338 updateCollection(currentCollection, newCollection); 339 } else if ((currentValue instanceof FXOMIntrinsic) && 340 (newValue instanceof FXOMIntrinsic)) { 341 final FXOMIntrinsic currentIntrinsic = (FXOMIntrinsic) currentValue; 342 final FXOMIntrinsic newIntrinsic = (FXOMIntrinsic) newValue; 343 updateIntrinsic(currentIntrinsic, newIntrinsic); 344 } else { 345 replacePropertyValue(currentValue, newValue); 346 } 347 } 348 349 if (currentCount < newCount) { 350 // Add new items 351 for (int i = currentCount; i < newCount; i++) { 352 final FXOMObject newValue = sourceValues.get(i); 353 newValue.addToParentProperty(-1, fxomProperty); 354 } 355 } else { 356 // Delete old items 357 for (int i = newCount; i < currentCount; i++) { 358 final FXOMObject currentValue = currentValues.get(i); 359 currentValue.removeFromParentProperty(); 360 } 361 } 362 } 363 364 365 public static void updateInstance(FXOMInstance fxomInstance, FXOMInstance sourceInstance) { 366 assert fxomInstance != null; 367 assert sourceInstance != null; 368 assert fxomInstance.getFxomDocument() == sourceInstance.getFxomDocument(); 369 assert fxomInstance.getDeclaredClass() == sourceInstance.getDeclaredClass(); 370 371 // Compute obsolete properties. 372 // It must be done here because sourceInstance is going to mutate. 373 final Set<PropertyName> obsoleteNames = new HashSet<>(); 374 obsoleteNames.addAll(fxomInstance.getProperties().keySet()); 375 obsoleteNames.removeAll(sourceInstance.getProperties().keySet()); 376 377 // Update properties 378 final Set<FXOMProperty> sourceProperties = new HashSet<>(sourceInstance.getProperties().values()); 379 for (FXOMProperty sourceProperty : sourceProperties) { 380 updateProperty(fxomInstance, sourceProperty); 381 } 382 // Remove obsolete properties 383 for (PropertyName pn : obsoleteNames) { 384 final FXOMProperty fxomProperty = fxomInstance.getProperties().get(pn); 385 assert fxomProperty != null; 386 assert fxomProperty.getParentInstance() == fxomInstance; 387 fxomProperty.removeFromParentInstance(); 388 } 389 390 fxomInstance.setFxConstant(sourceInstance.getFxConstant()); 391 fxomInstance.setFxValue(sourceInstance.getFxValue()); 392 fxomInstance.setFxFactory(sourceInstance.getFxFactory()); 393 } 394 395 396 public static void updateCollection(FXOMCollection fxomCollection, FXOMCollection sourceCollection) { 397 assert fxomCollection != null; 398 assert sourceCollection != null; 399 assert fxomCollection.getFxomDocument() == sourceCollection.getFxomDocument(); 400 401 final int currentCount = fxomCollection.getItems().size(); 402 final int sourceCount = sourceCollection.getItems().size(); 403 final int updateCount = Math.min(currentCount, sourceCount); 404 405 // Update items 406 for (int i = 0; i < updateCount; i++) { 407 final FXOMObject currentValue = fxomCollection.getItems().get(i); 408 final FXOMObject newValue = sourceCollection.getItems().get(i); 409 if ((currentValue instanceof FXOMInstance) && 410 (newValue instanceof FXOMInstance)) { 411 final FXOMInstance currentInstance = (FXOMInstance) currentValue; 412 final FXOMInstance newInstance = (FXOMInstance) newValue; 413 updateInstance(currentInstance, newInstance); 414 } else if ((currentValue instanceof FXOMCollection) && 415 (newValue instanceof FXOMCollection)) { 416 final FXOMCollection currentCollection = (FXOMCollection) currentValue; 417 final FXOMCollection newCollection = (FXOMCollection) newValue; 418 updateCollection(currentCollection, newCollection); 419 } else if ((currentValue instanceof FXOMIntrinsic) && 420 (newValue instanceof FXOMIntrinsic)) { 421 final FXOMIntrinsic currentIntrinsic = (FXOMIntrinsic) currentValue; 422 final FXOMIntrinsic newIntrinsic = (FXOMIntrinsic) newValue; 423 updateIntrinsic(currentIntrinsic, newIntrinsic); 424 } else { 425 final int index = currentValue.getIndexInParentProperty(); 426 assert index != -1; 427 currentValue.removeFromParentCollection(); 428 newValue.addToParentCollection(index, fxomCollection); 429 } 430 } 431 432 if (currentCount < sourceCount) { 433 // Add new items 434 final int addCount = sourceCount - currentCount; 435 for (int i = 0; i < addCount; i++) { 436 final FXOMObject newValue = sourceCollection.getItems().get(i); 437 newValue.addToParentCollection(-1, fxomCollection); 438 } 439 } else { 440 // Delete old items 441 final int removeCount = currentCount - sourceCount; 442 for (int i = 0; i < removeCount; i++) { 443 final FXOMObject currentValue = fxomCollection.getItems().get(sourceCount); 444 currentValue.removeFromParentProperty(); 445 } 446 } 447 448 fxomCollection.setFxConstant(sourceCollection.getFxConstant()); 449 fxomCollection.setFxValue(sourceCollection.getFxValue()); 450 fxomCollection.setFxFactory(sourceCollection.getFxFactory()); 451 } 452 453 454 public static void updateIntrinsic(FXOMIntrinsic fxomIntrinsic, FXOMIntrinsic sourceIntrinsic) { 455 assert fxomIntrinsic != null; 456 assert sourceIntrinsic != null; 457 assert fxomIntrinsic.getFxomDocument() != sourceIntrinsic.getFxomDocument(); 458 assert fxomIntrinsic.getType() == sourceIntrinsic.getType(); 459 460 fxomIntrinsic.setSource(sourceIntrinsic.getSource()); 461 fxomIntrinsic.setFxConstant(sourceIntrinsic.getFxConstant()); 462 fxomIntrinsic.setFxValue(sourceIntrinsic.getFxValue()); 463 fxomIntrinsic.setFxFactory(sourceIntrinsic.getFxFactory()); 464 } 465 466 467 public static List<FXOMPropertyT> collectReferenceExpression(FXOMObject fxomRoot, String fxId) { 468 assert fxomRoot != null; 469 assert fxId != null; 470 471 final List<FXOMPropertyT> result = new ArrayList<>(); 472 473 for (FXOMPropertyT p : fxomRoot.collectPropertiesT()) { 474 final PrefixedValue pv = new PrefixedValue(p.getValue()); 475 if (pv.isExpression()) { 476 /* 477 * p is an FXOMPropertyT like this: 478 * 479 * <.... property="$id" .... /> 480 */ 481 final String id = pv.getSuffix(); 482 if (id.equals(fxId)) { 483 result.add(p); 484 } 485 } 486 } 487 488 return result; 489 } 490 491 492 public static List<FXOMObject> collectUnresolvedObjects(FXOMObject fxomObject) { 493 final List<FXOMObject> result = new ArrayList<>(); 494 495 for (FXOMObject o : serializeObjects(fxomObject)) { 496 if (o.getSceneGraphObject() == null) { 497 result.add(o); 498 } 499 } 500 501 return result; 502 } 503 504 505 public static List<FXOMObject> serializeObjects(FXOMObject fxomObject) { 506 final List<FXOMObject> result = new ArrayList<>(); 507 508 serializeObjects(fxomObject, result); 509 assert result.isEmpty() == false; 510 assert result.get(0) == fxomObject; 511 512 return result; 513 } 514 515 public static void removeToggleGroups(Map<String, FXOMObject> fxIdMap) { 516 assert fxIdMap != null; 517 518 for (String fxId : new HashSet<>(fxIdMap.keySet())) { 519 final FXOMObject fxomObject = fxIdMap.get(fxId); 520 if (fxomObject.getSceneGraphObject() instanceof ToggleGroup) { 521 fxIdMap.remove(fxId); 522 } 523 } 524 } 525 526 527 public static String extractReferenceSource(FXOMNode node) { 528 final String result; 529 530 if (node instanceof FXOMIntrinsic) { 531 final FXOMIntrinsic intrinsic = (FXOMIntrinsic) node; 532 switch(intrinsic.getType()) { 533 case FX_REFERENCE: 534 case FX_COPY: 535 result = intrinsic.getSource(); 536 break; 537 default: 538 result = null; 539 } 540 } else if (node instanceof FXOMPropertyT) { 541 final FXOMPropertyT property = (FXOMPropertyT) node; 542 final PrefixedValue pv = new PrefixedValue(property.getValue()); 543 if (pv.isExpression() && JavaLanguage.isIdentifier(pv.getSuffix())) { 544 result = pv.getSuffix(); 545 } else { 546 result = null; 547 } 548 } else { 549 result = null; 550 } 551 552 return result; 553 } 554 555 556 private static final PropertyName toggleGroupName = new PropertyName("toggleGroup"); 557 558 public static boolean isToggleGroupReference(FXOMNode node) { 559 final boolean result; 560 561 if (extractReferenceSource(node) == null) { 562 result = false; 563 } else { 564 if (node instanceof FXOMIntrinsic) { 565 final FXOMIntrinsic intrinsic = (FXOMIntrinsic) node; 566 final FXOMProperty parentProperty = intrinsic.getParentProperty(); 567 if (parentProperty == null) { 568 result = false; 569 } else { 570 result = parentProperty.getName().equals(toggleGroupName); 571 } 572 } else if (node instanceof FXOMPropertyT) { 573 final FXOMPropertyT property = (FXOMPropertyT) node; 574 result = property.getName().equals(toggleGroupName); 575 } else { 576 result = false; 577 } 578 } 579 580 return result; 581 } 582 583 584 public static FXOMPropertyC makeToggleGroup(FXOMDocument fxomDocument, String fxId) { 585 final FXOMInstance toggleGroup = new FXOMInstance(fxomDocument, ToggleGroup.class); 586 toggleGroup.setFxId(fxId); 587 return new FXOMPropertyC(fxomDocument, toggleGroupName, toggleGroup); 588 } 589 590 591 public static boolean isWeakReference(FXOMNode node) { 592 final boolean result; 593 594 if (node instanceof FXOMIntrinsic) { 595 final FXOMIntrinsic intrinsic = (FXOMIntrinsic) node; 596 switch(intrinsic.getType()) { 597 case FX_REFERENCE: 598 case FX_COPY: 599 if (intrinsic.getParentProperty() != null) { 600 final PropertyName propertyName = intrinsic.getParentProperty().getName(); 601 if (propertyName.getResidenceClass() == null) { 602 result = getWeakPropertyNames().contains(propertyName.getName()); 603 } else { 604 result = false; 605 } 606 } else { 607 result = false; 608 } 609 break; 610 default: 611 result = false; 612 } 613 } else if (node instanceof FXOMPropertyT) { 614 final FXOMPropertyT property = (FXOMPropertyT) node; 615 final PrefixedValue pv = new PrefixedValue(property.getValue()); 616 if (pv.isExpression() && JavaLanguage.isIdentifier(pv.getSuffix())) { 617 final PropertyName propertyName = property.getName(); 618 if (propertyName.getResidenceClass() == null) { 619 result = getWeakPropertyNames().contains(propertyName.getName()); 620 } else { 621 result = false; 622 } 623 } else { 624 result = false; 625 } 626 } else { 627 result = false; 628 } 629 630 return result; 631 } 632 633 634 private static Set<String> weakPropertyNames; 635 636 public static synchronized Set<String> getWeakPropertyNames() { 637 638 if (weakPropertyNames == null) { 639 weakPropertyNames = new HashSet<>(); 640 weakPropertyNames.add("labelFor"); 641 weakPropertyNames.add("expandedPane"); 642 weakPropertyNames.add("clip"); 643 } 644 645 return weakPropertyNames; 646 } 647 648 649 /* 650 * Private 651 */ 652 653 private static void sort(FXOMObject from, 654 Set<FXOMObject> objects, List<FXOMObject> result) { 655 656 if (objects.contains(from)) { 657 result.add(from); 658 } 659 660 if (from instanceof FXOMCollection) { 661 final FXOMCollection collection = (FXOMCollection) from; 662 for (FXOMObject item : collection.getItems()) { 663 sort(item, objects, result); 664 } 665 } else if (from instanceof FXOMInstance) { 666 final FXOMInstance instance = (FXOMInstance) from; 667 final List<PropertyName> propertyNames 668 = new ArrayList<>(instance.getProperties().keySet()); 669 Collections.sort(propertyNames); 670 for (PropertyName name : propertyNames) { 671 final FXOMProperty property = instance.getProperties().get(name); 672 assert property != null; 673 if (property instanceof FXOMPropertyC) { 674 final FXOMPropertyC propertyC = (FXOMPropertyC) property; 675 for (FXOMObject v : propertyC.getValues()) { 676 sort(v, objects, result); 677 } 678 } 679 } 680 } else { 681 assert from instanceof FXOMIntrinsic 682 : "Unexpected FXOMObject subclass " + from.getClass(); 683 } 684 } 685 686 687 private static void trimStaticProperties(FXOMInstance fxomInstance) { 688 final List<FXOMProperty> properties = 689 new ArrayList<>(fxomInstance.getProperties().values()); 690 for (FXOMProperty p : properties) { 691 if (p.getName().getResidenceClass() != null) { 692 // This is a static property : we remove it. 693 p.removeFromParentInstance(); 694 } 695 } 696 } 697 698 699 private static void replacePropertyValue(FXOMObject replacee, FXOMObject replacement) { 700 assert replacee.getIndexInParentProperty() != -1; 701 702 final int replaceeIndex = replacee.getIndexInParentProperty(); 703 assert replaceeIndex != -1; 704 replacement.addToParentProperty(replaceeIndex, replacee.getParentProperty()); 705 replacee.removeFromParentProperty(); 706 } 707 708 private static FXOMDocument makeFxomDocumentFromImageURL( 709 Image image, double fitSize) throws IOException { 710 711 assert image != null; 712 assert fitSize > 0.0; 713 714 final double imageWidth = image.getWidth(); 715 final double imageHeight = image.getHeight(); 716 717 final double fitWidth, fitHeight; 718 final double imageSize = Math.max(imageWidth, imageHeight); 719 if (imageSize < fitSize) { 720 fitWidth = 0; 721 fitHeight = 0; 722 } else { 723 final double widthScale = fitSize / imageSize; 724 final double heightScale = fitSize / imageHeight; 725 final double scale = Math.min(widthScale, heightScale); 726 fitWidth = Math.floor(imageWidth * scale); 727 fitHeight = Math.floor(imageHeight * scale); 728 } 729 730 return makeFxomDocumentFromImageURL(image, fitWidth, fitHeight); 731 } 732 733 private static FXOMDocument makeFxomDocumentFromImageURL( 734 Image image, double fitWidth, double fitHeight) { 735 736 final FXOMDocument result = new FXOMDocument(); 737 final FXOMInstance imageView = new FXOMInstance(result, ImageView.class); 738 739 final PropertyName imageName = new PropertyName("image"); //NOI18N 740 final PropertyName fitWidthName = new PropertyName("fitWidth"); //NOI18N 741 final PropertyName fitHeightName = new PropertyName("fitHeight"); //NOI18N 742 743 final ComponentClassMetadata imageViewMeta 744 = Metadata.getMetadata().queryComponentMetadata(ImageView.class); 745 final PropertyMetadata imagePropMeta 746 = imageViewMeta.lookupProperty(imageName); 747 final PropertyMetadata fitWidthPropMeta 748 = imageViewMeta.lookupProperty(fitWidthName); 749 final PropertyMetadata fitHeightPropMeta 750 = imageViewMeta.lookupProperty(fitHeightName); 751 752 assert imagePropMeta instanceof ImagePropertyMetadata; 753 assert fitWidthPropMeta instanceof DoublePropertyMetadata; 754 assert fitHeightPropMeta instanceof DoublePropertyMetadata; 755 756 final ImagePropertyMetadata imageMeta 757 = (ImagePropertyMetadata) imagePropMeta; 758 final DoublePropertyMetadata fitWidthMeta 759 = (DoublePropertyMetadata) fitWidthPropMeta; 760 final DoublePropertyMetadata fitHeightMeta 761 = (DoublePropertyMetadata) fitHeightPropMeta; 762 763 imageMeta.setValue(imageView, new DesignImage(image)); 764 fitWidthMeta.setValue(imageView, fitWidth); 765 fitHeightMeta.setValue(imageView, fitHeight); 766 767 result.setFxomRoot(imageView); 768 769 return result; 770 } 771 772 private static FXOMDocument makeFxomDocumentFromMedia( 773 Media media, double fitSize) throws IOException { 774 775 assert media != null; 776 assert fitSize > 0.0; 777 778 final double mediaWidth = media.getWidth(); 779 final double mediaHeight = media.getHeight(); 780 781 final double fitWidth, fitHeight; 782 final double mediaSize = Math.max(mediaWidth, mediaHeight); 783 if (mediaSize < fitSize) { 784 fitWidth = 0; 785 fitHeight = 0; 786 } else { 787 final double widthScale = fitSize / mediaSize; 788 final double heightScale = fitSize / mediaHeight; 789 final double scale = Math.min(widthScale, heightScale); 790 fitWidth = Math.floor(mediaWidth * scale); 791 fitHeight = Math.floor(mediaHeight * scale); 792 } 793 794 return makeFxomDocumentFromMedia(media, fitWidth, fitHeight); 795 } 796 797 private static FXOMDocument makeFxomDocumentFromMedia( 798 Media media, double fitWidth, double fitHeight) { 799 800 /* 801 * <MediaView fitWidth="200" fitHeight="2003 > 802 * <mediaPlayer> 803 * <MediaPlayer cycleCount="-1"> 804 * <media> 805 * <Media> 806 * <source> 807 * <URL value="file:/Users/elp/Dekstop/blah.flv" /> 808 * </source> 809 * <Media/> 810 * </media> 811 * </MediaPlayer> 812 * </mediaPlayer> 813 * </MediaView> 814 */ 815 816 final FXOMDocument result = new FXOMDocument(); 817 818 /* 819 * URL 820 */ 821 final PropertyName valueName 822 = new PropertyName("value"); //NOI18N 823 final FXOMPropertyT valueProperty 824 = new FXOMPropertyT(result, valueName, media.getSource()); 825 final FXOMInstance urlInstance 826 = new FXOMInstance(result, URL.class); 827 valueProperty.addToParentInstance(-1, urlInstance); 828 829 /* 830 * Media 831 */ 832 final PropertyName sourceName 833 = new PropertyName("source"); //NOI18N 834 final FXOMPropertyC sourceProperty 835 = new FXOMPropertyC(result, sourceName, urlInstance); 836 final FXOMInstance mediaInstance 837 = new FXOMInstance(result, Media.class); 838 sourceProperty.addToParentInstance(-1, mediaInstance); 839 840 /* 841 * MediaPlayer 842 */ 843 final PropertyName mediaName 844 = new PropertyName("media"); //NOI18N 845 final FXOMPropertyC mediaProperty 846 = new FXOMPropertyC(result, mediaName, mediaInstance); 847 final FXOMInstance mediaPlayerInstance 848 = new FXOMInstance(result, MediaPlayer.class); 849 mediaProperty.addToParentInstance(-1, mediaPlayerInstance); 850 851 /* 852 * MediaView 853 */ 854 final PropertyName mediaPlayerName 855 = new PropertyName("mediaPlayer"); //NOI18N 856 final FXOMPropertyC mediaPlayerProperty 857 = new FXOMPropertyC(result, mediaPlayerName, mediaPlayerInstance); 858 final PropertyName fitWidthName 859 = new PropertyName("fitWidth"); //NOI18N 860 final FXOMPropertyT fitWidthProperty 861 = new FXOMPropertyT(result, fitWidthName, String.valueOf(fitWidth)); 862 final PropertyName fitHeightName 863 = new PropertyName("fitHeight"); //NOI18N 864 final FXOMPropertyT fitHeightProperty 865 = new FXOMPropertyT(result, fitHeightName, String.valueOf(fitHeight)); 866 final FXOMInstance mediaView 867 = new FXOMInstance(result, MediaView.class); 868 mediaPlayerProperty.addToParentInstance(-1, mediaView); 869 fitWidthProperty.addToParentInstance(-1, mediaView); 870 fitHeightProperty.addToParentInstance(-1, mediaView); 871 872 result.setFxomRoot(mediaView); 873 874 return result; 875 } 876 877 878 private static void serializeObjects(FXOMObject fxomObject, List<FXOMObject> result) { 879 assert fxomObject != null; 880 assert result != null; 881 882 result.add(fxomObject); 883 884 if (fxomObject instanceof FXOMInstance) { 885 final FXOMInstance fxomInstance = (FXOMInstance) fxomObject; 886 for (FXOMProperty p : fxomInstance.getProperties().values()) { 887 if (p instanceof FXOMPropertyC) { 888 final FXOMPropertyC pc = (FXOMPropertyC) p; 889 for (FXOMObject v : pc.getValues()) { 890 serializeObjects(v, result); 891 } 892 } 893 } 894 } else if (fxomObject instanceof FXOMCollection) { 895 final FXOMCollection fxomCollection = (FXOMCollection) fxomObject; 896 for (FXOMObject i : fxomCollection.getItems()) { 897 serializeObjects(i, result); 898 } 899 } 900 } 901 }