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 }