modules/graphics/src/main/java/javafx/css/CompoundSelector.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization


   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.Styleable;
  30 
  31 import java.io.DataInputStream;
  32 import java.io.DataOutputStream;
  33 import java.io.IOException;
  34 import java.util.ArrayList;
  35 import java.util.Collections;
  36 import java.util.List;
  37 import java.util.Set;
  38 

  39 /**
  40  * A compound selector which behaves according to the CSS standard. The selector is 
  41  * composed of one or more <code>Selectors</code>, along with an array of 
  42  * <code>CompoundSelectorRelationships</code> indicating the required relationship at each 
  43  * stage.  There must be exactly one less <code>Combinator</code> than 
  44  * there are selectors.
  45  * <p>
  46  * For example, the parameters <code>[selector1, selector2, selector3]</code>
  47  * and <code>[Combinator.CHILD, Combinator.DESCENDANT]</code> will match 
  48  * a component when all of the following conditions hold:
  49  * <ol>
  50  * <li>The component itself is matched by selector3
  51  * <li>The component has an ancestor which is matched by selector2
  52  * <li>The ancestor matched in step 2 is a direct CHILD of a component 
  53  * matched by selector1
  54  * </ol>
  55  * In other words, the compound selector specified above is (in CSS syntax) 
  56  * <code>selector1 &gt; selector2 selector3</code>.  The greater-than (&gt;) 
  57  * between selector1 and selector2 specifies a direct CHILD, whereas the 
  58  * whitespace between selector2 and selector3 corresponds to
  59  * <code>Combinator.DESCENDANT</code>.
  60  *

  61  */
  62 
  63 final public class CompoundSelector extends Selector {
  64 
  65     private final List<SimpleSelector> selectors;
  66     /**
  67      * The selectors that make up this compound selector
  68      * @return Immutable List&lt;SimpleSelector&gt;
  69      */
  70     public List<SimpleSelector> getSelectors() {
  71         return selectors;
  72     }
  73 
  74     private final List<Combinator> relationships;








  75     /**
  76      * The relationships between the selectors
  77      * @return Immutable List&lt;Combinator&gt;

  78      */
  79     public List<Combinator> getRelationships() {
  80         return relationships;
  81     }
  82 
  83     public CompoundSelector(List<SimpleSelector> selectors, List<Combinator> relationships)
  84     {
  85         this.selectors = 
  86             (selectors != null) 
  87                 ? Collections.unmodifiableList(selectors) 
  88                 : Collections.EMPTY_LIST;
  89         this.relationships =
  90             (relationships != null) 
  91                 ? Collections.unmodifiableList(relationships) 
  92                 : Collections.EMPTY_LIST;
  93     }
  94         
  95     private CompoundSelector()
  96     {
  97         this(null, null);
  98     }
  99     
 100     Match createMatch() {
 101 

 102         final PseudoClassState allPseudoClasses = new PseudoClassState();
 103         int idCount = 0;
 104         int styleClassCount = 0;
 105 
 106         for(int n=0, nMax=selectors.size(); n<nMax; n++) {
 107             Selector sel = selectors.get(n);
 108             Match match = sel.createMatch();
 109             allPseudoClasses.addAll(match.pseudoClasses);
 110             idCount += match.idCount;
 111             styleClassCount += match.styleClassCount;
 112         }
 113 
 114         return new Match(this, allPseudoClasses, idCount, styleClassCount);
 115     }
 116 
 117     @Override
 118     public boolean applies(final Styleable styleable) {
 119         return applies(styleable, selectors.size()-1, null, 0);
 120     }
 121 
 122     @Override
 123     boolean applies(final Styleable styleable, Set<PseudoClass>[] triggerStates, int depth) {
 124         
 125         assert (triggerStates == null || depth < triggerStates.length);
 126         if (triggerStates != null && triggerStates.length <= depth) {
 127             return false;
 128         }
 129         
 130         // 
 131         // We only care about pseudo-class if the selector applies. But in
 132         // the case of a compound selector, we don't know whether it applies
 133         // until all the selectors have been checked (in the worse case). So
 134         // the setting of pseudo-class has to be deferred until we know
 135         // that this compound selector applies. So we'll send a new 
 136         // PseudoClassSet[] and if the compound selector applies, 
 137         // just copy the state back. 
 138         //
 139         final Set<PseudoClass>[] tempStates = triggerStates != null 
 140                 ? new PseudoClassState[triggerStates.length] : null;
 141         
 142         final boolean applies = applies(styleable, selectors.size()-1, tempStates, depth);
 143         


 183         if (relationship == Combinator.CHILD) {
 184             final Styleable parent = styleable.getStyleableParent();
 185             if (parent == null) return false;
 186             // If this call succeeds, then all preceding selectors will have
 187             // matched due to the recursive nature of the call
 188             return applies(parent, index - 1, triggerStates, ++depth);
 189         } else {
 190              Styleable parent = styleable.getStyleableParent();
 191             while (parent != null) {
 192                 boolean answer = applies(parent, index - 1, triggerStates, ++depth);
 193                 // If a call to stateMatches succeeded, then we know that
 194                 // all preceding selectors will have also matched.
 195                 if (answer) return true;
 196                 // Otherwise we need to get the next parent and try again
 197                 parent = parent.getStyleableParent();
 198             }
 199         }
 200         return false;
 201     }
 202 
 203     @Override
 204     public boolean stateMatches(final Styleable styleable, Set<PseudoClass> states) {
 205         return stateMatches(styleable, states, selectors.size()-1);
 206     }
 207 
 208     private boolean stateMatches(Styleable styleable, Set<PseudoClass> states, int index) {
 209         // If the index is < 0 then we know we don't match
 210         if (index < 0) return false;
 211 
 212         // Simply check the selector associated with this index and see if it
 213         // matches the Node and states provided.
 214         if (! selectors.get(index).stateMatches(styleable, states)) return false;
 215 
 216         // If there are no more selectors to match (ie: index == 0) then we
 217         // know we have successfully matched
 218         if (index == 0) return true;
 219 
 220         // We have not yet checked all the selectors in this CompoundSelector,
 221         // so now we need to find the next parent and try again. If the
 222         // relationship between this selector and its ancestor selector is
 223         // "CHILD" then it is required that the parent scenegraph node match
 224         // the ancestor selector. Otherwise, we just walk up the scenegraph


 237             }
 238         } else {
 239             Styleable parent = styleable.getStyleableParent();
 240             while (parent != null) {
 241                 if (selectors.get(index-1).applies(parent)) { 
 242                     Set<PseudoClass> parentStates = parent.getPseudoClassStates();
 243                     return stateMatches(parent, parentStates, index - 1);
 244                 }
 245                 // Otherwise we need to get the next parent and try again
 246                 parent = parent.getStyleableParent();
 247             }
 248         }
 249 
 250         return false;
 251     }
 252 
 253     private  int hash = -1;
 254 
 255     /* Hash code is used in Style's hash code and Style's hash
 256        code is used by StyleHelper */
 257     @Override
 258     public int hashCode() {
 259         if (hash == -1) {
 260             for (int i = 0, max=selectors.size(); i<max; i++) 
 261                 hash = 31 * (hash + selectors.get(i).hashCode());
 262             for (int i = 0, max=relationships.size(); i<max; i++) 
 263                 hash = 31 * (hash + relationships.get(i).hashCode());
 264         }
 265         return hash;
 266     }
 267 
 268     @Override
 269     public boolean equals(Object obj) {
 270         if (obj == null) {
 271             return false;
 272         }
 273         if (getClass() != obj.getClass()) {
 274             return false;
 275         }
 276         final CompoundSelector other = (CompoundSelector) obj;
 277         if (other.selectors.size() != selectors.size()) return false;
 278         // Avoid ArrayList equals since it uses enhanced for loop
 279         for (int i = 0, max=selectors.size(); i<max; i++) {
 280             if (!other.selectors.get(i).equals(selectors.get(i))) return false;
 281         }
 282         // Avoid ArrayList equals since it uses enhanced for loop
 283         if (other.relationships.size() != relationships.size()) return false;
 284         for (int i = 0, max=relationships.size(); i<max; i++) {
 285             if (!other.relationships.get(i).equals(relationships.get(i))) return false;
 286         }
 287         return true;
 288     }
 289 
 290     @Override public String toString() {
 291         StringBuilder sbuf = new StringBuilder(); 
 292         sbuf.append(selectors.get(0));
 293         for(int n=1; n<selectors.size(); n++) {
 294             sbuf.append(relationships.get(n-1));
 295             sbuf.append(selectors.get(n));
 296         }
 297         return sbuf.toString();
 298     }
 299 
 300     public final void writeBinary(final DataOutputStream os, final StringStore stringStore)
 301             throws IOException
 302     {
 303         super.writeBinary(os, stringStore);
 304         os.writeShort(selectors.size());
 305         for (int n=0; n< selectors.size(); n++) selectors.get(n).writeBinary(os,stringStore);
 306         os.writeShort(relationships.size());
 307         for (int n=0; n< relationships.size(); n++) os.writeByte(relationships.get(n).ordinal());
 308     }
 309 
 310     public static CompoundSelector readBinary(int bssVersion, final DataInputStream is, final String[] strings)
 311             throws IOException
 312     {
 313 
 314         final int nSelectors = is.readShort();
 315         final List<SimpleSelector> selectors = new ArrayList<SimpleSelector>();
 316         for (int n=0; n<nSelectors; n++) {
 317             selectors.add((SimpleSelector)Selector.readBinary(bssVersion, is,strings));
 318         }
 319 
 320         final int nRelationships = is.readShort();
 321 
 322         final List<Combinator> relationships = new ArrayList<Combinator>();
 323         for (int n=0; n<nRelationships; n++) {
 324             final int ordinal = is.readByte();
 325             if (ordinal == Combinator.CHILD.ordinal()) 
 326                 relationships.add(Combinator.CHILD);
 327             else if (ordinal == Combinator.DESCENDANT.ordinal()) 
 328                 relationships.add(Combinator.DESCENDANT);
 329             else {
 330                 assert false : "error deserializing CompoundSelector: Combinator = " + ordinal;


   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 javafx.css;
  27 
  28 import com.sun.javafx.css.Combinator;
  29 import com.sun.javafx.css.PseudoClassState;
  30 
  31 import java.io.DataInputStream;
  32 import java.io.DataOutputStream;
  33 import java.io.IOException;
  34 import java.util.ArrayList;
  35 import java.util.Collections;
  36 import java.util.List;
  37 import java.util.Set;
  38 
  39 
  40 /**
  41  * A compound selector which behaves according to the CSS standard. The selector is 
  42  * composed of one or more <code>Selectors</code>, along with an array of 
  43  * <code>CompoundSelectorRelationships</code> indicating the required relationship at each 
  44  * stage.  There must be exactly one less <code>Combinator</code> than 
  45  * there are selectors.
  46  * <p>
  47  * For example, the parameters <code>[selector1, selector2, selector3]</code>
  48  * and <code>[Combinator.CHILD, Combinator.DESCENDANT]</code> will match 
  49  * a component when all of the following conditions hold:
  50  * <ol>
  51  * <li>The component itself is matched by selector3
  52  * <li>The component has an ancestor which is matched by selector2
  53  * <li>The ancestor matched in step 2 is a direct CHILD of a component 
  54  * matched by selector1
  55  * </ol>
  56  * In other words, the compound selector specified above is (in CSS syntax) 
  57  * <code>selector1 &gt; selector2 selector3</code>.  The greater-than (&gt;) 
  58  * between selector1 and selector2 specifies a direct CHILD, whereas the 
  59  * whitespace between selector2 and selector3 corresponds to
  60  * <code>Combinator.DESCENDANT</code>.
  61  *
  62  * @since 9
  63  */

  64 final public class CompoundSelector extends Selector {
  65 
  66     private final List<SimpleSelector> selectors;
  67     /**
  68      * The selectors that make up this compound selector
  69      * @return Immutable List&lt;SimpleSelector&gt;
  70      */
  71     public List<SimpleSelector> getSelectors() {
  72         return selectors;
  73     }
  74 
  75     private final List<Combinator> relationships;
  76     // /**
  77     //  * The relationships between the selectors
  78     //  * @return Immutable List&lt;Combinator&gt;
  79     //  */
  80     // public List<Combinator> getRelationships() {
  81     //     return relationships;
  82     // }
  83 
  84     /**
  85      * Creates a <code>CompoundSelector</code> from a list of selectors and a
  86      * list of <code>Combinator</code> relationships.  There must be exactly one
  87      * less <code>Combinator</code> than there are selectors.
  88      */
  89     CompoundSelector(List<SimpleSelector> selectors, List<Combinator> relationships) {





  90         this.selectors = 
  91             (selectors != null) 
  92                 ? Collections.unmodifiableList(selectors) 
  93                 : Collections.EMPTY_LIST;
  94         this.relationships =
  95             (relationships != null) 
  96                 ? Collections.unmodifiableList(relationships) 
  97                 : Collections.EMPTY_LIST;
  98     }
  99         
 100     private CompoundSelector() {

 101         this(null, null);
 102     }
 103     

 104 
 105     @Override public Match createMatch() {
 106         final PseudoClassState allPseudoClasses = new PseudoClassState();
 107         int idCount = 0;
 108         int styleClassCount = 0;
 109 
 110         for(int n=0, nMax=selectors.size(); n<nMax; n++) {
 111             Selector sel = selectors.get(n);
 112             Match match = sel.createMatch();
 113             allPseudoClasses.addAll(match.pseudoClasses);
 114             idCount += match.idCount;
 115             styleClassCount += match.styleClassCount;
 116         }
 117 
 118         return new Match(this, allPseudoClasses, idCount, styleClassCount);
 119     }
 120 
 121     @Override public boolean applies(final Styleable styleable) {

 122         return applies(styleable, selectors.size()-1, null, 0);
 123     }
 124 
 125     @Override public boolean applies(final Styleable styleable, Set<PseudoClass>[] triggerStates, int depth) {

 126         
 127         assert (triggerStates == null || depth < triggerStates.length);
 128         if (triggerStates != null && triggerStates.length <= depth) {
 129             return false;
 130         }
 131         
 132         // 
 133         // We only care about pseudo-class if the selector applies. But in
 134         // the case of a compound selector, we don't know whether it applies
 135         // until all the selectors have been checked (in the worse case). So
 136         // the setting of pseudo-class has to be deferred until we know
 137         // that this compound selector applies. So we'll send a new 
 138         // PseudoClassSet[] and if the compound selector applies, 
 139         // just copy the state back. 
 140         //
 141         final Set<PseudoClass>[] tempStates = triggerStates != null 
 142                 ? new PseudoClassState[triggerStates.length] : null;
 143         
 144         final boolean applies = applies(styleable, selectors.size()-1, tempStates, depth);
 145         


 185         if (relationship == Combinator.CHILD) {
 186             final Styleable parent = styleable.getStyleableParent();
 187             if (parent == null) return false;
 188             // If this call succeeds, then all preceding selectors will have
 189             // matched due to the recursive nature of the call
 190             return applies(parent, index - 1, triggerStates, ++depth);
 191         } else {
 192              Styleable parent = styleable.getStyleableParent();
 193             while (parent != null) {
 194                 boolean answer = applies(parent, index - 1, triggerStates, ++depth);
 195                 // If a call to stateMatches succeeded, then we know that
 196                 // all preceding selectors will have also matched.
 197                 if (answer) return true;
 198                 // Otherwise we need to get the next parent and try again
 199                 parent = parent.getStyleableParent();
 200             }
 201         }
 202         return false;
 203     }
 204 
 205     @Override public boolean stateMatches(final Styleable styleable, Set<PseudoClass> states) {

 206         return stateMatches(styleable, states, selectors.size()-1);
 207     }
 208 
 209     private boolean stateMatches(Styleable styleable, Set<PseudoClass> states, int index) {
 210         // If the index is < 0 then we know we don't match
 211         if (index < 0) return false;
 212 
 213         // Simply check the selector associated with this index and see if it
 214         // matches the Node and states provided.
 215         if (! selectors.get(index).stateMatches(styleable, states)) return false;
 216 
 217         // If there are no more selectors to match (ie: index == 0) then we
 218         // know we have successfully matched
 219         if (index == 0) return true;
 220 
 221         // We have not yet checked all the selectors in this CompoundSelector,
 222         // so now we need to find the next parent and try again. If the
 223         // relationship between this selector and its ancestor selector is
 224         // "CHILD" then it is required that the parent scenegraph node match
 225         // the ancestor selector. Otherwise, we just walk up the scenegraph


 238             }
 239         } else {
 240             Styleable parent = styleable.getStyleableParent();
 241             while (parent != null) {
 242                 if (selectors.get(index-1).applies(parent)) { 
 243                     Set<PseudoClass> parentStates = parent.getPseudoClassStates();
 244                     return stateMatches(parent, parentStates, index - 1);
 245                 }
 246                 // Otherwise we need to get the next parent and try again
 247                 parent = parent.getStyleableParent();
 248             }
 249         }
 250 
 251         return false;
 252     }
 253 
 254     private  int hash = -1;
 255 
 256     /* Hash code is used in Style's hash code and Style's hash
 257        code is used by StyleHelper */
 258     @Override public int hashCode() {

 259         if (hash == -1) {
 260             for (int i = 0, max=selectors.size(); i<max; i++) 
 261                 hash = 31 * (hash + selectors.get(i).hashCode());
 262             for (int i = 0, max=relationships.size(); i<max; i++) 
 263                 hash = 31 * (hash + relationships.get(i).hashCode());
 264         }
 265         return hash;
 266     }
 267 
 268     @Override public boolean equals(Object obj) {

 269         if (obj == null) {
 270             return false;
 271         }
 272         if (getClass() != obj.getClass()) {
 273             return false;
 274         }
 275         final CompoundSelector other = (CompoundSelector) obj;
 276         if (other.selectors.size() != selectors.size()) return false;
 277         // Avoid ArrayList equals since it uses enhanced for loop
 278         for (int i = 0, max=selectors.size(); i<max; i++) {
 279             if (!other.selectors.get(i).equals(selectors.get(i))) return false;
 280         }
 281         // Avoid ArrayList equals since it uses enhanced for loop
 282         if (other.relationships.size() != relationships.size()) return false;
 283         for (int i = 0, max=relationships.size(); i<max; i++) {
 284             if (!other.relationships.get(i).equals(relationships.get(i))) return false;
 285         }
 286         return true;
 287     }
 288 
 289     @Override public String toString() {
 290         StringBuilder sbuf = new StringBuilder(); 
 291         sbuf.append(selectors.get(0));
 292         for(int n=1; n<selectors.size(); n++) {
 293             sbuf.append(relationships.get(n-1));
 294             sbuf.append(selectors.get(n));
 295         }
 296         return sbuf.toString();
 297     }
 298 
 299     @Override protected final void writeBinary(final DataOutputStream os, final StyleConverter.StringStore stringStore)
 300             throws IOException
 301     {
 302         super.writeBinary(os, stringStore);
 303         os.writeShort(selectors.size());
 304         for (int n=0; n< selectors.size(); n++) selectors.get(n).writeBinary(os,stringStore);
 305         os.writeShort(relationships.size());
 306         for (int n=0; n< relationships.size(); n++) os.writeByte(relationships.get(n).ordinal());
 307     }
 308 
 309     static CompoundSelector readBinary(int bssVersion, final DataInputStream is, final String[] strings)
 310             throws IOException
 311     {
 312 
 313         final int nSelectors = is.readShort();
 314         final List<SimpleSelector> selectors = new ArrayList<SimpleSelector>();
 315         for (int n=0; n<nSelectors; n++) {
 316             selectors.add((SimpleSelector)Selector.readBinary(bssVersion, is,strings));
 317         }
 318 
 319         final int nRelationships = is.readShort();
 320 
 321         final List<Combinator> relationships = new ArrayList<Combinator>();
 322         for (int n=0; n<nRelationships; n++) {
 323             final int ordinal = is.readByte();
 324             if (ordinal == Combinator.CHILD.ordinal()) 
 325                 relationships.add(Combinator.CHILD);
 326             else if (ordinal == Combinator.DESCENDANT.ordinal()) 
 327                 relationships.add(Combinator.DESCENDANT);
 328             else {
 329                 assert false : "error deserializing CompoundSelector: Combinator = " + ordinal;