1 /*
   2  * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.css;
  27 
  28 import javafx.css.PseudoClass;
  29 import javafx.css.StyleOrigin;
  30 
  31 import java.util.Set;
  32 
  33 
  34 /** A marriage of pseudo-classes (potentially empty) to property and value */
  35 public class CascadingStyle implements Comparable<CascadingStyle> {
  36 
  37     /** */
  38     private final Style style;
  39     public Style getStyle() {
  40         return style;
  41     }
  42     
  43     /** State variables, like &quot;hover&quot; or &quot;pressed&quot; */
  44     private final Set<PseudoClass> pseudoClasses;
  45 
  46     /* specificity of the selector that matched */
  47     private final int specificity;
  48     
  49     /* order in which this style appeared in the stylesheet */
  50     private final int ordinal;
  51     
  52     /*
  53      * True if the property is -fx-skin. We want the skin property to
  54      * sort less than all other properties.
  55      */
  56     private final boolean skinProp;
  57 
  58     public CascadingStyle(final Style style, Set<PseudoClass> pseudoClasses,
  59             final int specificity, final int ordinal) {
  60         this.style = style;
  61         this.pseudoClasses = pseudoClasses;
  62         this.specificity = specificity;
  63         this.ordinal = ordinal;
  64         this.skinProp = "-fx-skin".equals(style.getDeclaration().getProperty());
  65     }
  66         
  67     // Wrapper to make StyleHelper's life a little easier
  68     public String getProperty() {
  69         return style.getDeclaration().getProperty();
  70     }
  71     
  72     // Wrapper to make StyleHelper's life a little easier
  73     public Selector getSelector() {
  74         return style.getSelector();
  75     }
  76     
  77     // Wrapper to make StyleHelper's life a little easier
  78     public Rule getRule() {
  79         return style.getDeclaration().getRule();
  80     }
  81     
  82     // Wrapper to make StyleHelper's life a little easier
  83     public StyleOrigin getOrigin() {
  84         return getRule().getOrigin();
  85     }
  86     
  87     // Wrapper to make StyleHelper's life a little easier
  88     public ParsedValueImpl getParsedValueImpl() {
  89         return style.getDeclaration().getParsedValueImpl();
  90     }
  91     
  92     @Override public String toString() { return getProperty(); }
  93 
  94     /**
  95      * When testing equality against another Style, we only care about
  96      * the property and pseudo-classes. In other words, we only care about
  97      * where the style is applied, not what is applied.
  98      */
  99     @Override public boolean equals(Object obj) {
 100         if (obj == null) {
 101             return false;
 102         }
 103         if (getClass() != obj.getClass()) {
 104             return false;
 105         }
 106         CascadingStyle other = (CascadingStyle)obj;
 107 
 108         final String property = getProperty();
 109         final String otherProperty = other.getProperty();
 110         if (property == null ? otherProperty != null : !property.equals(otherProperty)) {
 111             return false;
 112         }
 113         
 114         // does [foo bar bang] contain all of [foo bar]?
 115         if (pseudoClasses == null ? other.pseudoClasses != null : !pseudoClasses.containsAll(other.pseudoClasses)) {
 116             return false;            
 117         }
 118         
 119         return true;
 120 
 121     }
 122 
 123     /*
 124      * Hash on property and pseudoclasses since
 125      * obj1.hashCode() should equal obj2.hashCode() if obj1.equals(obj2)
 126      */
 127     @Override
 128     public int hashCode() {
 129         int hash = 7;
 130         final String property = getProperty();
 131         hash = 47 * hash + (property != null ? property.hashCode() : 0);
 132         hash = 47 * hash + (pseudoClasses != null ? pseudoClasses.hashCode() : 0);
 133         return hash;
 134     }
 135 
 136     /**
 137      * Implementation of Comparable such that more specific styles get
 138      * sorted before less specific ones.
 139      */
 140     @Override
 141     public int compareTo(CascadingStyle other) {
 142 
 143         //
 144         // Important styles take the cake
 145         // Importance being equal, then specificity is considered
 146         // Specificity being equal, then the order of declaration decides.
 147         //
 148         
 149         final Declaration decl = style.getDeclaration();
 150         final boolean important = decl != null ? decl.isImportant() : false;
 151         final Rule rule = decl != null ? decl.getRule() : null;
 152         final StyleOrigin source = rule != null ? rule.getOrigin() : null;
 153         
 154         final Declaration otherDecl = other.style.getDeclaration();
 155         final boolean otherImportant = otherDecl != null ? otherDecl.isImportant() : false;
 156         final Rule otherRule = otherDecl != null ? otherDecl.getRule() : null;
 157         final StyleOrigin otherSource = otherRule != null ? otherRule.getOrigin() : null;
 158 
 159         int c = 0;
 160 
 161         if (this.skinProp && !other.skinProp) {
 162             c = 1;
 163         } else if (important != otherImportant) {
 164             c = important ? -1 : 1;
 165         } else if (source != otherSource) {
 166             if (source == null) c = -1;
 167             else if (otherSource == null) c = 1;
 168             else c = otherSource.compareTo(source);
 169         } else {
 170             c = other.specificity - this.specificity;
 171         };
 172 
 173         if (c == 0) c = other.ordinal - this.ordinal;
 174         return c;
 175     }
 176 
 177 }
 178