1 /* 2 * Copyright (c) 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.editor.panel.css; 33 34 import com.oracle.javafx.scenebuilder.kit.editor.panel.css.CssContentMaker.CssPropertyState; 35 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; 36 import com.oracle.javafx.scenebuilder.kit.util.CssInternal; 37 import com.oracle.javafx.scenebuilder.kit.util.Deprecation; 38 import com.sun.javafx.css.Rule; 39 import com.sun.javafx.css.Style; 40 import java.util.ArrayList; 41 import java.util.Collection; 42 import java.util.Collections; 43 import java.util.Comparator; 44 import java.util.HashMap; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.Objects; 48 import java.util.Set; 49 import java.util.SortedSet; 50 import java.util.TreeSet; 51 import javafx.beans.property.ObjectProperty; 52 import javafx.beans.property.SimpleObjectProperty; 53 import javafx.css.CssMetaData; 54 import javafx.css.StyleOrigin; 55 import javafx.css.Styleable; 56 import javafx.css.StyleableProperty; 57 import javafx.scene.Node; 58 import javafx.scene.control.Skinnable; 59 60 /** 61 * 62 * @treatAsPrivate 63 */ 64 public class NodeCssState { 65 66 private static final List<StyleOrigin> ORDERED_ORIGIN = new ArrayList<>(); 67 68 static { 69 ORDERED_ORIGIN.add(StyleOrigin.USER_AGENT);// fxTheme : modena/caspian 70 ORDERED_ORIGIN.add(StyleOrigin.USER);//Bean API Call 71 ORDERED_ORIGIN.add(StyleOrigin.AUTHOR);//CSS files 72 ORDERED_ORIGIN.add(StyleOrigin.INLINE);//Style property 73 74 } 75 76 @SuppressWarnings("rawtypes") 77 private final Map<StyleableProperty, List<Style>> map; 78 private final Node node; 79 private final FXOMObject fxomObject; 80 private Collection<CssContentMaker.CssPropertyState> author; 81 private Collection<CssContentMaker.CssPropertyState> inline; 82 private Collection<CssContentMaker.CssPropertyState> userAgent; 83 private Map<MatchingRule, List<MatchingDeclaration>> matchingRules; 84 private List<MatchingRule> sortedMatchingRules = new ArrayList<>(); 85 private Collection<CssProperty> props; 86 87 @SuppressWarnings("rawtypes") 88 protected NodeCssState(Map<StyleableProperty, List<Style>> map, Node node, FXOMObject fxomObject) { 89 this.map = map; 90 this.node = node; 91 this.fxomObject = fxomObject; 92 getAuthorStyles(); 93 getInlineStyles(); 94 getUserAgentStyles(); 95 getMatchingRules(); 96 getAllStyleables(); 97 } 98 99 /** 100 * 101 * @treatAsPrivate 102 */ 103 @SuppressWarnings("rawtypes") 104 public static class CssProperty implements Comparable<CssProperty> { 105 106 private final CssMetaData cssMeta; 107 private final CssProperty mainProperty; 108 private final Node target; 109 private final List<CssProperty> sub = new ArrayList<>(); 110 private final ObjectProperty<String> name = new SimpleObjectProperty<>(); 111 private final ObjectProperty<CssContentMaker.PropertyState> builtin = new SimpleObjectProperty<>(); 112 private final ObjectProperty<CssContentMaker.CssPropertyState> fxTheme = new SimpleObjectProperty<>(); 113 private final ObjectProperty<CssContentMaker.CssPropertyState> authorCss = new SimpleObjectProperty<>(); 114 private final ObjectProperty<CssContentMaker.CssPropertyState> inlineCss = new SimpleObjectProperty<>(); 115 private final ObjectProperty<CssContentMaker.PropertyState> fxmlModel = new SimpleObjectProperty<>(); 116 private CssContentMaker.PropertyState currentState; 117 118 CssProperty(NodeCssState nodeCssState, CssMetaData cssMeta, Node target, FXOMObject fxomObject) { 119 this(nodeCssState, null, cssMeta, target, fxomObject); 120 } 121 122 CssProperty(NodeCssState nodeCssState, CssProperty mainProperty, 123 CssMetaData cssMeta, Node target, FXOMObject fxomObject) { 124 this.mainProperty = mainProperty; 125 this.cssMeta = cssMeta; 126 this.target = target; 127 name.setValue(cssMeta.getProperty()); 128 CssContentMaker.CssPropertyState inlineState = nodeCssState.retrieveCssStyle(cssMeta, nodeCssState.getInlineStyles()); 129 if (inlineState != null) { 130 inlineCss.setValue(inlineState); 131 } 132 CssContentMaker.CssPropertyState authorState = nodeCssState.retrieveCssStyle(cssMeta, nodeCssState.getAuthorStyles()); 133 if (authorState != null) { 134 authorCss.setValue(authorState); 135 } 136 CssContentMaker.CssPropertyState fxThemeState = nodeCssState.retrieveCssStyle(cssMeta, nodeCssState.getUserAgentStyles()); 137 if (fxThemeState != null) { 138 fxTheme.setValue(fxThemeState); 139 } 140 @SuppressWarnings("unchecked") 141 CssContentMaker.PropertyState builtinState = CssContentMaker.initialValue(target, mainProperty == null ? this : mainProperty, cssMeta); 142 assert builtinState != null; 143 builtin.setValue(builtinState); 144 145 CssContentMaker.PropertyState modelState = CssContentMaker.modelValue(target, cssMeta, fxomObject); 146 if (modelState != null) { 147 fxmlModel.setValue(modelState); 148 } 149 } 150 151 public ObjectProperty<CssContentMaker.PropertyState> builtinState() { 152 return builtin; 153 } 154 155 public ObjectProperty<CssContentMaker.PropertyState> modelState() { 156 return fxmlModel; 157 } 158 159 public ObjectProperty<CssContentMaker.CssPropertyState> fxThemeState() { 160 return fxTheme; 161 } 162 163 public ObjectProperty<CssContentMaker.CssPropertyState> authorState() { 164 return authorCss; 165 } 166 167 public ObjectProperty<CssContentMaker.CssPropertyState> inlineState() { 168 return inlineCss; 169 } 170 171 public ObjectProperty<String> propertyName() { 172 return name; 173 } 174 175 public CssMetaData getStyleable() { 176 return cssMeta; 177 } 178 179 public Node getTarget() { 180 return target; 181 } 182 183 public List<CssProperty> getSubProperties() { 184 return sub; 185 } 186 187 public CssProperty getMainProperty() { 188 return mainProperty; 189 } 190 191 @Override 192 public int compareTo(CssProperty cssProperty) { 193 return cssMeta.getProperty().compareTo(cssProperty.cssMeta.getProperty()); 194 } 195 196 @Override 197 public boolean equals(Object obj) { 198 if (obj == null) { 199 return false; 200 } 201 if (getClass() != obj.getClass()) { 202 return false; 203 } 204 CssProperty cssProperty = (CssProperty) obj; 205 return cssMeta.getProperty().compareTo(cssProperty.cssMeta.getProperty()) == 0; 206 } 207 208 @Override 209 public int hashCode() { 210 int hash = 3; 211 hash = 31 * hash + Objects.hashCode(this.cssMeta); 212 hash = 31 * hash + Objects.hashCode(this.mainProperty); 213 hash = 31 * hash + Objects.hashCode(this.target); 214 return hash; 215 } 216 217 public boolean isBuiltinSource() { 218 return inlineState().get() == null 219 && authorState().get() == null 220 && modelState().get() == null 221 && fxThemeState().get() == null; 222 } 223 224 public boolean isFxThemeSource() { 225 return fxThemeState().get() != null 226 && inlineState().get() == null 227 && authorState().get() == null 228 && modelState().get() == null; 229 } 230 231 public boolean isModelSource() { 232 return modelState().get() != null 233 && inlineState().get() == null 234 && authorState().get() == null; 235 } 236 237 public boolean isAuthorSource() { 238 return authorState().get() != null 239 && inlineState().get() == null; 240 } 241 242 public boolean isInlineSource() { 243 return inlineState().get() != null; 244 } 245 246 // CSS only, model and builtin doesn't make sense there 247 public CssContentMaker.CssPropertyState getWinner() { 248 if (inlineState().get() != null) { 249 return inlineState().get(); 250 } 251 if (authorState().get() != null) { 252 return authorState().get(); 253 } 254 return fxThemeState().get(); 255 } 256 257 public CssContentMaker.PropertyState getCurrentStyle() { 258 if (currentState == null) { 259 currentState = builtinState().get(); 260 CssContentMaker.PropertyState model = modelState().get(); 261 CssContentMaker.CssPropertyState cssState = getWinner(); 262 if (cssState == null) { 263 if (model != null) { 264 currentState = model; 265 } 266 } else { 267 if (model != null && cssState.getStyle() != null 268 && cssState.getStyle().getOrigin() == StyleOrigin.USER_AGENT) { 269 currentState = model; 270 } else { 271 currentState = cssState; 272 } 273 } 274 } 275 return currentState; 276 } 277 278 public StyleOrigin getCurrentStyleOrigin() { 279 CssContentMaker.PropertyState state = getCurrentStyle(); 280 if (state instanceof CssContentMaker.CssPropertyState) { 281 CssContentMaker.CssPropertyState cssState = (CssContentMaker.CssPropertyState) state; 282 return cssState.getStyle().getOrigin(); 283 } else { 284 if (state instanceof CssContentMaker.BeanPropertyState) { 285 return StyleOrigin.USER; 286 } else { 287 return null; 288 } 289 } 290 } 291 292 public boolean isInlineInherited() { 293 boolean ret = false; 294 CssContentMaker.CssPropertyState css = inlineCss.get(); 295 if (css != null) { 296 ret = CssContentMaker.isInlineInherited(target, css); 297 } 298 return ret; 299 } 300 301 public Node getSourceNodeForInline() { 302 Node ret = null; 303 CssContentMaker.CssPropertyState css = inlineCss.get(); 304 if (css != null) { 305 ret = CssContentMaker.getSourceNodeForStyle(target, propertyName().get()); 306 } 307 return ret; 308 } 309 310 public List<CssContentMaker.CssPropertyState.CssStyle> getFxThemeHiddenByModel() { 311 List<CssContentMaker.CssPropertyState.CssStyle> ret = new ArrayList<>(); 312 CssContentMaker.CssPropertyState ps = getWinner(); 313 List<CssContentMaker.CssPropertyState.CssStyle> notAppliedStyles = 314 ps == null ? 315 Collections.<CssContentMaker.CssPropertyState.CssStyle>emptyList() : 316 ps.getNotAppliedStyles(); 317 boolean hasModel = modelState().get() != null; 318 if (hasModel) { 319 List<Style> allStyles = Deprecation.getMatchingStyles(getStyleable(), target); 320 List<Style> matchingStyles = CssContentMaker.removeUserAgentStyles(allStyles); 321 for (Style style : matchingStyles) { 322 CssContentMaker.CssPropertyState.CssStyle cssStyle = new CssContentMaker.CssPropertyState.CssStyle(style); 323 if (cssStyle.getOrigin() == StyleOrigin.USER_AGENT && !notAppliedStyles.contains(cssStyle)) { 324 if (getStyleable().getProperty().equals(cssStyle.getCssProperty())) { 325 cssStyle = CssContentMaker.retrieveStyle(matchingStyles, style); 326 ret.add(cssStyle); 327 } 328 } 329 } 330 } 331 return ret; 332 } 333 334 } 335 336 private CssContentMaker.CssPropertyState retrieveCssStyle( 337 CssMetaData<?, ?> cssMeta, Collection<CssContentMaker.CssPropertyState> styles) { 338 for (CssContentMaker.CssPropertyState prop : styles) { 339 if (prop.getCssProperty().equals(cssMeta.getProperty())) { 340 return prop; 341 } else { 342 if (prop.getSubProperties() != null) { 343 for (CssContentMaker.PropertyState sub : prop.getSubProperties()) { 344 if (sub.getCssProperty().equals(cssMeta.getProperty())) { 345 return (CssContentMaker.CssPropertyState) sub; 346 } 347 } 348 } 349 } 350 } 351 return null; 352 } 353 354 public Node getNode() { 355 return node; 356 } 357 358 @SuppressWarnings("rawtypes") 359 public final Collection<CssProperty> getAllStyleables() { 360 if (props == null) { 361 props = new TreeSet<>(); 362 List<CssMetaData<? extends Styleable, ?>> cssMetaList = node.getCssMetaData(); 363 for (CssMetaData<? extends Styleable, ?> cssMeta : cssMetaList) { 364 CssProperty mainProp = new CssProperty(this, cssMeta, node, fxomObject); 365 props.add(mainProp); 366 if (cssMeta.getSubProperties() != null) { 367 for (CssMetaData sub : cssMeta.getSubProperties()) { 368 CssProperty subProp = new CssProperty(this, mainProp, sub, node, fxomObject); 369 mainProp.getSubProperties().add(subProp); 370 } 371 } 372 } 373 374 if (node instanceof Skinnable) { 375 Skinnable skinnable = (Skinnable) node; 376 Node skinNode = skinnable.getSkin().getNode(); 377 List<CssMetaData<? extends Styleable, ?>> skinList = skinNode.getCssMetaData(); 378 for (CssMetaData<? extends Styleable, ?> skinCssMeta : skinList) { 379 boolean found = false; 380 for (CssMetaData cssMeta : cssMetaList) { 381 if (skinCssMeta.getProperty().equals(cssMeta.getProperty())) { 382 found = true; 383 break; 384 } 385 } 386 if (!found) { 387 CssProperty mainProp = new CssProperty(this, skinCssMeta, skinNode, fxomObject); 388 props.add(mainProp); 389 if (skinCssMeta.getSubProperties() != null) { 390 for (CssMetaData<? extends Styleable, ?> sub : skinCssMeta.getSubProperties()) { 391 CssProperty subProp = new CssProperty(this, sub, node, fxomObject); 392 mainProp.getSubProperties().add(subProp); 393 } 394 } 395 } 396 } 397 } 398 } 399 return props; 400 } 401 402 public final Collection<CssContentMaker.CssPropertyState> getAuthorStyles() { 403 if (author == null) { 404 author = getAppliedStyles(StyleOrigin.AUTHOR); 405 } 406 return author; 407 } 408 409 public final Collection<CssContentMaker.CssPropertyState> getInlineStyles() { 410 if (inline == null) { 411 inline = getAppliedStyles(StyleOrigin.INLINE); 412 } 413 return inline; 414 } 415 416 public final Collection<CssContentMaker.CssPropertyState> getUserAgentStyles() { 417 if (userAgent == null) { 418 userAgent = getAppliedStyles(StyleOrigin.USER_AGENT); 419 } 420 return userAgent; 421 } 422 423 /** 424 * 425 * @treatAsPrivate 426 */ 427 public static class RuleComparator implements Comparator<MatchingRule> { 428 429 @Override 430 public int compare(MatchingRule t, MatchingRule t1) { 431 int originComparaison = compareOrigin( 432 t.getRule().getOrigin(), t1.rule.getOrigin()); 433 int tnotApplied = countNotApplied(t.declarations); 434 int t1notApplied = countNotApplied(t1.declarations); 435 int notAppliedComparaisons = tnotApplied - t1notApplied; 436 437 if (originComparaison == 0) {// Same origin, not Applied count is what is important. The less, the stronger 438 return notAppliedComparaisons; 439 } else { 440 return originComparaison; 441 } 442 } 443 444 } 445 446 private static int compareOrigin(StyleOrigin toCompare, StyleOrigin other) { 447 int index1 = ORDERED_ORIGIN.indexOf(toCompare); 448 int index2 = ORDERED_ORIGIN.indexOf(other); 449 return index2 - index1; 450 451 } 452 453 private static int countNotApplied(List<MatchingDeclaration> declarations) { 454 int count = 0; 455 for (MatchingDeclaration decl : declarations) { 456 if (!decl.isApplied()) { 457 count += 1; 458 } 459 } 460 return count; 461 } 462 463 /** 464 * 465 * @treatAsPrivate 466 */ 467 public static class MatchingRule { 468 469 private final Rule rule; 470 private final String selector; 471 private final List<MatchingDeclaration> declarations = new ArrayList<>(); 472 473 private MatchingRule(Rule rule, String selector) { 474 this.rule = rule; 475 this.selector = selector; 476 } 477 478 public Rule getRule() { 479 return rule; 480 } 481 482 public String getSelector() { 483 return selector; 484 } 485 486 @Override 487 public int hashCode() { 488 int hash = 7; 489 hash = 59 * hash + (this.rule != null ? this.rule.hashCode() : 0); 490 return hash; 491 } 492 493 @Override 494 public boolean equals(Object obj) { 495 if (!(obj instanceof MatchingRule)) { 496 return false; 497 } 498 MatchingRule mr = (MatchingRule) obj; 499 return rule.equals(mr.rule); 500 } 501 502 private void addDeclarations(List<MatchingDeclaration> values) { 503 declarations.addAll(values); 504 } 505 506 public List<MatchingDeclaration> getDeclarations() { 507 return Collections.unmodifiableList(declarations); 508 } 509 510 @Override 511 public String toString() { 512 return rule.getSelectors().toString(); 513 } 514 } 515 516 /** 517 * 518 * @treatAsPrivate 519 */ 520 public static class MatchingDeclaration { 521 522 private final CssContentMaker.CssPropertyState.CssStyle style; 523 private final CssContentMaker.CssPropertyState prop; 524 private final boolean applied; 525 private final boolean lookup; 526 527 MatchingDeclaration(CssContentMaker.CssPropertyState.CssStyle style, 528 CssContentMaker.CssPropertyState prop, boolean applied, boolean lookup) { 529 this.style = style; 530 this.prop = prop; 531 this.applied = applied; 532 this.lookup = lookup; 533 } 534 535 /** 536 * @return the style 537 */ 538 public CssContentMaker.CssPropertyState.CssStyle getStyle() { 539 return style; 540 } 541 542 /** 543 * @return the prop 544 */ 545 public CssContentMaker.CssPropertyState getProp() { 546 return prop; 547 } 548 549 /** 550 * @return the applied 551 */ 552 public boolean isApplied() { 553 return applied; 554 } 555 556 public boolean isLookup() { 557 return lookup; 558 } 559 } 560 561 // This method add sub properties instead of compund property. 562 private static void addSubProperties( 563 Collection<CssContentMaker.CssPropertyState> source, 564 Collection<CssContentMaker.CssPropertyState> target) { 565 for (CssContentMaker.CssPropertyState p : source) { 566 if (p.getSubProperties().isEmpty()) { 567 target.add(p); 568 } else { 569 for (CssContentMaker.PropertyState sub : p.getSubProperties()) { 570 target.add((CssContentMaker.CssPropertyState) sub); 571 } 572 } 573 } 574 } 575 576 // Sorted according to Author/User Agent and applied / not applied 577 public final List<MatchingRule> getMatchingRules() { 578 if (matchingRules == null) { 579 Collection<CssContentMaker.CssPropertyState> styledProperties = new TreeSet<>(); 580 addSubProperties(getUserAgentStyles(), styledProperties); 581 addSubProperties(getAuthorStyles(), styledProperties); 582 // We need them to have the exhaustive set of styled properties. 583 // We compute the rules based on the set of properties. 584 addSubProperties(getInlineStyles(), styledProperties); 585 matchingRules = new HashMap<>(); 586 for (CssContentMaker.CssPropertyState cssP : styledProperties) { 587 List<CssContentMaker.PropertyState> l = cssP.getSubProperties(); 588 if (l.isEmpty()) { 589 addMatchingDeclaration(cssP); 590 } else { 591 for (CssContentMaker.PropertyState pp : l) { 592 CssContentMaker.CssPropertyState cssSubP = (CssContentMaker.CssPropertyState) pp; 593 addMatchingDeclaration(cssSubP); 594 } 595 } 596 } 597 for (Map.Entry<MatchingRule, List<MatchingDeclaration>> entry : matchingRules.entrySet()) { 598 MatchingRule rule = entry.getKey(); 599 // Filterout the Inline 600 if (rule.getRule().getOrigin() != StyleOrigin.INLINE) { 601 rule.addDeclarations(entry.getValue()); 602 sortedMatchingRules.add(rule); 603 } 604 } 605 Collections.<MatchingRule>sort(sortedMatchingRules, new RuleComparator()); 606 } 607 return sortedMatchingRules; 608 } 609 610 private void addMatchingDeclaration(CssPropertyState cssP) { 611 addMatchingDeclaration(cssP, cssP.getStyle(), true, false); 612 for (CssPropertyState.CssStyle s : cssP.getNotAppliedStyles()) { 613 addMatchingDeclaration(cssP, s, false, false); 614 } 615 } 616 617 private void addMatchingDeclaration( 618 CssPropertyState cssP, CssPropertyState.CssStyle style, boolean applied, boolean isLookup) { 619 MatchingRule mr = new MatchingRule(style.getCssRule(), style.getSelector()); 620 List<MatchingDeclaration> lst = matchingRules.get(mr); 621 if (lst == null) { 622 lst = new ArrayList<>(); 623 matchingRules.put(mr, lst); 624 } 625 MatchingDeclaration pmr = new MatchingDeclaration(style, cssP, applied, isLookup); 626 boolean found = false; 627 for (MatchingDeclaration d : lst) { 628 if (d.style.getCssProperty().equals(style.getCssProperty())) { 629 found = true; 630 break; 631 } 632 } 633 634 if (!found) { 635 lst.add(pmr); 636 } 637 for (CssContentMaker.CssPropertyState.CssStyle lookup : style.getLookupChain()) { 638 addMatchingDeclaration(cssP, lookup, applied, true); 639 } 640 } 641 642 @SuppressWarnings("rawtypes") 643 private Set<CssContentMaker.CssPropertyState> getAppliedStyles(StyleOrigin origin) { 644 SortedSet<CssContentMaker.CssPropertyState> propertyStates = new TreeSet<>(); 645 646 // if (origin == StyleOrigin.USER_AGENT) { 647 // System.out.println("==========================="); 648 // System.out.println("getAppliedStyles() called!"); 649 // System.out.println("==========================="); 650 // for (StyleableProperty sp : map.keySet()) { 651 // System.out.println("---------------------------"); 652 // System.out.println("Styleable property: " + sp); 653 // System.out.println("---------------------------"); 654 // List<Style> styles = map.get(sp); 655 // CssContentMaker.printStyles(styles); 656 // } 657 // System.out.println("\n\n\n"); 658 // } 659 for (Map.Entry<StyleableProperty, List<Style>> entry : map.entrySet()) {//NOI18N 660 StyleableProperty<?> value = entry.getKey(); 661 // System.out.println("\nStyleable property: " + value); 662 assert entry.getValue() != null; 663 assert !entry.getValue().isEmpty(); 664 Style st = entry.getValue().get(0); 665 StyleOrigin o = CssInternal.getOrigin(st); 666 // printStyle(st); 667 /* If this origin is equals to the passed one, this is the nominal case. 668 * If this property contains sub properties (eg:background-fills), then we need to check 669 * each sub property. 670 */ 671 CssMetaData<? extends Styleable, ?> cssMetaList = value.getCssMetaData(); 672 if (o == origin || cssMetaList.getSubProperties() != null) { 673 // Need the first style to compute the value 674 // We have at least a style. The first one is the winner. 675 String cssValue = CssValueConverter.toCssString(cssMetaList.getProperty(), 676 st.getDeclaration().getRule(), value.getValue()); 677 678 CssContentMaker.CssPropertyState pState = new CssContentMaker.CssPropertyState(value, cssMetaList, cssValue); 679 680 /* 681 * Each sub property can be ruled by a specific Origin, 682 * we need to check if the sub property is in a rule of the passed origin. 683 * For example, we can have background-radius set by fxTheme 684 * and background-color set by inline or author. 685 */ 686 if (cssMetaList.getSubProperties() != null) { 687 for (CssMetaData sub : cssMetaList.getSubProperties()) { 688 List<CssContentMaker.CssPropertyState.CssStyle> notApplied = CssContentMaker.getNotAppliedStyles(entry.getValue(), node, sub); 689 for (Style style : entry.getValue()) { 690 StyleOrigin styleOrigin = CssInternal.getOrigin(style); 691 if (style.getDeclaration().getProperty().equals(sub.getProperty()) 692 && (styleOrigin == origin)) { 693 CssContentMaker.CssPropertyState.CssStyle cssStyle = CssContentMaker.retrieveStyle(entry.getValue(), style); 694 String subCssValue = CssValueConverter.toCssString(sub.getProperty(), style.getDeclaration().getRule(), value.getValue()); 695 CssContentMaker.CssSubPropertyState subCss = new CssContentMaker.CssSubPropertyState(value, sub, subCssValue); 696 subCss.setStyle(cssStyle); 697 subCss.getNotAppliedStyles().addAll(notApplied); 698 pState.getSubProperties().add(subCss); 699 } 700 } 701 } 702 // eg: -fx-font set 703 CssContentMaker.CssPropertyState.CssStyle style = CssContentMaker.retrieveStyle(entry.getValue(), st); 704 pState.setStyle(style); 705 if (!st.getDeclaration().getProperty().equals(cssMetaList.getProperty())) { 706 style.setUnused(); 707 } 708 } else { 709 // Single style for this single property. 710 // Transform the flat list into a chain of lookup. 711 CssContentMaker.CssPropertyState.CssStyle style = CssContentMaker.retrieveStyle(entry.getValue(), st); 712 pState.setStyle(style); 713 } 714 List<Style> applied = new ArrayList<>(); 715 applied.add(st); 716 pState.getNotAppliedStyles().addAll(CssContentMaker.getNotAppliedStyles(applied, node, cssMetaList)); 717 /* 718 * In case the origin is not the same and no sub properties have been found for 719 * the passed origin, then the property is not taken into consideration 720 */ 721 if (o == origin || !pState.getSubProperties().isEmpty()) { 722 propertyStates.add(pState); 723 } 724 } 725 } 726 return propertyStates; 727 } 728 }