changeset 3823:0e8bd17417d3

RT-30381: change selector matching to return list of matching selectors rather than the rules of matching selectors
author David Grieve<david.grieve@oracle.com>
date Tue, 14 May 2013 12:48:23 -0700
parents a1348a50b9ef
children 2141df51b9c5
files javafx-ui-common/src/com/sun/javafx/css/CascadingStyle.java javafx-ui-common/src/com/sun/javafx/css/Match.java javafx-ui-common/src/com/sun/javafx/css/Rule.java javafx-ui-common/src/com/sun/javafx/css/Selector.java javafx-ui-common/src/com/sun/javafx/css/SelectorPartitioning.java javafx-ui-common/src/com/sun/javafx/css/SimpleSelector.java javafx-ui-common/src/com/sun/javafx/css/Style.java javafx-ui-common/src/com/sun/javafx/css/StyleCache.java javafx-ui-common/src/com/sun/javafx/css/StyleManager.java javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java javafx-ui-common/src/javafx/scene/doc-files/cssref.html javafx-ui-common/test/unit/com/sun/javafx/css/SelectorPartitioningTest.java
diffstat 12 files changed, 207 insertions(+), 295 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/com/sun/javafx/css/CascadingStyle.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/CascadingStyle.java	Tue May 14 12:48:23 2013 -0700
@@ -44,7 +44,7 @@
     /** State variables, like &quot;hover&quot; or &quot;pressed&quot; */
     private Set<PseudoClass> pseudoClasses;
 
-    /* specificity of the rule that matched */
+    /* specificity of the selector that matched */
     private final int specificity;
     
     /* order in which this style appeared in the stylesheet */
--- a/javafx-ui-common/src/com/sun/javafx/css/Match.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/Match.java	Tue May 14 12:48:23 2013 -0700
@@ -31,7 +31,7 @@
 
 
 /**
- * Used by {@link Rule} to determine whether or not the rule applies to a
+ * Used by {@link Rule} to determine whether or not the selector applies to a
  * given object.
  *
 /**
--- a/javafx-ui-common/src/com/sun/javafx/css/Rule.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/Rule.java	Tue May 14 12:48:23 2013 -0700
@@ -42,11 +42,35 @@
 import javafx.scene.Scene;
 
 /*
- * A rule is a collection of selectors and declarations.
+ * A selector is a collection of selectors and declarations.
  */
 final public class Rule {
 
-    final List<Selector> selectors;
+    final ObservableList<Selector> selectors =
+            new TrackableObservableList<Selector>() {
+
+                @Override
+                protected void onChanged(Change<Selector> c) {
+                    while (c.next()) {
+                        if (c.wasAdded()) {
+                            List<Selector> added = c.getAddedSubList();
+                            for(int i = 0, max = added.size(); i < max; i++) {
+                                Selector sel = added.get(i);
+                                sel.setRule(Rule.this);
+                            }
+                        }
+
+                        if (c.wasRemoved()) {
+                            List<Selector> removed = c.getAddedSubList();
+                            for(int i = 0, max = removed.size(); i < max; i++) {
+                                Selector sel = removed.get(i);
+                                if (sel.getRule() == Rule.this) sel.setRule(null);
+                            }
+                        }
+                    }
+                }
+            };
+
     public List<Selector> getSelectors() {
         return selectors;
     }
@@ -88,7 +112,7 @@
         return declarations;
     }
 
-    /** The stylesheet this rule belongs to */
+    /** The stylesheet this selector belongs to */
     private Stylesheet stylesheet;
     public Stylesheet getStylesheet() {
         return stylesheet;
@@ -113,18 +137,14 @@
 
 
     public Rule(List<Selector> selectors, List<Declaration> declarations) {
-        this.selectors = selectors;
-        this.declarations.addAll(declarations);
-    }
-
-    private Rule() {
-        this(null, null);
+        this.selectors.setAll(selectors);
+        this.declarations.setAll(declarations);
     }
 
     /**
      * Checks all selectors for a match, returning an array of all relevant
      * matches.  A match is considered irrelevant if its presence or absence
-     * cannot affect whether or not the rule applies;  this means that among
+     * cannot affect whether or not the selector applies;  this means that among
      * static (non-pseudoclass) matches, only the highest priority one is
      * relevant, and among pseudoclass matches, only ones with higher priority
      * than the most specific static match are relevant.
--- a/javafx-ui-common/src/com/sun/javafx/css/Selector.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/Selector.java	Tue May 14 12:48:23 2013 -0700
@@ -37,7 +37,7 @@
 import javafx.scene.Node;
 
 /**
- * Used by CSSRule to determine whether or not the rule applies to a 
+ * Used by CSSRule to determine whether or not the selector applies to a
  * given object.
  *
  */
@@ -51,7 +51,14 @@
     public static Selector getUniversalSelector() {
         return UniversalSelector.INSTANCE;
     }
-    
+
+    private Rule rule;
+    void setRule(Rule rule) {
+        this.rule = rule;
+    }
+    Rule getRule() {
+        return rule;
+    }
     /**
      * Determines whether this selector applies to the specified object.  
      * Returns a {@link Match} on success, <code>null</code> otherwise. Note
--- a/javafx-ui-common/src/com/sun/javafx/css/SelectorPartitioning.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/SelectorPartitioning.java	Tue May 14 12:48:23 2013 -0700
@@ -25,7 +25,6 @@
 
 package com.sun.javafx.css;
 
-import java.lang.reflect.Array;
 import java.util.*;
 
 /**
@@ -35,107 +34,7 @@
 
     /** package accessible */
     SelectorPartitioning() {}
-    
-    /*
-     * SimpleSelector uses long[] for StyleClass. This wraps the long[]
-     * so that it can be used as a key in the selector maps.
-    */
-//    private final static class StyleClassKey {
-//        
-//        private final long[] bits;
-//        private final static long[] EMPTY_BITS = new long[0];
-//        private final static StyleClassKey WILDCARD = new StyleClassKey(EMPTY_BITS);
-//        
-//        private StyleClassKey(long[] bits) {
-//            this.bits = (bits != null) ? new long[bits.length] : EMPTY_BITS;
-//            System.arraycopy(bits, 0, this.bits, 0, bits.length);
-//        }
-//        
-//        /*
-//         * Test that all the bits of this key are present in the other. Given
-//         * <pre>
-//         * {@code
-//         * Key k1 = new Key(0x5);
-//         * Key k2 = new Key(0x4);}</pre> {@code k1.isSubset(k2) } returns false
-//         * and {@code k2.isSubset(k1) } returns true; <p> Note that one cannot
-//         * assume if {@code x.isSubsetOf(y) == false }
-//         * that {@code y.isSuperSetOf(x) == true } since <i>x</i> and <i>y</i>
-//         * might be disjoint.
-//         *
-//         * @return true if this Key is a subset of the other.
-//         */
-//        private boolean isSubsetOf(StyleClassKey other) {
-//        
-//            // they are a subset if both are empty
-//            if (bits.length == 0 && other.bits.length == 0) return true;
-//
-//            // [foo bar] cannot be a subset of [foo]
-//            if (bits.length > other.bits.length) return false;
-//
-//            // is [foo bar] a subset of [foo bar bang]?
-//            for (int n=0, max=bits.length; n<max; n++) {
-//                if ((bits[n] & other.bits[n]) != bits[n]) return false;
-//            }
-//            return true;
-//
-//        }
-//
-//        /*
-//         * return true if this seq1 is a superset of seq2. That is to say
-//         * that all the bits seq2 of the other key are present in seq1. Given
-//         * <pre>
-//         * {@code
-//         * Key k1 = new Key(0x5);
-//         * Key k2 = new Key(0x4);}</pre> {@code k1.isSupersetOf(k2) } returns
-//         * true and {@code k2.isSupersetOf(k1) } returns false <p> Note that one
-//         * cannot assume if {@code x.isSubsetOf(y) == false }
-//         * that {@code y.isSuperSetOf(x) == true } since <i>x</i> and <i>y</i>
-//         * might be disjoint.
-//         */
-//        private boolean isSupersetOf(StyleClassKey other) {
-//            
-//            // [foo] cannot be a superset of [foo  bar]
-//            // nor can [foo] be a superset of [foo]
-//            if (bits.length < other.bits.length) return false;
-//
-//            // is [foo bar bang] a superset of [foo bar]?
-//            for (int n=0, max=other.bits.length; n<max; n++) {
-//                if ((bits[n] & other.bits[n]) != other.bits[n]) return false;
-//            }
-//            return true;
-//
-//        }
-//        
-//        //
-//        // Need hashCode since StyleClassKey is used as a key in a Map.
-//        // 
-//        @Override
-//        public int hashCode() {
-//            int hash = 7;
-//            hash = 41 * hash + Arrays.hashCode(this.bits);
-//            return hash;
-//        }
-//
-//        //
-//        // Need equals since StyleClassKey is used as a key in a Map.
-//        // 
-//        @Override
-//        public boolean equals(Object obj) {
-//            if (obj == null) {
-//                return false;
-//            }
-//            if (getClass() != obj.getClass()) {
-//                return false;
-//            }
-//            final StyleClassKey other = (StyleClassKey) obj;
-//            if (!Arrays.equals(this.bits, other.bits)) {
-//                return false;
-//            }
-//            return true;
-//        }
-//        
-//    }
-    
+
     /*
      * Wrapper so that we can have Map<ParitionKey, Partition> even though
      * the innards of the key might be a String or long[]
@@ -173,25 +72,25 @@
     }   
     
     /** 
-     * Since Rules are retrieved from the tree in an indeterminate manner,
-     * we need to keep track of the order in which the rule was added. This
-     * can't be kept in Rule itself since the order depends on both the
-     * order of the stylesheets and the order of the rules in the stylesheets.
-     * Also, rules can be added and removed from the stylesheet which would
+     * Since matched selectors are retrieved from the tree in an indeterminate manner,
+     * we need to keep track of the order in which the selector was added. This
+     * can't be kept in Selector itself since the order depends on both the
+     * order of the stylesheets and the order of the selectors in the stylesheets.
+     * Also, selectors can be added and removed from the stylesheet which would
      * invalidate the order.
      */
-    private static class RuleData implements Comparable {
-        final Rule rule;
+    static final class SelectorData implements Comparable {
+        final Selector selector;
         final int ordinal;
         
-        private RuleData(Rule rule, int ordinal) {
-            this.rule = rule;
+        private SelectorData(Selector selector, int ordinal) {
+            this.selector = selector;
             this.ordinal = ordinal;
         }
 
         @Override
         public int compareTo(Object t) {
-            return ordinal - ((RuleData)t).ordinal;
+            return ordinal - ((SelectorData)t).ordinal;
         }
     }
     
@@ -207,38 +106,32 @@
      * for A in Partition #c would now have Slots for both .b and .z.
      * <p> 
      * Rules are added to the last Slot or to the Partition. If there is a 
-     * rule #c { -fx-fill: red; }, then the rule will be added to the
-     * Partition for #c. If the rule were for A.b#c, then rule would be added
+     * selector #c { -fx-fill: red; }, then the selector will be added to the
+     * Partition for #c. If the selector were for A.b#c, then selector would be added
      * to the slot for '.b' which is in the slot for A in partion #c. 
      * <p>
-     * When Node is matched, it picks up the Rules from the Partition and Slot 
+     * When Node is matched, it picks up the Selectors from the Partition and Slot
      * as the graph is traversed. 
      */
     private static final class Partition {
         
         private final PartitionKey key;
         private final Map<PartitionKey, Slot> slots;
-        private List<RuleData> rules;
+        private List<SelectorData> selectors;
         
         private Partition(PartitionKey key) {
            this.key = key;
             slots = new HashMap<PartitionKey,Slot>();
         }
-        
-        private void addRule(Rule rule, int ordinal) {
-            if (rules == null) {
-                rules = new ArrayList<RuleData>();
+
+        private void addSelectorData(SelectorData selectorData) {
+            if (selectors == null) {
+                selectors = new ArrayList<SelectorData>();
             }
-            rules.add(new RuleData(rule,ordinal));
+            selectors.add(selectorData);
         }
-        
-        private void copyRules(List<RuleData> to) {
-            if (rules != null && rules.isEmpty() == false) {
-                to.addAll(rules);
-            }
-        }
-        
-        /**   
+
+        /**
          * This routine finds the slot corresponding to the PartitionKey, 
          * creating a Partition and Slot if necessary. 
          */
@@ -267,29 +160,22 @@
         // The other Slots to which this Slot refers
         private final Map<PartitionKey, Slot> referents;
 
-        // Rules from selectors that matches the path to this slot
-        private List<RuleData> rules;
+        // Selectors that match the path to this slot
+        private List<SelectorData> selectors;
         
         private Slot(Partition partition) {
             this.partition = partition;
             this.referents = new HashMap<PartitionKey, Slot>();            
         }
         
-        private void addRule(Rule rule, int ordinal) {
-            if (rules == null) {
-                rules = new ArrayList<RuleData>();
+        private void addSelectorData(SelectorData selectorData) {
+            if (selectors == null) {
+                selectors = new ArrayList<SelectorData>();
             }
-            rules.add(new RuleData(rule,ordinal));
+            selectors.add(selectorData);
         }
-        
-        private void copyRules(List<RuleData> to) {
-            if (rules != null && rules.isEmpty() == false) {
-                to.addAll(rules);
-            }
-            partition.copyRules(to);
-        }
-                     
-        /**   
+
+        /**
          * This routine finds the slot corresponding to the PartitionKey, 
          * creating a Partition and Slot if necessary. 
          */
@@ -317,7 +203,7 @@
     private Map<PartitionKey, Partition> styleClassMap = new HashMap<PartitionKey,Partition>();
 
     /** 
-     * Keep track of the order in which a rule is added to the mapping so
+     * Keep track of the order in which a selector is added to the mapping so
      * the original order can be restored for the cascade.
      */
     private int ordinal;
@@ -355,7 +241,7 @@
     private static final PartitionKey WILDCARD = new PartitionKey<String>("*");
     
     /* Place this selector into the partitioning map. Package accessible */
-    void partition(Selector selector, Rule rule) {
+    void partition(Selector selector) {
         
         SimpleSelector simpleSelector = null;
         if (selector instanceof CompoundSelector) {
@@ -393,6 +279,8 @@
         Partition partition = null;
         Slot slot = null;
 
+        final SelectorData selectorData = new SelectorData(selector,ordinal++);
+
         switch(c) {
             case ID_BIT | TYPE_BIT | STYLECLASS_BIT: 
             case ID_BIT | TYPE_BIT: 
@@ -402,7 +290,7 @@
                 if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                     slot = slot.partition(styleClassKey, styleClassMap);
                 }                
-                slot.addRule(rule, ordinal++);
+                slot.addSelectorData(selectorData);
                 break;
                 
             case TYPE_BIT | STYLECLASS_BIT:
@@ -411,9 +299,9 @@
                 partition = getPartition(typeKey, typeMap);
                 if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                     slot = partition.partition(styleClassKey, styleClassMap);
-                    slot.addRule(rule, ordinal++);
+                    slot.addSelectorData(selectorData);
                 } else {
-                    partition.addRule(rule, ordinal++);                    
+                    partition.addSelectorData(selectorData);
                 }
                 break;
                 
@@ -427,8 +315,8 @@
         
     }
     
-    /** Get the list of rules that match this selector. Package accessible */
-    List<Rule> match(String selectorId, String selectorType, Set<StyleClass> selectorStyleClass) {
+    /** Get the list of selectors that match this selector. Package accessible */
+    List<SelectorData> match(String selectorId, String selectorType, Set<StyleClass> selectorStyleClass) {
         
         final boolean hasId = 
             (selectorId != null && selectorId.isEmpty() == false);
@@ -453,7 +341,7 @@
 
         Partition partition = null;
         Slot slot = null;
-        List<RuleData> ruleData = new ArrayList<RuleData>();
+        List<SelectorData> selectorData = new ArrayList<SelectorData>();
         
         while (c != 0) {
             
@@ -464,8 +352,9 @@
 
                     partition = idMap.get(idKey);
                     if (partition != null) {
-                        partition.copyRules(ruleData);
-
+                        if (partition.selectors != null) {
+                            selectorData.addAll(partition.selectors);
+                        }
                         // do-while handles A.b#c also matches A#c by first
                         // doing A.b#c then doing *.b#c
                         PartitionKey typePK = typeKey;
@@ -473,14 +362,16 @@
                             slot = partition.slots.get(typePK);                    
                             if (slot != null) {
 
-                                slot.copyRules(ruleData);
-
+                                if (slot.selectors != null) {
+                                    selectorData.addAll(slot.selectors);
+                                }
                                 if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                                     Set<StyleClass> key = (Set<StyleClass>)styleClassKey.key;
                                     for (Slot s : slot.referents.values()) {
+                                        if (s.selectors == null || s.selectors.isEmpty()) continue;;
                                         Set<StyleClass> other = (Set<StyleClass>)s.partition.key.key;
                                         if (key.containsAll(other)) {
-                                            s.copyRules(ruleData);
+                                            selectorData.addAll(s.selectors);
                                         }
                                     }
                                 }
@@ -513,14 +404,16 @@
                     do {
                         partition = typeMap.get(typePK);
                         if (partition != null) {
-                            partition.copyRules(ruleData);
-
+                            if (partition.selectors != null) {
+                                selectorData.addAll(partition.selectors);
+                            }
                             if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                                 Set<StyleClass> key = (Set<StyleClass>)styleClassKey.key;
                                 for (Slot s : partition.slots.values()) {
+                                    if (s.selectors == null || s.selectors.isEmpty()) continue;
                                     Set<StyleClass> other = (Set<StyleClass>)s.partition.key.key;
                                     if (key.containsAll(other)) {
-                                        s.copyRules(ruleData);
+                                        selectorData.addAll(s.selectors);
                                     }
                                 }
                             }
@@ -543,16 +436,7 @@
                     assert(false);
             }
         }
-        
-        Collections.sort(ruleData);
-        
-        // TODO: Unfortunate copy :(
-        List<Rule> rules = new ArrayList<Rule>(ruleData.size());
-        for (int r=0,rMax=ruleData.size(); r<rMax; r++) {
-            final RuleData datum = ruleData.get(r);
-            rules.add(datum.rule);
-        }
-        return rules;
+        return selectorData;
     }
 
 }
--- a/javafx-ui-common/src/com/sun/javafx/css/SimpleSelector.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/SimpleSelector.java	Tue May 14 12:48:23 2013 -0700
@@ -345,13 +345,13 @@
     // Are the Selector's style classes a subset of the Node's style classes?
     //
     // http://www.w3.org/TR/css3-selectors/#class-html
-    // The following rule matches any P element whose class attribute has been
+    // The following selector matches any P element whose class attribute has been
     // assigned a list of whitespace-separated values that includes both
     // pastoral and marine:
     //
     //     p.pastoral.marine { color: green }
     //
-    // This rule matches when class="pastoral blue aqua marine" but does not
+    // This selector matches when class="pastoral blue aqua marine" but does not
     // match for class="pastoral blue".
     private boolean matchStyleClasses(StyleClassSet otherStyleClasses) {
         return otherStyleClasses.containsAll(styleClassSet);
--- a/javafx-ui-common/src/com/sun/javafx/css/Style.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/Style.java	Tue May 14 12:48:23 2013 -0700
@@ -32,7 +32,7 @@
 final public class Style {
     
     /**
-     * A rule might have more than one selector. This is the one that was
+     * A selector might have more than one selector. This is the one that was
      * matched.
      */
     public Selector getSelector() {
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleCache.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleCache.java	Tue May 14 12:48:23 2013 -0700
@@ -38,7 +38,7 @@
  * to be unique to the set of StyleHelpers [A B C]. Because StyleHelpers
  * are chosen by the rules they match, and because StyleHelpers are shared,
  * every node that has the set of StyleHelpers [A B C] will match the same
- * rule for opacity (for a given pseudoclass state). Further, the value for
+ * selector for opacity (for a given pseudoclass state). Further, the value for
  * opacity (in the given pseudoclass state) will not change for the given
  * set of StyleHelpers. Therefore, rather than trying to cache a calculated
  * value with an individual StyleHelper, the value can be cached with a key
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Tue May 14 12:48:23 2013 -0700
@@ -85,18 +85,18 @@
  * is a two level cache. The first level cache simply maps the
  * classname/id/styleclass combination of the request node to a 2nd level cache.
  * If the node has "styles" specified then we still use this 2nd level cache,
- * but must combine its rules with the rules specified in "styles" and perform
+ * but must combine its selectorData with the selectorData specified in "styles" and perform
  * more work to cascade properly. <p> The 2nd level cache contains a data
  * structure called the Cache. The Cache contains an ordered sequence of Rules,
- * a Long, and a Map. The ordered sequence of rules are the rules that *may*
+ * a Long, and a Map. The ordered sequence of selectorData are the selectorData that *may*
  * match a node with the given classname, id, and style class. For example,
- * rules which may apply are any rule where the simple selector of the rule
+ * selectorData which may apply are any selector where the simple selector of the selector
  * contains a reference to the id, style class, or classname of the Node, or a
  * compound selector who's "descendant" part is a simple selector which contains
  * a reference to the id, style class, or classname of the Node. <p> During
- * lookup, we will iterate over all the potential rules and discover if they
+ * lookup, we will iterate over all the potential selectorData and discover if they
  * apply to this particular node. If so, then we toggle a bit position in the
- * Long corresponding to the position of the rule that matched. This long then
+ * Long corresponding to the position of the selector that matched. This long then
  * becomes our key into the final map. <p> Once we have established our key, we
  * will visit the map and look for an existing StyleHelper. If we find a
  * StyleHelper, then we will return it. If not, then we will take the Rules that
@@ -264,7 +264,7 @@
                     for (int s=0; s < sMax; s++) {
 
                         final Selector selector = selectors.get(s);
-                        selectorPartitioning.partition(selector, rule);
+                        selectorPartitioning.partition(selector);
 
                     }
                 }
@@ -1361,8 +1361,8 @@
             // If the cache is null, then we need to create a new Cache and
             // add it to the cache map
             
-            // Construct the list of Rules that could possibly apply
-            final List<Rule> rules = new ArrayList<Rule>();
+            // Construct the list of Selectors that could possibly apply
+            final List<SelectorPartitioning.SelectorData> selectorData = new ArrayList<>();
 
             // User agent stylesheets have lowest precedence and go first
             if (userAgentStylesheets.isEmpty() == false) {
@@ -1370,9 +1370,9 @@
                     final StylesheetContainer container = userAgentStylesheets.get(n);
                     
                     if (container != null && container.selectorPartitioning != null) {
-                        final List<Rule> matchingRules = 
+                        final List<SelectorPartitioning.SelectorData> matchingRules =
                                 container.selectorPartitioning.match(id, cname, key.styleClasses);
-                        rules.addAll(matchingRules);
+                        selectorData.addAll(matchingRules);
                     }
                 }
             }
@@ -1384,9 +1384,9 @@
                     final StylesheetContainer container = sceneStylesheets.get(n);
                     if (container != null && container.selectorPartitioning != null) {
                         container.keys.add(key); // remember that this stylesheet was used in this cache
-                        final List<Rule> matchingRules = 
+                        final List<SelectorPartitioning.SelectorData> matchingRules =
                                 container.selectorPartitioning.match(id, cname, key.styleClasses);
-                        rules.addAll(matchingRules);
+                        selectorData.addAll(matchingRules);
                     }
                 }
             }
@@ -1398,15 +1398,15 @@
                     final StylesheetContainer container = parentStylesheets.get(n);
                     container.keys.add(key); // remember that this stylesheet was used in this cache
                     if (container.selectorPartitioning != null) {
-                        final List<Rule> matchingRules = 
+                        final List<SelectorPartitioning.SelectorData> matchingRules =
                                 container.selectorPartitioning.match(id, cname, key.styleClasses);
-                        rules.addAll(matchingRules);
+                        selectorData.addAll(matchingRules);
                     }
                 }
             }
             
-            // create a new Cache from these rules.
-            cache = new Cache(rules);   
+            // create a new Cache from these selectorData.
+            cache = new Cache(selectorData);
             cacheMap.put(key, cache);
             
             // cause a new Key to be created the next time this method is called
@@ -1605,84 +1605,70 @@
             }
             
         }
-        // this must be initialized to the appropriate possible rules when
+        // this must be initialized to the appropriate possible selectorData when
         // the helper cache is created by the StylesheetContainer
-        private final List<Rule> rules;
+        private final List<SelectorPartitioning.SelectorData> selectorData;
         private final Map<Key, Integer> cache;
 
-        Cache(List<Rule> rules) {
-            this.rules = rules;
+        Cache(List<SelectorPartitioning.SelectorData> selectorData) {
+            this.selectorData = selectorData;
             this.cache = new HashMap<Key, Integer>();
         }
 
         private StyleMap getStyleMap(CacheContainer cacheContainer, Node node, Set<PseudoClass>[] triggerStates) {
             
-            if (rules == null || rules.isEmpty()) {                
+            if (selectorData == null || selectorData.isEmpty()) {
                 return StyleMap.EMPTY_MAP;
             }
 
+            final int selectorDataSize = selectorData.size();
 
             //
-            // Since the list of rules is found by matching only the
-            // rightmost selector, the set of rules may larger than those 
-            // rules that actually match the node. The following loop
-            // whittles the list down to those rules that apply.
+            // Since the list of selectorData is found by matching only the
+            // rightmost selector, the set of selectorData may larger than those
+            // selectorData that actually match the node. The following loop
+            // whittles the list down to those selectorData that apply.
             //
-            final Rule[] applicableRules = new Rule[rules.size()];
             //
             // To lookup from the cache, we construct a key from a Long
             // where the selectors that match this particular node are
-            // represented by bits on the Long.
+            // represented by bits on the long[].
             //
-            long key[] = new long[1];
-            int count = 0;
-            int index = 0;
-            for (int r = 0, rMax = rules.size(); r < rMax; r++) {
+            long key[] = new long[Long.SIZE/selectorDataSize + 1];
+            boolean nothingMatched = true;
+
+            for (int s = 0; s < selectorDataSize; s++) {
                 
-                final Rule rule = rules.get(r);
-
                 //
                 // This particular flavor of applies takes a PseudoClassState[]
-                // fills in the pseudo-class states from the selectors where 
+                // fills in the pseudo-class states from the selectors where
                 // they apply to a node. This is an expedient to looking the
-                // applies loopa second time on the matching rules. This has to
+                // applies loopa second time on the matching selectorData. This has to
                 // be done ahead of the cache lookup since not all nodes that
-                // have the same set of rules will have the same node hierarchy. 
-                // 
+                // have the same set of selectorData will have the same node hierarchy.
+                //
                 // For example, if I have .foo:hover:focused .bar:selected {...}
                 // and the "bar" node is 4 away from the root and the foo
                 // node is two away from the root, pseudoclassBits would be
                 // [selected, 0, hover:focused, 0]
                 // Note that the states run from leaf to root. This is how
-                // the code in StyleHelper expects things. 
-                // Note also that, if the rule does not apply, the triggerStates
-                // is unchanged. 
+                // the code in StyleHelper expects things.
+                // Note also that, if the selector does not apply, the triggerStates
+                // is unchanged.
                 //
-                
-                final int nSelectors = rule.getSelectors().size();
-                if ((count + nSelectors) > Long.SIZE) {
-                    final long[] temp = new long[key.length+1];
-                    System.arraycopy(key, 0, temp, 0, key.length);
-                    key = temp;
-                    ++index;
-                    count = 0;
+                final SelectorPartitioning.SelectorData selectorDatum = selectorData.get(s);
+                final Selector sel = selectorDatum.selector;
+
+                if (sel.applies(node, triggerStates, 0)) {
+                    final int index = s / Long.SIZE;
+                    final long mask = key[index] | 1l << s;
+                    key[index] = mask;
+                    nothingMatched = false;
                 }
-                
-                long mask = rule.applies(node, triggerStates);
-                
-                if (mask != 0) {
-                    key[index] |= mask << count;
-                    applicableRules[r] = rule;
-                } else {
-                    applicableRules[r] = null;
-                }
-                
-                count += rule.getSelectors().size(); 
-                
             }
             
             // nothing matched!
-            if (key.length == 1 && key[0] == 0) {
+            if (nothingMatched) {
                 return StyleMap.EMPTY_MAP;
             }
             
@@ -1693,54 +1679,67 @@
                 return styleMap;
             }
 
-            int ordinal = 0;
-            
             // if there isn't a map in cache already, create one. 
             // 
-            // We know the rules apply, so they should also match. A rule
+            // We know the selectorData apply, so they should also match. A selector
             // might have more than one selector and match will return the
             // selector that matches. Matches is more expensive than applies, 
             // so we pay for it here, but only for the first time the 
             // cache is created.
             //
             final List<CascadingStyle> styles = new ArrayList<CascadingStyle>();
-            
-            for (int r = 0, rMax = applicableRules.length; r<rMax; r++) {
-                
-                final Rule rule = applicableRules[r];
-                
-                // if the rule didn't apply, then applicableRules[r] will be null
-                if (rule == null) {
-                    continue;
-                }
-                
-                final List<Match> matches = rule.matches(node);
-                if (matches == null || matches.isEmpty()) {
-                    continue;
-                }
-                
-                for (int m=0, mMax=matches.size(); m<mMax; m++) {
-                
-                    final Match match = matches.get(m);
-                    // TODO: should never get nulls in this list. Fix Rule#matches
-                    if (match == null) {
+
+            for (int k = 0; k<key.length; k++) {
+
+                if (key[k] == 0) continue;
+
+                final int offset = k * Long.SIZE;
+
+                for (int b = 0; b<Long.SIZE; b++) {
+
+                    // bit at b in key[k] set?
+                    final long mask = 1l << b;
+                    if ((mask & key[k]) != mask) continue;
+
+                    final SelectorPartitioning.SelectorData selectorDatum = selectorData.get(offset+b);
+                    int ordinal = selectorDatum.ordinal;
+                    final Selector selector = selectorDatum.selector;
+
+                    final Rule rule = selector.getRule();
+
+                    // if the selector didn't apply, then applicableRules[r] will be null
+                    if (rule == null) {
                         continue;
                     }
-                    
-                    for (int k = 0, kmax = rule.declarations.size(); k < kmax; k++) {
-                        final Declaration decl = rule.declarations.get(k);
 
-                        final CascadingStyle s = new CascadingStyle(
-                            new Style(match.selector, decl),
-                            match.pseudoClasses,
-                            match.specificity,
-                            // ordinal increments at declaration level since
-                            // there may be more than one declaration for the
-                            // same attribute within a rule or within a stylesheet
-                            ordinal++
-                        );
+                    final List<Match> matches = rule.matches(node);
+                    if (matches == null || matches.isEmpty()) {
+                        continue;
+                    }
 
-                        styles.add(s);
+                    for (int m=0, mMax=matches.size(); m<mMax; m++) {
+
+                        final Match match = matches.get(m);
+                        // TODO: should never get nulls in this list. Fix Rule#matches
+                        if (match == null) {
+                            continue;
+                        }
+
+                        for (int d = 0, dmax = rule.declarations.size(); d < dmax; d++) {
+                            final Declaration decl = rule.declarations.get(d);
+
+                            final CascadingStyle s = new CascadingStyle(
+                                    new Style(match.selector, decl),
+                                    match.pseudoClasses,
+                                    match.specificity,
+                                    // ordinal increments at declaration level since
+                                    // there may be more than one declaration for the
+                                    // same attribute within a selector or within a stylesheet
+                                    ordinal++
+                            );
+
+                            styles.add(s);
+                        }
                     }
                 }
             }
--- a/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java	Tue May 14 12:48:23 2013 -0700
@@ -37,7 +37,7 @@
 /**
  * A stylesheet which can apply properties to a tree of objects.  A stylesheet 
  * is a collection of zero or more {@link Rule Rules}, each of which is applied 
- * to each object in the tree.  Typically the rule will examine the object to 
+ * to each object in the tree.  Typically the selector will examine the object to
  * determine whether or not it is applicable, and if so it will apply certain 
  * property values to the object.
  * <p>
--- a/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Tue May 14 12:48:23 2013 -0700
@@ -433,9 +433,9 @@
       default user agent style sheet is loaded. Inline styles are specified via
       the Node <span style="font-weight: bold;">setStyle</span> API. Inline
       styles are analogous to the style="..." attribute of an HTML element.
-      Styles loaded from a Scene's style sheets take precedence over rules from
+      Styles loaded from a Scene's style sheets take precedence over selectorData from
       the user agent style sheet. Inline styles take&nbsp; precedence over
-      styles originating elsewhere. The precedence order of style rules can be
+      styles originating elsewhere. The precedence order of style selectorData can be
       modified using "!important" in a style declaration.&nbsp;</p>
     <p>Beginning with JavaFX 2.1, the Parent class has a stylesheets property,
       allowing style sheets to be set on a container. This allows for one branch
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/SelectorPartitioningTest.java	Wed May 29 16:37:39 2013 -0400
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/SelectorPartitioningTest.java	Tue May 14 12:48:23 2013 -0700
@@ -187,7 +187,7 @@
                 
         for (Rule rule : stylesheet.getRules()) {
             for (Selector selector : rule.selectors) {
-                instance.partition(selector, rule);                
+                instance.partition(selector);
             }
         }
         
@@ -202,11 +202,13 @@
                 
         SimpleSelector simple = simpleData.selector;
         
-        List<Rule> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
+        List<SelectorPartitioning.SelectorData> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
         
         assertEquals(1,matched.size());
-        Rule rule = matched.get(0);
-        
+        SelectorPartitioning.SelectorData selectorDatum = matched.get(0);
+
+        Rule rule = selectorDatum.selector.getRule();
+
         assertEquals(1,rule.declarations.size());
         Declaration decl = rule.declarations.get(0);
         
@@ -224,11 +226,11 @@
                 
         SimpleSelector simple = complexData.selector;
         
-        List<Rule> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
+        List<SelectorPartitioning.SelectorData> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
         assertEquals(complexData.matches, matched.size());
         
-        for(Rule rule : matched) {
-            Selector s1 = rule.selectors.get(0);
+        for(SelectorPartitioning.SelectorData selectorDatum : matched) {
+            Selector s1 = selectorDatum.selector;
             for (SimpleData datum : complexData.data) {
                 Selector s2 = (SimpleSelector)datum.selector;
                 if (s1.equals(s2)) {