changeset 3844:40836a4b4d85 8.0-b93

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/8.0/MASTER/jfx/rt
author David Grieve<david.grieve@oracle.com>
date Wed, 05 Jun 2013 08:08:10 -0400
parents ea108dc8da59 1e300104bbc6
children 572b1ec2e971 479da853570b ece9e054a7cd
files build.properties javafx-ui-controls/src/javafx/scene/control/DatePicker.java javafx-ui-controls/src/javafx/scene/control/ListCell.java javafx-ui-controls/src/javafx/scene/control/TableCell.java javafx-ui-controls/src/javafx/scene/control/TableColumnBase.java javafx-ui-controls/src/javafx/scene/control/TreeCell.java javafx-ui-controls/src/javafx/scene/control/TreeTableCell.java
diffstat 45 files changed, 1295 insertions(+), 827 deletions(-) [+]
line wrap: on
line diff
--- a/build.properties	Tue Jun 04 09:43:48 2013 -0700
+++ b/build.properties	Wed Jun 05 08:08:10 2013 -0400
@@ -53,4 +53,4 @@
 
 jfx.build.jdk.version=1.8.0
 jfx.build.jdk.buildnum=92
-jfx.build.jdk.buildnum.min=87
+jfx.build.jdk.buildnum.min=91
--- a/javafx-ui-common/src/com/sun/javafx/css/CascadingStyle.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/CascadingStyle.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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 */
@@ -93,6 +93,8 @@
         return style.getDeclaration().getParsedValueImpl();
     }
     
+    @Override public String toString() { return getProperty(); }
+
     /**
      * When testing equality against another Style, we only care about
      * the property and pseudo-classes. In other words, we only care about
@@ -157,8 +159,9 @@
         final boolean otherImportant = otherDecl != null ? otherDecl.isImportant() : false;
         final Rule otherRule = otherDecl != null ? otherDecl.getRule() : null;
         final StyleOrigin otherSource = rule != null ? otherRule.getOrigin() : null;
-        
+
         int c = 0;
+
         if (this.skinProp && !other.skinProp) {
             c = 1;
         } else if (important != otherImportant) {
--- a/javafx-ui-common/src/com/sun/javafx/css/CompoundSelector.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/CompoundSelector.java	Wed Jun 05 08:08:10 2013 -0400
@@ -98,46 +98,21 @@
         this(null, null);
     }
     
-    /**
-     * Returns a {@link Match} if this selector matches the specified object, or 
-     * <code>null</code> otherwise.
-     *
-     *@param node the object to check for a match
-     *@return a {@link Match} if the selector matches, or <code>null</code> 
-     *      otherwise
-     */
-    @Override
-    Match matches(final Styleable node) {
-        return matches(node, selectors.size()-1);
-    }
+    Match createMatch() {
 
-    private Match matches(final Styleable node, final int index) {
-            
-        final Match descendantMatch = selectors.get(index).matches(node);
-        if (descendantMatch == null || index == 0) {
-            return descendantMatch;
+        final PseudoClassState allPseudoClasses = new PseudoClassState();
+        int idCount = 0;
+        int styleClassCount = 0;
+
+        for(int n=0, nMax=selectors.size(); n<nMax; n++) {
+            Selector sel = selectors.get(n);
+            Match match = sel.createMatch();
+            allPseudoClasses.addAll(match.pseudoClasses);
+            idCount += match.idCount;
+            styleClassCount += match.styleClassCount;
         }
 
-        Styleable parent = node.getStyleableParent();
-        while (parent != null) {
-            final Match ancestorMatch = matches(parent, index-1);
-            if (ancestorMatch != null) {
-
-                final PseudoClassState allPseudoClasses = new PseudoClassState();
-                allPseudoClasses.addAll(ancestorMatch.pseudoClasses);
-                allPseudoClasses.addAll(descendantMatch.pseudoClasses);
-                
-                return new Match(this, 
-                        allPseudoClasses,
-                        ancestorMatch.idCount + descendantMatch.idCount,
-                        ancestorMatch.styleClassCount + descendantMatch.styleClassCount);
-            }
-            // Combinator.CHILD will cause this loop to exit after the first iteration
-            if ( relationships.get(index-1) == Combinator.CHILD ) break;
-            parent = parent.getStyleableParent();
-        }
-        
-        return null;
+        return new Match(this, allPseudoClasses, idCount, styleClassCount);
     }
 
     @Override
@@ -256,19 +231,17 @@
             final Styleable parent = styleable.getStyleableParent();
             if (parent == null) return false;
             if (selectors.get(index-1).applies(parent)) {
-                PseudoClassState parentStates = new PseudoClassState();
-                parentStates.addAll(parent.getPseudoClassStates());
                 // If this call succeeds, then all preceding selectors will have
                 // matched due to the recursive nature of the call
+                Set<PseudoClass> parentStates = parent.getPseudoClassStates();
                 return stateMatches(parent, parentStates, index - 1);
             }
         } else {
             Styleable parent = styleable.getStyleableParent();
             while (parent != null) {
                 if (selectors.get(index-1).applies(parent)) { 
-                    PseudoClassState parentStates = new PseudoClassState();
-                    parentStates.addAll(parent.getPseudoClassStates());
-                    return stateMatches(parent, parentStates, index - 1);
+                    Set<PseudoClass> parentStates = parent.getPseudoClassStates();
+                    return stateMatches(parent, states, index - 1);
                 }
                 // Otherwise we need to get the next parent and try again
                 parent = parent.getStyleableParent();
--- a/javafx-ui-common/src/com/sun/javafx/css/Match.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Match.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Rule.java	Wed Jun 05 08:08:10 2013 -0400
@@ -26,6 +26,8 @@
 package com.sun.javafx.css;
 
 import com.sun.javafx.collections.TrackableObservableList;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -33,77 +35,134 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener.Change;
 import javafx.collections.ObservableList;
 import javafx.css.PseudoClass;
 import javafx.css.StyleOrigin;
 
 import javafx.scene.Node;
-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;
-    public List<Selector> getSelectors() {
+    private List<Selector> selectors = null;
+
+    /**
+     * The list returned from this method should be treated as unmodifiable.
+     * Tools should use {@link #getSelectors()} which tracks adds and removes.
+     */
+    public List<Selector>  getUnobservedSelectorList() {
+        if (selectors == null) {
+            selectors = new ArrayList<Selector>();
+        }
         return selectors;
     }
 
-    final ObservableList<Declaration> declarations = 
-        new TrackableObservableList<Declaration>() {
+    private List<Declaration> declarations = null;
+    /**
+     * The list returned from this method should be treated as unmodifiable.
+     * Tools should use {@link #getDeclarations()} which tracks adds and removes.
+     */
+    public List<Declaration> getUnobservedDeclarationList() {
 
-        @Override
-        protected void onChanged(Change<Declaration> c) {
-            while (c.next()) {
-                if (c.wasAdded()) {
-                    List<Declaration> added = c.getAddedSubList();
-                    for(int i = 0, max = added.size(); i < max; i++) {
-                        Declaration decl = added.get(i);
-                        decl.rule = Rule.this;
-                        
-                        if (stylesheet != null && stylesheet.getUrl() != null) {
+        if (declarations == null && serializedDecls != null) {
 
-                            final URL stylesheetUrl = stylesheet.getUrl();
-                            decl.fixUrl(stylesheetUrl);
+            assert(Rule.strings != null);
 
-                        }
-                        
+            try {
+                ByteArrayInputStream bis = new ByteArrayInputStream(serializedDecls);
+                DataInputStream dis = new DataInputStream(bis);
+
+                short nDeclarations = dis.readShort();
+                declarations = new ArrayList<Declaration>(nDeclarations);
+
+                for (int i = 0; i < nDeclarations; i++) {
+
+                    Declaration decl = Declaration.readBinary(dis, Rule.strings);
+                    decl.rule = Rule.this;
+
+                    if (stylesheet != null && stylesheet.getUrl() != null) {
+                        final URL stylesheetUrl = stylesheet.getUrl();
+                        decl.fixUrl(stylesheetUrl);
                     }
+
+                    declarations.add(decl);
                 }
-                
-                if (c.wasRemoved()) {
-                    List<Declaration> removed = c.getRemoved();
-                    for(int i = 0, max = removed.size(); i < max; i++) {
-                        Declaration decl = removed.get(i);
-                        if (decl.rule == Rule.this) decl.rule = null;
-                    }
-                }
+
+            } catch (IOException ioe) {
+                declarations = new ArrayList<>();
+                assert false; ioe.getMessage();
+
+            } finally {
+                serializedDecls = null;
             }
+
         }
-    };
-    
-    public List<Declaration> getDeclarations() {
+
         return declarations;
     }
 
-    /** The stylesheet this rule belongs to */
+    private Observables observables = null;
+
+    /**
+     * This method is to support tooling that may want to add declarations to
+     * or remove declarations from a Rule. Changes to the list are tracked
+     * so that added declarations are tagged as belonging to this rule, and
+     * the rule for removed declarations is nulled out.
+     * If the list is not going to be modified, then it is more efficient to
+     * call {@link #getUnobservedDeclarationList()}, but the returned list
+     * must be treated as unmodifiable.
+     * @return
+     */
+    public final ObservableList<Declaration> getDeclarations() {
+
+        if (observables == null) {
+            observables = new Observables(this);
+        }
+
+        return observables.getDeclarations();
+    }
+
+    /**
+     * This method is to support tooling that may want to add selectors to
+     * or remove selectors from a Rule. Changes to the list are tracked
+     * so that added selectors are tagged as belonging to this rule, and
+     * the rule for removed selectors is nulled out.
+     * If the list is not going to be modified, then it is more efficient to
+     * call {@link #getUnobservedSelectorList()}, but the returned list
+     * must be treated as unmodifiable.
+     * @return
+     */
+    public final ObservableList<Selector> getSelectors() {
+
+        if (observables == null) {
+            observables = new Observables(this);
+        }
+
+        return observables.getSelectors();
+    }
+
+    /** The stylesheet this selector belongs to */
     private Stylesheet stylesheet;
     public Stylesheet getStylesheet() {
         return stylesheet;
     }
+
     /* package */
     void setStylesheet(Stylesheet stylesheet) {
+
         this.stylesheet = stylesheet;
         
         if (stylesheet != null && stylesheet.getUrl() != null) {
-            
             final URL stylesheetUrl = stylesheet.getUrl();
-            for (int d=0, dMax=declarations.size(); d<dMax; d++) {
+
+            int nDeclarations = declarations != null ? declarations.size() : 0;
+            for (int d=0; d<nDeclarations; d++) {
                 declarations.get(d).fixUrl(stylesheetUrl);
             }
-            
         }
     }
 
@@ -113,35 +172,40 @@
 
 
     public Rule(List<Selector> selectors, List<Declaration> declarations) {
+
         this.selectors = selectors;
-        this.declarations.addAll(declarations);
+        this.declarations = declarations;
+        serializedDecls = null;
+
+        int sMax = selectors != null ? selectors.size() : 0;
+        for(int i = 0; i < sMax; i++) {
+            Selector sel = selectors.get(i);
+            sel.setRule(Rule.this);
+        }
+
+        int dMax = declarations != null ? declarations.size() : 0;
+        for (int d=0; d<dMax; d++) {
+            Declaration decl = declarations.get(d);
+            decl.rule = this;
+        }
     }
 
-    private Rule() {
-        this(null, null);
-    }
+    private static String[] strings = null;  // TBD: blech!
+    private byte[] serializedDecls;
 
-    /**
-     * 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
-     * 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.
-     *
-     *@param node the object to test against
-     *@return an array of all relevant matches, or <code>null</code> if none
-     */
-    List<Match> matches(Node node) {
-        List<Match> matches = new ArrayList<Match>();
-        for (int i = 0; i < selectors.size(); i++) {
+    private Rule(List<Selector> selectors, byte[] buf, String[] stringStoreStrings) {
+
+        this.selectors = selectors;
+        this.declarations = null;
+        this.serializedDecls = buf;
+        if (Rule.strings == null) Rule.strings = stringStoreStrings;
+
+        int sMax = selectors != null ? selectors.size() : 0;
+        for(int i = 0; i < sMax; i++) {
             Selector sel = selectors.get(i);
-            Match match = sel.matches(node);
-            if (match != null) {
-                matches.add(match);
-            }
+            sel.setRule(Rule.this);
         }
-        return matches;
+
     }
 
     // Return mask of selectors that match
@@ -168,27 +232,129 @@
             sb.append(selectors.get(n));
         }
         sb.append("{\n");
-        for (Declaration decl : declarations) {
+        int nDeclarations = declarations != null ? declarations.size() : 0;
+        for (int n=0; n<nDeclarations; n++) {
             sb.append("\t");
-            sb.append(decl);
+            sb.append(declarations.get(n));
             sb.append("\n");
         }
         sb .append("}");
         return sb.toString();
     }
 
+    /*
+     * If an authoring tool adds or removes a selector or declaration,
+     * then the selector or declaration needs to be tweaked to know that
+     * this is the Rule to which it belongs.
+     */
+    private final static class Observables {
+
+        private Observables(Rule rule) {
+
+            this.rule = rule;
+
+            selectorObservableList = new TrackableObservableList<Selector>(rule.getUnobservedSelectorList()) {
+                @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(Observables.this.rule);
+                            }
+                        }
+
+                        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() == Observables.this.rule) {
+                                    sel.setRule(null);
+                                }
+                            }
+                        }
+                    }
+                }
+            };
+
+            declarationObservableList = new TrackableObservableList<Declaration>(rule.getUnobservedDeclarationList()) {
+                @Override protected void onChanged(Change<Declaration> c) {
+                    while (c.next()) {
+                        if (c.wasAdded()) {
+                            List<Declaration> added = c.getAddedSubList();
+                            for(int i = 0, max = added.size(); i < max; i++) {
+                                Declaration decl = added.get(i);
+                                decl.rule = Observables.this.rule;
+
+                                Stylesheet stylesheet = Observables.this.rule.stylesheet;
+                                if (stylesheet != null && stylesheet.getUrl() != null) {
+                                    final URL stylesheetUrl = stylesheet.getUrl();
+                                    decl.fixUrl(stylesheetUrl);
+                                }
+                            }
+                        }
+
+                        if (c.wasRemoved()) {
+                            List<Declaration> removed = c.getRemoved();
+                            for(int i = 0, max = removed.size(); i < max; i++) {
+                                Declaration decl = removed.get(i);
+                                if (decl.rule == Observables.this.rule) {
+                                    decl.rule = null;
+                                }
+                            }
+                        }
+                    }
+                }
+            };
+
+        }
+
+        private ObservableList<Selector> getSelectors() {
+            return selectorObservableList;
+        }
+
+        private ObservableList<Declaration> getDeclarations() {
+            return declarationObservableList;
+        }
+
+        private final Rule rule;
+        private final ObservableList<Selector> selectorObservableList;
+        private final ObservableList<Declaration> declarationObservableList;
+
+    }
+
     void writeBinary(DataOutputStream os, StringStore stringStore)
             throws IOException {
-        os.writeShort(selectors.size());
-        for (int i = 0; i < selectors.size(); i++) {
-            Selector sel = selectors.get(i);
+
+        final int nSelectors = this.selectors != null ? this.selectors.size() : 0;
+        os.writeShort(nSelectors);
+        for (int i = 0; i < nSelectors; i++) {
+            Selector sel = this.selectors.get(i);
             sel.writeBinary(os, stringStore);
         }
-        os.writeShort(declarations.size());
-        for (int i = 0; i < declarations.size(); i++) {
-            Declaration decl = declarations.get(i);
-            decl.writeBinary(os, stringStore);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(5192);
+        DataOutputStream dos = new DataOutputStream(bos);
+
+        if (declarations != null) {
+            int nDeclarations =  declarations.size();
+
+            dos.writeShort(nDeclarations);
+
+            for (int i = 0; i < nDeclarations; i++) {
+                Declaration decl = declarations.get(i);
+                decl.writeBinary(dos, stringStore);
+            }
+        } else if (serializedDecls != null) {
+            dos.write(serializedDecls);
+
+        } else {
+            // no declarations!
+            dos.writeShort(0);
         }
+
+        os.writeInt(bos.size());
+        os.write(bos.toByteArray());
     }
 
     static Rule readBinary(DataInputStream is, String[] strings)
@@ -201,13 +367,13 @@
             selectors.add(s);
         }
 
-        short nDeclarations = is.readShort();
-        List<Declaration> declarations = new ArrayList<Declaration>(nDeclarations);
-        for (int i = 0; i < nDeclarations; i++) {
-            Declaration d = Declaration.readBinary(is, strings);
-            declarations.add(d);
-        }
 
-        return new Rule(selectors, declarations);
+        // de-serialize decls into byte array
+        int nBytes = is.readInt();
+        byte[] buf = new byte[nBytes];
+
+        is.readFully(buf);
+
+        return new Rule(selectors, buf, strings);
     }
 }
--- a/javafx-ui-common/src/com/sun/javafx/css/Selector.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Selector.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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,16 +51,24 @@
     public static Selector getUniversalSelector() {
         return UniversalSelector.INSTANCE;
     }
-    
-    /**
-     * Determines whether this selector applies to the specified object.  
-     * Returns a {@link Match} on success, <code>null</code> otherwise. Note
-     * that pseudoclass states are NOT included in this check.
-     *
-     *@return a <code>Match</code> describing the match, or <code>null</code> 
-     *      for no match
-     */
-    abstract Match matches(Styleable node);
+
+    private Rule rule;
+    void setRule(Rule rule) {
+        this.rule = rule;
+    }
+    Rule getRule() {
+        return rule;
+    }
+
+    private int ordinal = -1;
+    void setOrdinal(int ordinal) {
+        this.ordinal = ordinal;
+    }
+    int getOrdinal() {
+        return ordinal;
+    }
+
+    abstract Match createMatch();
 
     // same as the matches method expect return true/false rather than a match
     public abstract boolean applies(Styleable styleable);
--- a/javafx-ui-common/src/com/sun/javafx/css/SelectorPartitioning.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/SelectorPartitioning.java	Wed Jun 05 08:08:10 2013 -0400
@@ -25,8 +25,14 @@
 
 package com.sun.javafx.css;
 
-import java.lang.reflect.Array;
-import java.util.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Code to partition selectors into a tree-like structure for faster matching.
@@ -35,107 +41,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,29 +79,6 @@
     }   
     
     /** 
-     * 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
-     * invalidate the order.
-     */
-    private static class RuleData implements Comparable {
-        final Rule rule;
-        final int ordinal;
-        
-        private RuleData(Rule rule, int ordinal) {
-            this.rule = rule;
-            this.ordinal = ordinal;
-        }
-
-        @Override
-        public int compareTo(Object t) {
-            return ordinal - ((RuleData)t).ordinal;
-        }
-    }
-    
-    /**
      * A Partition corresponds to a selector type, id or styleclass. For any
      * given id (for example) there will be one Partition held in the
      * corresponding map (idMap, for example). Each Partition has Slots which
@@ -207,38 +90,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<Selector> 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 addSelector(Selector pair) {
+            if (selectors == null) {
+                selectors = new ArrayList<Selector>();
             }
-            rules.add(new RuleData(rule,ordinal));
+            selectors.add(pair);
         }
-        
-        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 +144,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<Selector> 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 addSelector(Selector pair) {
+            if (selectors == null) {
+                selectors = new ArrayList<Selector>();
             }
-            rules.add(new RuleData(rule,ordinal));
+            selectors.add(pair);
         }
-        
-        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 +187,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 +225,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 +263,8 @@
         Partition partition = null;
         Slot slot = null;
 
+        selector.setOrdinal(ordinal++);
+
         switch(c) {
             case ID_BIT | TYPE_BIT | STYLECLASS_BIT: 
             case ID_BIT | TYPE_BIT: 
@@ -402,7 +274,7 @@
                 if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                     slot = slot.partition(styleClassKey, styleClassMap);
                 }                
-                slot.addRule(rule, ordinal++);
+                slot.addSelector(selector);
                 break;
                 
             case TYPE_BIT | STYLECLASS_BIT:
@@ -411,9 +283,9 @@
                 partition = getPartition(typeKey, typeMap);
                 if ((c & STYLECLASS_BIT) == STYLECLASS_BIT) {
                     slot = partition.partition(styleClassKey, styleClassMap);
-                    slot.addRule(rule, ordinal++);
+                    slot.addSelector(selector);
                 } else {
-                    partition.addRule(rule, ordinal++);                    
+                    partition.addSelector(selector);
                 }
                 break;
                 
@@ -427,8 +299,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<Selector> match(String selectorId, String selectorType, Set<StyleClass> selectorStyleClass) {
         
         final boolean hasId = 
             (selectorId != null && selectorId.isEmpty() == false);
@@ -453,7 +325,7 @@
 
         Partition partition = null;
         Slot slot = null;
-        List<RuleData> ruleData = new ArrayList<RuleData>();
+        List<Selector> selectors = new ArrayList<Selector>();
         
         while (c != 0) {
             
@@ -464,8 +336,9 @@
 
                     partition = idMap.get(idKey);
                     if (partition != null) {
-                        partition.copyRules(ruleData);
-
+                        if (partition.selectors != null) {
+                            selectors.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 +346,16 @@
                             slot = partition.slots.get(typePK);                    
                             if (slot != null) {
 
-                                slot.copyRules(ruleData);
-
+                                if (slot.selectors != null) {
+                                    selectors.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);
+                                            selectors.addAll(s.selectors);
                                         }
                                     }
                                 }
@@ -513,14 +388,16 @@
                     do {
                         partition = typeMap.get(typePK);
                         if (partition != null) {
-                            partition.copyRules(ruleData);
-
+                            if (partition.selectors != null) {
+                                selectors.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);
+                                        selectors.addAll(s.selectors);
                                     }
                                 }
                             }
@@ -543,16 +420,18 @@
                     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;
+
+        Collections.sort(selectors, COMPARATOR);
+        return selectors;
     }
 
+    private static final Comparator<Selector> COMPARATOR =
+            new Comparator<Selector>() {
+                @Override
+                public int compare(Selector o1, Selector o2) {
+                    return o1.getOrdinal() - o2.getOrdinal();
+                }
+            };
+
+
 }
--- a/javafx-ui-common/src/com/sun/javafx/css/SimpleSelector.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/SimpleSelector.java	Wed Jun 05 08:08:10 2013 -0400
@@ -188,46 +188,11 @@
         this.matchOnId = (id != null && !("".equals(id)));
 
     }
-    
-    /** copy constructor used by StyleManager
-    SimpleSelector(final SimpleSelector other) {
-        
-        this.name = other.name;
-        this.matchOnName = other.matchOnName;
-        this.styleClassSet = new StyleClass
-        if (other.matchOnStyleClass) {
-            final int length = other.styleClassSet.length;
-            final long[] src = other.styleClassSet;
-            final long[] dest = this.styleClassSet = new long[length];
-            System.arraycopy(src, 0, dest, 0, length);
-        } else {
-            // other is long[0]
-            this.styleClassSet = other.styleClassSet;
-        }
-        this.matchOnStyleClass = other.matchOnStyleClass;
-        
-        this.pseudoClassState = other.pseudoClassState;
-        this.nodeOrientation = other.nodeOrientation;
-        this.id = other.id;
-        this.matchOnId = other.matchOnId;
-    } */
 
-    /**
-     * Returns a {@link Match} if this selector matches the specified object, or 
-     * <code>null</code> otherwise.
-     *
-     *@param node the object to check for a match
-     *@return a {@link Match} if the selector matches, or <code>null</code> 
-     *      otherwise
-     */
-    @Override 
-    Match matches(final Styleable node) {
-        if (applies(node)) {
-            final int idCount = (matchOnId) ? 1 : 0;
-            int styleClassCount = styleClassSet.size();
-            return new Match(this, pseudoClassState, idCount, styleClassCount);
-        }
-        return null;
+    Match createMatch() {
+        final int idCount = (matchOnId) ? 1 : 0;
+        int styleClassCount = styleClassSet.size();
+        return new Match(this, pseudoClassState, idCount, styleClassCount);
     }
 
     @Override 
@@ -345,13 +310,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	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Style.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleCache.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Wed Jun 05 08:08:10 2013 -0400
@@ -67,6 +67,7 @@
 import javafx.css.StyleOrigin;
 import javafx.scene.image.Image;
 import javafx.stage.PopupWindow;
+import javafx.util.Pair;
 import sun.util.logging.PlatformLogger;
 
 /**
@@ -85,18 +86,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 selectors with the selectors 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 selectors are the selectors 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
+ * selectors 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 selectors 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
@@ -259,12 +260,12 @@
                 for (int r=0; r<rMax; r++) {
 
                     final Rule rule = rules.get(r);
-                    final List<Selector> selectors = rule.getSelectors();
+                    final List<Selector> selectors = rule.getUnobservedSelectorList();
                     final int sMax = selectors == null || selectors.isEmpty() ? 0 : selectors.size();
                     for (int s=0; s < sMax; s++) {
 
                         final Selector selector = selectors.get(s);
-                        selectorPartitioning.partition(selector, rule);
+                        selectorPartitioning.partition(selector);
 
                     }
                 }
@@ -1361,8 +1362,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<Selector> selectorData = new ArrayList<>();
 
             // User agent stylesheets have lowest precedence and go first
             if (userAgentStylesheets.isEmpty() == false) {
@@ -1370,9 +1371,9 @@
                     final StylesheetContainer container = userAgentStylesheets.get(n);
                     
                     if (container != null && container.selectorPartitioning != null) {
-                        final List<Rule> matchingRules = 
+                        final List<Selector> matchingRules =
                                 container.selectorPartitioning.match(id, cname, key.styleClasses);
-                        rules.addAll(matchingRules);
+                        selectorData.addAll(matchingRules);
                     }
                 }
             }
@@ -1384,9 +1385,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<Selector> matchingRules =
                                 container.selectorPartitioning.match(id, cname, key.styleClasses);
-                        rules.addAll(matchingRules);
+                        selectorData.addAll(matchingRules);
                     }
                 }
             }
@@ -1398,15 +1399,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<Selector> 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 selectors.
+            cache = new Cache(selectorData);
             cacheMap.put(key, cache);
             
             // cause a new Key to be created the next time this method is called
@@ -1605,84 +1606,74 @@
             }
             
         }
-        // this must be initialized to the appropriate possible rules when
-        // the helper cache is created by the StylesheetContainer
-        private final List<Rule> rules;
+
+        // this must be initialized to the appropriate possible selectors when
+        // the helper cache is created by the StylesheetContainer. Note that
+        // SelectorPartioning sorts the matched selectors by ordinal, so this
+        // list of selectors will be in the same order in which the selectors
+        // appear in the stylesheets.
+        private final List<Selector> selectors;
         private final Map<Key, Integer> cache;
 
-        Cache(List<Rule> rules) {
-            this.rules = rules;
+        Cache(List<Selector> selectors) {
+            this.selectors = selectors;
             this.cache = new HashMap<Key, Integer>();
         }
 
         private StyleMap getStyleMap(CacheContainer cacheContainer, Node node, Set<PseudoClass>[] triggerStates) {
             
-            if (rules == null || rules.isEmpty()) {                
+            if (selectors == null || selectors.isEmpty()) {
                 return StyleMap.EMPTY_MAP;
             }
 
+            final int selectorDataSize = selectors.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 selectors is found by matching only the
+            // rightmost selector, the set of selectors may larger than those
+            // selectors that actually match the node. The following loop
+            // whittles the list down to those selectors 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++) {
-                
-                final Rule rule = rules.get(r);
+            long key[] = new long[Long.SIZE/selectorDataSize + 1];
+            boolean nothingMatched = true;
+
+            for (int s = 0; s < selectorDataSize; s++) {
+
+                final Selector sel = selectors.get(s);
 
                 //
                 // 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 selectors. 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 selectors 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;
+
+                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,89 +1684,70 @@
                 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
-            // 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) {
-                        continue;
-                    }
-                    
-                    for (int k = 0, kmax = rule.declarations.size(); k < kmax; k++) {
-                        final Declaration decl = rule.declarations.get(k);
+            final List<Selector> selectors = new ArrayList<>();
 
-                        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++
-                        );
+            for (int k = 0; k<key.length; k++) {
 
-                        styles.add(s);
-                    }
+                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 Selector pair = this.selectors.get(offset + b);
+                    selectors.add(pair);
                 }
             }
 
-                // return sorted styles
-            Collections.sort(styles);
-
-            
-            // We need to create a new style map, add it to the cache,
-            // and then return it.
-            final Map<String, List<CascadingStyle>> smap =
-                new HashMap<String, List<CascadingStyle>>();
-            
-            for (int i=0, max=styles.size(); i<max; i++) {
-                
-                final CascadingStyle style = styles.get(i);
-                final String property = style.getProperty();
-                
-                // This is carefully written to use the minimal amount of hashing.
-                List<CascadingStyle> list = smap.get(property);
-                if (list == null) {
-                    list = new ArrayList<CascadingStyle>(5);
-                    smap.put(property, list);
-                }
-                list.add(style);
-            }
-
             final int id = cacheContainer.nextSmapId();
-            final StyleMap styleMap = new StyleMap(id, smap);
+            final StyleMap styleMap = new StyleMap(id, selectors);
             cacheContainer.addStyleMap(styleMap);
             cache.put(keyObj, Integer.valueOf(id));
             return styleMap;
         }
 
     }
+
+    /**
+     * Get the map of property to style from the rules and declarations
+     * in the stylesheet. There is no need to do selector matching here since
+     * the stylesheet is from parsing Node.style.
+     */
+    public StyleMap createInlineStyleMap(Styleable styleable) {
+
+        Stylesheet inlineStylesheet =
+                CSSParser.getInstance().parseInlineStyle(styleable);
+        if (inlineStylesheet == null)  return StyleMap.EMPTY_MAP;
+
+        inlineStylesheet.setOrigin(StyleOrigin.INLINE);
+
+        List<Selector> pairs = new ArrayList<>(1);
+
+        int ordinal = 0;
+            
+        final List<Rule> stylesheetRules = inlineStylesheet.getRules();
+
+        List<Selector> selectorList =  null;
+
+        for (int i = 0, imax = stylesheetRules.size(); i < imax; i++) {
+            
+            final Rule rule = stylesheetRules.get(i);
+            if (rule == null) continue;
+
+            List<Selector> selectors = rule.getUnobservedSelectorList();
+            if (selectorList == null || selectors.isEmpty()) continue;
+
+            selectorList.addAll(selectors);
+        }
+
+        // TODO: should have a cacheContainer for inline styles?
+        return new StyleMap(-1, selectorList);
+    }
+
     
     /**
      * The key used in the cacheMap of the StylesheetContainer
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleMap.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleMap.java	Wed Jun 05 08:08:10 2013 -0400
@@ -25,9 +25,20 @@
 
 package com.sun.javafx.css;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+import javafx.util.Pair;
 
 /**
  * A map of property name to the cascading styles that match a node.
@@ -35,22 +46,141 @@
 public final class StyleMap {
 
     public static final StyleMap EMPTY_MAP = 
-        new StyleMap(-1, Collections.<String,List<CascadingStyle>>emptyMap());
+        new StyleMap(-1, Collections.<Selector>emptyList());
 
     /** Only StyleManager creates StyleMap */
-    StyleMap(int id, Map<String, List<CascadingStyle>> map) {
+    StyleMap(int id, List<Selector> selectors) {
         this.id = id;
-        this.map  = map;
+        this.selectors = selectors;
     }
 
     public int getId() {
         return id;
     }
 
-    public Map<String, List<CascadingStyle>> getMap() {
-        return map;
+    public boolean isEmpty() {
+        if (selectors != null) return selectors.isEmpty();
+        else if (cascadingStyles != null) return cascadingStyles.isEmpty();
+        else return true;
     }
         
+    public Map<String, List<CascadingStyle>> getCascadingStyles() {
+
+        if (cascadingStyles == null) {
+
+            if (selectors == null || selectors.isEmpty()) {
+                cascadingStyles = Collections.emptyMap();
+                return cascadingStyles;
+            }
+
+            //
+            // Creating the map is a three step process. First, create
+            // a list of CascadingStyles. Second, sort the CascadingStyles.
+            // And, finally, loop through the CascadingStyles to put them
+            // into the Map by property name.
+            //
+            List<CascadingStyle> cascadingStyleList = new ArrayList<>();
+
+            int ordinal = 0;
+            for (int i=0, iMax=selectors.size(); i<iMax; i++) {
+
+                final Selector selector = selectors.get(i);
+
+                final Match match = selector.createMatch();
+
+                final Rule rule = selector.getRule();
+
+                for (int d = 0, dmax = rule.getDeclarations().size(); d < dmax; d++) {
+                    final Declaration decl = rule.getDeclarations().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++
+                    );
+
+                    cascadingStyleList.add(s);
+
+                }
+            }
+
+            // apply the cascade. CascadingStyle's primary sort key is the
+            // property name, so the same properties should be in sequence.
+            Collections.sort(cascadingStyleList, cascadingStyleComparator);
+
+            // there may be more entries in this HashMap than we need if there
+            // is more than one CascadingStyle per property. But this is
+            // still better than
+            final int nCascadingStyles = cascadingStyleList.size();
+            cascadingStyles = new HashMap<>(nCascadingStyles);
+
+            CascadingStyle cascadingStyle = cascadingStyleList.get(0);
+            String property = cascadingStyle.getProperty();
+
+
+            for (int fromIndex=0; fromIndex<nCascadingStyles; /*increment is in code*/) {
+
+                List<CascadingStyle> value = cascadingStyles.get(property);
+                if (value == null)  {
+
+                    int toIndex = fromIndex;
+                    final String currentProperty = property;
+
+                    while (++toIndex < nCascadingStyles) {
+                        cascadingStyle = cascadingStyleList.get(toIndex);
+                        property = cascadingStyle.getProperty();
+                        if (property.equals(currentProperty) == false) break;
+                    }
+
+                    cascadingStyles.put(currentProperty, cascadingStyleList.subList(fromIndex, toIndex));
+
+                    fromIndex = toIndex;
+
+
+                } else {
+                    // logic is broken!
+                    assert(false);
+                }
+            }
+
+            selectors.clear();
+            selectors = null;
+
+        }
+
+        return cascadingStyles;
+    }
+
+    private static final Comparator<CascadingStyle> cascadingStyleComparator =
+            new Comparator<CascadingStyle>() {
+
+                @Override public int compare(CascadingStyle o1, CascadingStyle o2) {
+
+                    //
+                    // primary sort is on property. If the property names are the same,
+                    // then go through the cascade logic. Otherwise, sort by property
+                    // name since the cascade doesn't matter for dissimilar properties.
+                    //
+                    // What we want to end up with is a list where all the same properties
+                    // are together in the list.
+                    //
+                    String thisProperty = o1.getProperty();
+                    String otherProperty = o2.getProperty();
+
+                    int c = thisProperty.compareTo(otherProperty);
+                    if (c != 0) return c;
+
+                    return o1.compareTo(o2);
+
+                }
+
+            };
+
     private final int id; // unique per container
-    private final Map<String, List<CascadingStyle>> map;            
+    private List<Selector> selectors;
+    private Map<String, List<CascadingStyle>> cascadingStyles;
 }
--- a/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java	Wed Jun 05 08:08:10 2013 -0400
@@ -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/CssStyleHelper.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/CssStyleHelper.java	Wed Jun 05 08:08:10 2013 -0400
@@ -24,26 +24,6 @@
  */
 package javafx.scene;
 
-import com.sun.javafx.Logging;
-import com.sun.javafx.Utils;
-import com.sun.javafx.css.CalculatedValue;
-import static com.sun.javafx.css.CalculatedValue.SKIP;
-import com.sun.javafx.css.CascadingStyle;
-import com.sun.javafx.css.CssError;
-import com.sun.javafx.css.Declaration;
-import com.sun.javafx.css.ParsedValueImpl;
-import com.sun.javafx.css.PseudoClassState;
-import com.sun.javafx.css.Rule;
-import com.sun.javafx.css.Selector;
-import com.sun.javafx.css.Style;
-import com.sun.javafx.css.StyleCache;
-import com.sun.javafx.css.StyleCacheEntry;
-import com.sun.javafx.css.StyleConverterImpl;
-import com.sun.javafx.css.StyleManager;
-import com.sun.javafx.css.StyleMap;
-import com.sun.javafx.css.Stylesheet;
-import com.sun.javafx.css.converters.FontConverter;
-import com.sun.javafx.css.parser.CSSParser;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -68,8 +48,29 @@
 import javafx.scene.text.FontPosture;
 import javafx.scene.text.FontWeight;
 import javafx.stage.Window;
+import com.sun.javafx.Logging;
+import com.sun.javafx.Utils;
+import com.sun.javafx.css.CalculatedValue;
+import com.sun.javafx.css.CascadingStyle;
+import com.sun.javafx.css.CssError;
+import com.sun.javafx.css.Declaration;
+import com.sun.javafx.css.ParsedValueImpl;
+import com.sun.javafx.css.PseudoClassState;
+import com.sun.javafx.css.Rule;
+import com.sun.javafx.css.Selector;
+import com.sun.javafx.css.Style;
+import com.sun.javafx.css.StyleCache;
+import com.sun.javafx.css.StyleCacheEntry;
+import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
+import com.sun.javafx.css.StyleMap;
+import com.sun.javafx.css.Stylesheet;
+import com.sun.javafx.css.converters.FontConverter;
+import com.sun.javafx.css.parser.CSSParser;
 import sun.util.logging.PlatformLogger;
 
+import static com.sun.javafx.css.CalculatedValue.*;
+
 /**
  * The StyleHelper is a helper class used for applying CSS information to Nodes.
  */
@@ -118,10 +119,7 @@
                 StyleManager.getInstance().findMatchingStyles(node, triggerStates);
 
         
-        final Map<String, List<CascadingStyle>> smap 
-                = styleMap != null ? styleMap.getMap() : null;
-        
-        if (smap == null || smap.isEmpty()) {
+        if (styleMap == null || styleMap.isEmpty()) {
             
             // If there are no styles at all, and no styles that inherit, then return
             final String inlineStyle = node.getStyle();
@@ -336,7 +334,7 @@
             final String inlineStyle = node.getStyle();
             if(inlineStyle == null || inlineStyle.isEmpty()) {
 
-                final Map<String, List<CascadingStyle>> smap = getStyleMap(node);            
+                StyleMap smap = getStyleMap(node);
                 if (smap == null || smap.isEmpty()) {
                     // We have no styles! Reset this StyleHelper to its
                     // initial state so that calls to transitionToState 
@@ -380,32 +378,30 @@
     }
     
     private void resetToInitialValues(Styleable styleable) {
-        
-        final List<CssMetaData<? extends Styleable, ?>> metaDataList = styleable.getCssMetaData();
-        final int nStyleables = metaDataList != null ? metaDataList.size() : 0;
-        for (int n=0; n<nStyleables; n++) {
-            final CssMetaData metaData = metaDataList.get(n);
-            if (metaData.isSettable(styleable) == false) continue;
-            final StyleableProperty styleableProperty = metaData.getStyleableProperty(styleable);
-            if (styleableProperty != null) {
-                final StyleOrigin origin = styleableProperty.getStyleOrigin();
-                if (origin != null && origin != StyleOrigin.USER) {
-                    // If a property is never set by the user or by CSS, then 
-                    // the StyleOrigin of the property is null. So, passing null 
-                    // here makes the property look (to CSS) like it was
-                    // initialized but never used.
-                    Object value = metaData.getInitialValue(styleable);
-                    styleableProperty.applyStyle(null, value);
-                }
+        for (StyleableProperty styleableProperty : cacheContainer.cssSetProperties.values()) {
+            final StyleOrigin origin = styleableProperty.getStyleOrigin();
+            if (origin != null && origin != StyleOrigin.USER) {
+                // If a property is never set by the user or by CSS, then
+                // the StyleOrigin of the property is null. So, passing null
+                // here makes the property look (to CSS) like it was
+                // initialized but never used.
+                CssMetaData metaData = styleableProperty.getCssMetaData();
+                Object value = metaData.getInitialValue(styleable);
+                styleableProperty.applyStyle(null, value);
             }
-        }        
+        }
     }
     
-        
-    private Map<String, List<CascadingStyle>> getStyleMap(Styleable styleable) {
+
+    private StyleMap getStyleMap(Styleable styleable) {
         if (cacheContainer == null || styleable == null) return null;
-        StyleMap styleMap = cacheContainer.getStyleMap(styleable);
-        return (styleMap != null) ? styleMap.getMap() : null;
+        return cacheContainer.getStyleMap(styleable);
+    }
+
+    private Map<String, List<CascadingStyle>> getCascadingStyles(Styleable styleable) {
+        StyleMap styleMap = getStyleMap(styleable);
+        // code looks for null return to indicate that the cache was blown away
+        return (styleMap != null) ? styleMap.getCascadingStyles() : null;
     }
     
     /** 
@@ -430,7 +426,7 @@
             final List<Rule> stylesheetRules = inlineStylesheet.getRules();
             for (int i = 0, imax = stylesheetRules.size(); i < imax; i++) {
                 final Rule rule = stylesheetRules.get(i);
-                final List<Declaration> declarations = rule.getDeclarations();
+                final List<Declaration> declarations = rule.getUnobservedDeclarationList();
                 for (int k = 0, kmax = declarations.size(); k < kmax; k++) {
                     Declaration decl = declarations.get(k);
 
@@ -861,10 +857,13 @@
         final CascadingStyle inlineStyle = (inlineStyles != null) ? inlineStyles.get(property) : null;
 
         // Get all of the Styles which may apply to this particular property
-        final Map<String, List<CascadingStyle>> smap = getStyleMap(styleable);
-        if (smap == null) return inlineStyle;
+        final StyleMap smap = getStyleMap(styleable);
+        if (smap == null || smap.isEmpty()) return inlineStyle;
 
-        final List<CascadingStyle> styles = smap.get(property);
+        final Map<String, List<CascadingStyle>> cascadingStyleMap = smap.getCascadingStyles();
+        if (cascadingStyleMap == null || cascadingStyleMap.isEmpty()) return inlineStyle;
+
+        List<CascadingStyle> styles = cascadingStyleMap.get(property);
 
         // If there are no styles for this property then we can just bail
         if ((styles == null) || styles.isEmpty()) return inlineStyle;
@@ -2134,7 +2133,7 @@
                     
             String property = styleableProperty.getProperty();
             Node _node = node instanceof Node ? (Node)node : null;
-            final Map<String, List<CascadingStyle>> smap = getStyleMap(_node);
+            final Map<String, List<CascadingStyle>> smap = getCascadingStyles(_node);
             if (smap == null) return;
             
              List<CascadingStyle> styles = smap.get(property);            
@@ -2194,7 +2193,7 @@
                                              
                         final int start = styleList.size();
                         
-                        final Map<String, List<CascadingStyle>> smap = helper.getStyleMap(_parent);
+                        final Map<String, List<CascadingStyle>> smap = helper.getCascadingStyles(_parent);
                         if (smap != null) {
 
                             List<CascadingStyle> styles = smap.get(property);
--- a/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Wed Jun 05 08:08:10 2013 -0400
@@ -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 selectors 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 selectors 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/CssMetaDataTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/CssMetaDataTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -240,35 +240,6 @@
         assertTrue(testNodeOpacity.equals(nodeOpacity));
     }
 
-    /**
-     * Helper for testGetMatchingStyles
-     */
-    List<CascadingStyle> match(Node node, Stylesheet stylesheet) {
-
-        List<CascadingStyle> styles = new ArrayList<CascadingStyle>();
-
-        int ord = 0;
-        for (Rule rule : stylesheet.getRules()) {
-            final List<Match> matches = rule.matches(node);
-            if (matches == null || matches.isEmpty()) continue;
-            for (Match match : matches) {
-                if (match == null) continue;
-                for (Declaration declaration : rule.getDeclarations()) {
-                    styles.add(
-                            new CascadingStyle(
-                                    new Style(match.selector, declaration),
-                                    match.pseudoClasses,
-                                    match.specificity,
-                                    ord++
-                            )
-                    );
-                }
-            }
-        }
-
-        return styles;
-    }
-
     static int ord = 0;
     static CascadingStyle createCascadingStyle(Selector selector, Declaration declaration) {
 
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/Node_cssStyleMap_Test.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/Node_cssStyleMap_Test.java	Wed Jun 05 08:08:10 2013 -0400
@@ -69,7 +69,7 @@
         for (Declaration decl : decls) {
             styles.add(
                 new CascadingStyle(
-                    new Style(decl.rule.selectors.get(0), decl), 
+                    new Style(decl.rule.getUnobservedSelectorList().get(0), decl),
                     new PseudoClassState(),
                     0, 
                     0
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/RuleTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/RuleTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -53,6 +53,27 @@
     }
 
     @Test
+    public void testGetUnobservedSelectorList() {
+        List<Selector> expResult = new ArrayList<Selector>();
+        expResult.add(Selector.createSelector("One.two#three"));
+        expResult.add(Selector.createSelector("Four.five#six"));
+        Rule instance = new Rule(expResult, Collections.EMPTY_LIST);
+        List result = instance.getUnobservedSelectorList();
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testGetUnobservedDeclarationList() {
+        List<Declaration> expResult = new ArrayList<Declaration>();
+        expResult.add(new Declaration("one", new ParsedValueImpl<String,String>("one", null), false));
+        expResult.add(new Declaration("two", new ParsedValueImpl<String,String>("two", null), false));
+        expResult.add(new Declaration("three", new ParsedValueImpl<String,String>("three", null), false));
+        Rule instance = new Rule(Collections.EMPTY_LIST, expResult);
+        List result = instance.getUnobservedDeclarationList();
+        assertEquals(expResult, result);
+    }
+
+    @Test
     public void testGetSelectors() {
         List<Selector> expResult = new ArrayList<Selector>();
         expResult.add(Selector.createSelector("One.two#three"));
@@ -121,17 +142,6 @@
         StyleOrigin result = instance.getOrigin();
         assertNull(result);
     }
-    
-    @Ignore @Test
-    public void testMatches_Node() {
-        System.out.println("matches");
-        Node node = null;
-        Rule instance = null;
-        List expResult = null;
-        List result = instance.matches(node);
-        assertEquals(expResult, result);
-        fail("The test case is a prototype.");
-    }
 
     @Ignore @Test
     public void testApplies() {
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/SelectorPartitioningTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/SelectorPartitioningTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -186,8 +186,8 @@
                 CSSParser.getInstance().parse(data.stylesheetText);
                 
         for (Rule rule : stylesheet.getRules()) {
-            for (Selector selector : rule.selectors) {
-                instance.partition(selector, rule);                
+            for (Selector selector : rule.getUnobservedSelectorList()) {
+                instance.partition(selector);
             }
         }
         
@@ -202,13 +202,15 @@
                 
         SimpleSelector simple = simpleData.selector;
         
-        List<Rule> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
+        List<Selector> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
         
         assertEquals(1,matched.size());
-        Rule rule = matched.get(0);
-        
-        assertEquals(1,rule.declarations.size());
-        Declaration decl = rule.declarations.get(0);
+        Selector selector = matched.get(0);
+
+        Rule rule = selector.getRule();
+
+        assertEquals(1,rule.getUnobservedDeclarationList().size());
+        Declaration decl = rule.getUnobservedDeclarationList().get(0);
         
         assertEquals("-fx-fill", decl.property);
         
@@ -224,11 +226,10 @@
                 
         SimpleSelector simple = complexData.selector;
         
-        List<Rule> matched = instance.match(simple.getId(), simple.getName(), simple.getStyleClassSet());
+        List<Selector> 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(Selector s1 : matched) {
             for (SimpleData datum : complexData.data) {
                 Selector s2 = (SimpleSelector)datum.selector;
                 if (s1.equals(s2)) {
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/StyleTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/StyleTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -63,8 +63,8 @@
         
         Stylesheet stylesheet = CSSParser.getInstance().parse(stylesheetText);
         Rule rule = stylesheet.getRules().get(0);
-        Selector sel = rule.getSelectors().get(0);
-        Declaration decl = rule.getDeclarations().get(0);
+        Selector sel = rule.getUnobservedSelectorList().get(0);
+        Declaration decl = rule.getUnobservedDeclarationList().get(0);
         return new Style(sel, decl);
     }
 
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/TypeTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/TypeTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -50,7 +50,7 @@
     // ParsedValue value = TypeTest.getValueFor(stylesheet, "-fx-cursor")
     public static ParsedValue getValueFor(Stylesheet stylesheet, String property ) {
         for (Rule rule : stylesheet.getRules()) {
-            for (Declaration decl : rule.getDeclarations()) {
+            for (Declaration decl : rule.getUnobservedDeclarationList()) {
                 if (property.equals(decl.getProperty())) {
                     return decl.getParsedValue();
                 }
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/parser/CSSParserTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/parser/CSSParserTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -61,7 +61,7 @@
         assertNotNull(ss);
         List<Rule> rules = ss.getRules();
         assertEquals(1,rules.size(),0);
-        List<Declaration> decls = ss.getRules().get(0).getDeclarations();
+        List<Declaration> decls = ss.getRules().get(0).getUnobservedDeclarationList();
         assertTrue(decls.size()==1);
         Declaration decl = decls.get(0);
         ParsedValue value = decl.getParsedValue();
@@ -104,7 +104,7 @@
         assertNotNull(ss);
         List<Rule> rules = ss.getRules();
         assertEquals(1,rules.size(),0);
-        List<Declaration> decls = ss.getRules().get(0).getDeclarations();
+        List<Declaration> decls = ss.getRules().get(0).getUnobservedDeclarationList();
         assertTrue(decls.size()==1);
         Declaration decl = decls.get(0);
         ParsedValue value = decl.getParsedValue();
@@ -144,7 +144,7 @@
         assertNotNull(ss);
         List<Rule> rules = ss.getRules();
         assertEquals(1,rules.size(),0);
-        List<Declaration> decls = ss.getRules().get(0).getDeclarations();
+        List<Declaration> decls = ss.getRules().get(0).getUnobservedDeclarationList();
         assertTrue(decls.size()==1);
         Declaration decl = decls.get(0);
         ParsedValue value = decl.getParsedValue();
@@ -185,7 +185,7 @@
             for (int n=0; n<cssRules.size(); n++) {
                 Rule expected = cssRules.get(n);
                 Rule actual = bssRules.get(n);
-                assertEquals(Integer.toString(n), expected.getDeclarations(), actual.getDeclarations());                
+                assertEquals(Integer.toString(n), expected.getUnobservedDeclarationList(), actual.getUnobservedDeclarationList());
             }
             
         } catch (IOException ioe) {
@@ -206,7 +206,7 @@
         assertNotNull(ss);
         List<Rule> rules = ss.getRules();
         assertEquals(1,rules.size(),0);
-        List<Declaration> decls = ss.getRules().get(0).getDeclarations();
+        List<Declaration> decls = ss.getRules().get(0).getUnobservedDeclarationList();
         assertEquals(2,decls.size(),0);
         
         Declaration decl = decls.get(0);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/DatePickerContent.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/DatePickerContent.java	Wed Jun 05 08:08:10 2013 -0400
@@ -26,9 +26,9 @@
 package com.sun.javafx.scene.control.skin;
 
 import java.time.LocalDate;
-// import java.time.format.DateTimeFormatSymbols;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DecimalStyle;
 import java.time.chrono.Chronology;
 import java.time.chrono.ChronoLocalDate;
 import java.time.temporal.ChronoUnit;
@@ -443,7 +443,7 @@
                 // such as when Thai numerals are required.
                 String cellText =
                     weekNumberFormatter.withLocale(locale)
-//                                        .withSymbols(DateTimeFormatSymbols.of(locale))
+                                       .withDecimalStyle(DecimalStyle.of(locale))
                                        .format(date);
                 weekNumberCells.get(i).setText(cellText);
             }
@@ -496,7 +496,7 @@
             String cellText =
                 dayCellFormatter.withLocale(locale)
                                 .withChronology(chrono)
-//                                 .withSymbols(DateTimeFormatSymbols.of(locale))
+                                .withDecimalStyle(DecimalStyle.of(locale))
                                 .format(cDate);
             dayCell.setText(cellText);
 
@@ -576,7 +576,7 @@
         // Fixme: Format Japanese era names with Japanese text.
         String str = formatter.withLocale(getLocale())
                               .withChronology(getChronology())
-//                               .withSymbols(DateTimeFormatSymbols.of(getLocale()))
+                              .withDecimalStyle(DecimalStyle.of(getLocale()))
                               .format(cDate);
 
         return str;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/DatePickerHijrahContent.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/DatePickerHijrahContent.java	Wed Jun 05 08:08:10 2013 -0400
@@ -28,7 +28,7 @@
 import java.time.LocalDate;
 import java.time.DateTimeException;
 import java.time.YearMonth;
-// import java.time.format.DateTimeFormatSymbols;
+import java.time.format.DecimalStyle;
 import java.time.chrono.Chronology;
 import java.time.chrono.HijrahChronology;
 import java.time.chrono.HijrahDate;
@@ -107,11 +107,11 @@
                 if (hijrahStr == null || month != firstMonth) {
                     String monthStr = monthFormatter.withLocale(locale)
                                                     .withChronology(chrono)
-//                                                     .withSymbols(DateTimeFormatSymbols.of(locale))
+                                                    .withDecimalStyle(DecimalStyle.of(locale))
                                                     .format(cDate);
                     String yearStr = yearFormatter.withLocale(locale)
                                                     .withChronology(chrono)
-//                                                     .withSymbols(DateTimeFormatSymbols.of(locale))
+                                                    .withDecimalStyle(DecimalStyle.of(locale))
                                                     .format(cDate);
                     if (hijrahStr == null) {
                         firstMonth = month;
@@ -170,7 +170,7 @@
                 String hijrahStr =
                     dayCellFormatter.withLocale(locale)
                                     .withChronology(chrono)
-//                                     .withSymbols(DateTimeFormatSymbols.of(locale))
+                                    .withDecimalStyle(DecimalStyle.of(locale))
                                     .format(cDate);
 
                 secondaryText.setText(hijrahStr);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Wed Jun 05 08:08:10 2013 -0400
@@ -418,6 +418,9 @@
         caretPosition.addListener(new ChangeListener<Number>() {
             @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                 targetCaretX = -1;
+                if (newValue.intValue() > oldValue.intValue()) {
+                    setForwardBias(true);
+                }
             }
         });
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Wed Jun 05 08:08:10 2013 -0400
@@ -376,7 +376,7 @@
     }
 
     private void updateTextNodeCaretPos(int pos) {
-        if (isForwardBias()) {
+        if (pos == 0 || isForwardBias()) {
             textNode.setImpl_caretPosition(pos);
         } else {
             textNode.setImpl_caretPosition(pos - 1);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TitledPaneSkin.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TitledPaneSkin.java	Wed Jun 05 08:08:10 2013 -0400
@@ -88,6 +88,7 @@
         contentContainer.getChildren().setAll(contentRegion);
 
         if (titledPane.isExpanded()) {
+            setTransition(1.0f);
             setExpanded(titledPane.isExpanded());
         } else {
             setTransition(0.0f);
@@ -267,6 +268,7 @@
     private void doAnimationTransition() {
         Duration duration;
 
+        
         if (contentRegion.getContent() == null) {
             return;
         }
--- a/javafx-ui-controls/src/javafx/scene/control/DatePicker.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/DatePicker.java	Wed Jun 05 08:08:10 2013 -0400
@@ -32,9 +32,10 @@
 import java.time.chrono.Chronology;
 import java.time.chrono.ChronoLocalDate;
 import java.time.chrono.IsoChronology;
-// import java.time.format.DateTimeFormatSymbols;
 import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DateTimeParseException;
+import java.time.format.DecimalStyle;
 import java.time.format.FormatStyle;
 import java.time.temporal.TemporalAccessor;
 import java.util.ArrayList;
@@ -355,16 +356,19 @@
                 DateTimeFormatter dateFormatter =
                     DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
                                      .withLocale(locale)
-                                     .withChronology(chrono);
-//                                      .withSymbols(DateTimeFormatSymbols.of(locale));
+                                     .withChronology(chrono)
+                                     .withDecimalStyle(DecimalStyle.of(locale));
 
-                String pattern = getPattern();
-                //System.err.println("pattern = "+pattern);
+                String pattern =
+                    DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT,
+                                                                         null, chrono, locale);
+
                 if (pattern.contains("yy") && !pattern.contains("yyy")) {
                     // Modify pattern to show four-digit year, including leading zeros.
                     String newPattern = pattern.replace("yy", "yyyy");
                     //System.err.println("Fixing pattern ("+forParsing+"): "+pattern+" -> "+newPattern);
-                    dateFormatter = DateTimeFormatter.ofPattern(newPattern);
+                    dateFormatter = DateTimeFormatter.ofPattern(newPattern)
+                                                     .withDecimalStyle(DecimalStyle.of(locale));
                 }
 
                 return dateFormatter.format(cDate);
@@ -378,30 +382,14 @@
                 Locale locale = Locale.getDefault(Locale.Category.FORMAT);
                 Chronology chrono = getChronology();
 
-                String pattern = getPattern();
-                if (pattern.contains("yy") && !pattern.contains("yyy")) {
-                    // First try a pattern using four-digit year
-                    pattern = pattern.replace("yy", "yyyy");
-                    DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
-                    try {
-                        TemporalAccessor temporal = df.parse(text);
-                        try {
-                            ChronoLocalDate cDate = chrono.date(temporal);
-                            return LocalDate.from(cDate);
-                        } catch (DateTimeException ex) {
-                            System.err.println(ex);
-                            return null;
-                        }
-                    } catch (DateTimeParseException ex) {
-                        // Didn't work, so continue with original two-digit pattern
-                    }
-                }
-
+                String pattern =
+                    DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT,
+                                                                         null, chrono, locale);
                 DateTimeFormatter df =
-                    DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
-                                     .withLocale(locale)
-                                     .withChronology(chrono);
-//                                      .withSymbols(DateTimeFormatSymbols.of(locale));
+                    new DateTimeFormatterBuilder().parseLenient()
+                                                  .appendPattern(pattern)
+                                                  .toFormatter()
+                                                  .withDecimalStyle(DecimalStyle.of(locale));
                 TemporalAccessor temporal = df.parse(text);
                 ChronoLocalDate cDate = chrono.date(temporal);
                 return LocalDate.from(cDate);
@@ -410,18 +398,6 @@
         }
     };
 
-    private String getPattern() {
-        Locale locale = Locale.getDefault(Locale.Category.FORMAT);
-        Chronology chrono = getChronology();
-
-        return LocaleProviderAdapter.getResourceBundleBased()
-                                    .getLocaleResources(locale)
-                                    .getJavaTimeDateTimePattern(-1,
-                                                                FormatStyle.SHORT.ordinal(),
-                                                                chrono.getCalendarType());
-    }
-
-
 
     // --- Editor
     /**
--- a/javafx-ui-controls/src/javafx/scene/control/ListCell.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/ListCell.java	Wed Jun 05 08:08:10 2013 -0400
@@ -421,9 +421,7 @@
                 updateItem(newValue, false);
             }
         } else {
-            if (! isEmpty()) {
-                updateItem(null, true);
-            }
+            updateItem(null, true);
         }
     }
     
--- a/javafx-ui-controls/src/javafx/scene/control/TableCell.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TableCell.java	Wed Jun 05 08:08:10 2013 -0400
@@ -82,6 +82,16 @@
 
     /***************************************************************************
      *                                                                         *
+     * Private fields                                                          *
+     *                                                                         *
+     **************************************************************************/
+
+    // package for testing
+    boolean lockItemOnEdit = false;
+
+
+    /***************************************************************************
+     *                                                                         *
      * Callbacks and Events                                                    *
      *                                                                         *
      **************************************************************************/
@@ -268,18 +278,17 @@
         final TableColumn column = getTableColumn();
         if (! isEditable() ||
                 (table != null && ! table.isEditable()) ||
-                (column != null && ! getTableColumn().isEditable()))
-        {
-//            if (Logging.getControlsLogger().isLoggable(PlatformLogger.WARNING)) {
-//                Logging.getControlsLogger().warning(
-//                    "Can not call TableCell.startEdit() on this TableCell, as it "
-//                        + "is not allowed to enter its editing state (TableCell: "
-//                        + this + ", TableView: " + table + ").");
-//            }
-            
+                (column != null && ! getTableColumn().isEditable())) {
             return;
         }
-        
+
+        // We check the boolean lockItemOnEdit field here, as whilst we want to
+        // updateItem normally, when it comes to unit tests we can't have the
+        // item change in all circumstances.
+        if (! lockItemOnEdit) {
+            updateItem();
+        }
+
         // it makes sense to get the cell into its editing state before firing
         // the event to listeners below, so that's what we're doing here
         // by calling super.startEdit().
@@ -534,9 +543,7 @@
                 tableColumn == null || 
                 !tableColumn.isVisible()) {
             
-            if (! isEmpty()) {
-                updateItem(null, true);
-            }
+            updateItem(null, true);
             return;
         } else {
             currentObservableValue = tableColumn.getCellObservableValue(index);
--- a/javafx-ui-controls/src/javafx/scene/control/TableColumnBase.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TableColumnBase.java	Wed Jun 05 08:08:10 2013 -0400
@@ -507,7 +507,7 @@
      */
     @Deprecated
     public final boolean impl_isReorderable() {
-        return reorderable == null ? false : reorderable.get();
+        return reorderable == null ? true : reorderable.get();
     }
     
     
--- a/javafx-ui-controls/src/javafx/scene/control/TreeCell.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeCell.java	Wed Jun 05 08:08:10 2013 -0400
@@ -334,6 +334,8 @@
 
     /** {@inheritDoc} */
     @Override public void startEdit() {
+        if (isEditing()) return;
+
         final TreeView<T> tree = getTreeView();
         if (! isEditable() || (tree != null && ! tree.isEditable())) {
 //            if (Logging.getControlsLogger().isLoggable(PlatformLogger.SEVERE)) {
@@ -344,6 +346,8 @@
 //            }
             return;
         }
+
+        updateItem();
         
         // it makes sense to get the cell into its editing state before firing
         // the event to the TreeView below, so that's what we're doing here
@@ -471,10 +475,8 @@
                 updateItem(newValue, false);
             }
         } else {
-            if (! isEmpty()) {
-                updateTreeItem(null);
-                updateItem(null, true);
-            }
+            updateTreeItem(null);
+            updateItem(null, true);
         }
     }
 
@@ -512,7 +514,7 @@
         // the edit mode, then I need to enter the edit mode
         if (match && !editing) {
             startEdit();
-        } else if (match && editing) {
+        } else if (! match && editing) {
             // If my tree item is not the one being edited then I need to cancel
             // the edit. The tricky thing here is that as part of this call
             // I cannot end up calling tree.edit(null) the way that the standard
--- a/javafx-ui-controls/src/javafx/scene/control/TreeTableCell.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeTableCell.java	Wed Jun 05 08:08:10 2013 -0400
@@ -259,6 +259,8 @@
 
     /** {@inheritDoc} */
     @Override public void startEdit() {
+        if (isEditing()) return;
+
         final TreeTableView<S> table = getTreeTableView();
         final TreeTableColumn<S,T> column = getTableColumn();
         if (! isEditable() ||
@@ -266,7 +268,9 @@
                 (column != null && ! getTableColumn().isEditable())) {
             return;
         }
-        
+
+        updateItem();
+
         // it makes sense to get the cell into its editing state before firing
         // the event to listeners below, so that's what we're doing here
         // by calling super.startEdit().
@@ -504,9 +508,7 @@
                 !tableColumn.isVisible() ||
                 tableView.getRoot() == null) {
             
-            if (! isEmpty()) {
-                updateItem(null, true);
-            }
+            updateItem(null, true);
             return;
         } else {
             currentObservableValue = tableColumn.getCellObservableValue(index);
--- a/javafx-ui-controls/test/com/sun/javafx/scene/control/infrastructure/VirtualFlowTestUtils.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/infrastructure/VirtualFlowTestUtils.java	Wed Jun 05 08:08:10 2013 -0400
@@ -49,7 +49,7 @@
 import com.sun.javafx.tk.Toolkit;
 
 public class VirtualFlowTestUtils {
-    
+
     public static void assertListContainsItemsInOrder(final List items, final Object... expected) {
         assertEquals(expected.length, items.size());
         for (int i = 0; i < expected.length; i++) {
@@ -57,10 +57,10 @@
             assertEquals(expected[i], item);
         }
     }
-    
+
     public static void clickOnRow(final Control control, int row, KeyModifier... modifiers) {
         IndexedCell cell = VirtualFlowTestUtils.getCell(control, row);
-        
+
         if ((cell instanceof TableRow) || (cell instanceof TreeTableRow)) {
             for (Node n : cell.getChildrenUnmodifiable()) {
                 if (! (n instanceof IndexedCell)) {
@@ -74,27 +74,50 @@
             MouseEventFirer.fireMousePressAndRelease(cell, modifiers);
         }
     }
-    
+
     public static void assertRowsEmpty(final Control control, final int startRow, final int endRow) {
         assertRows(control, startRow, endRow, true);
     }
-    
+
     public static void assertRowsNotEmpty(final Control control, final int startRow, final int endRow) {
         assertRows(control, startRow, endRow, false);
     }
-    
+
     public static void assertCellEmpty(IndexedCell cell) {
-        final String text = cell.getText();
-//        System.out.println("assertCellEmpty: " + cell.getIndex() + " : " + text);
-        assertTrue("Expected null, found '" + text + "'", text == null || text.isEmpty());
+        if (cell instanceof TableRow || cell instanceof TreeTableRow) {
+            for (Node n : cell.getChildrenUnmodifiable()) {
+                if (! (n instanceof IndexedCell)) {
+                    continue;
+                }
+                IndexedCell<?> childCell = (IndexedCell<?>)n;
+                assertCellEmpty(childCell);
+            }
+        } else {
+            final String text = cell.getText();
+            assertTrue("Expected null, found '" + text + "'", text == null || text.isEmpty());
+
+            final Node graphic = cell.getGraphic();
+            assertTrue("Expected null graphic, found " + graphic, graphic == null);
+        }
     }
-    
+
     public static void assertCellNotEmpty(IndexedCell cell) {
-        final String text = cell.getText();
-//        System.out.println("assertCellNotEmpty: " + cell.getIndex() + " : " + text);
-        assertTrue("Expected a non-null, found '" + text + "'", text != null && ! text.isEmpty());
+        if (cell instanceof TableRow || cell instanceof TreeTableRow) {
+            for (Node n : cell.getChildrenUnmodifiable()) {
+                if (! (n instanceof IndexedCell)) {
+                    continue;
+                }
+                IndexedCell<?> childCell = (IndexedCell<?>)n;
+                assertCellNotEmpty(childCell);
+            }
+        } else {
+            final String text = cell.getText();
+            final Node graphic = cell.getGraphic();
+            assertTrue("Expected a non-null text or graphic property",
+                       (text != null && ! text.isEmpty()) || graphic != null);
+        }
     }
-    
+
     private static void assertRows(final Control control, final int startRow, final int endRow, final boolean expectEmpty) {
         Callback<IndexedCell<?>, Void> callback = new Callback<IndexedCell<?>, Void>() {
             @Override public Void call(IndexedCell<?> indexedCell) {
@@ -112,7 +135,7 @@
                         assertCellNotEmpty(childCell);
                     }
                 }
-                
+
                 if (! hasChildrenCell) {
                     if (expectEmpty) {
                         assertCellEmpty(indexedCell);
@@ -123,17 +146,17 @@
                 return null;
             }
         };
-        
+
         assertCallback(control, startRow, endRow, callback);
     }
-    
+
     public static void assertCellTextEquals(final Control control, final int index, final String... expected) {
         if (expected == null || expected.length == 0) return;
-        
+
         Callback<IndexedCell<?>, Void> callback = new Callback<IndexedCell<?>, Void>() {
             @Override public Void call(IndexedCell<?> indexedCell) {
                 if (indexedCell.getIndex() != index) return null;
-        
+
                 if (expected.length == 1) {
                     assertEquals(expected[0], indexedCell.getText());
                 } else {
@@ -145,7 +168,7 @@
                             jump++;
                             continue;
                         }
-                        
+
                         text = ((IndexedCell) childNode).getText();
                         assertEquals(expected[i], text);
                     }
@@ -153,24 +176,24 @@
                 return null;
             }
         };
-        
+
         assertCallback(control, index, index + 1, callback);
     }
-    
+
     public static void assertTableCellTextEquals(final Control control, final int row, final int column, final String expected) {
         Callback<IndexedCell<?>, Void> callback = new Callback<IndexedCell<?>, Void>() {
             @Override public Void call(IndexedCell<?> indexedCell) {
                 if (indexedCell.getIndex() != row) return null;
-                
+
                 IndexedCell cell = (IndexedCell) indexedCell.getChildrenUnmodifiable().get(column);
                 assertEquals(expected, cell.getText());
                 return null;
             }
         };
-        
+
         assertCallback(control, row, row + 1, callback);
     }
-    
+
     // used by TreeView / TreeTableView to ensure the correct indentation
     // (although note that it has only been developed so far for TreeView)
     public static void assertLayoutX(final Control control, final int startRow, final int endRow, final double expectedLayoutX) {
@@ -191,42 +214,56 @@
                 return null;
             }
         };
-        
+
         assertCallback(control, startRow, endRow, callback);
     }
-    
+
     public static int getCellCount(final Control control) {
         return getVirtualFlow(control).getCellCount();
     }
-    
+
     public static IndexedCell getCell(final Control control, final int index) {
         return getVirtualFlow(control).getCell(index);
     }
-    
+
+    public static IndexedCell getCell(final Control control, final int row, final int column) {
+        IndexedCell rowCell = getVirtualFlow(control).getCell(row);
+        int count = 0;
+        for (Node n : rowCell.getChildrenUnmodifiable()) {
+            if (! (n instanceof IndexedCell)) {
+                continue;
+            }
+            count++;
+            if (count < column) continue;
+            return (IndexedCell) n;
+        }
+        return null;
+    }
+
     public static void assertCallback(final Control control, final int startRow, final int endRow, final Callback<IndexedCell<?>, Void> callback) {
         VirtualFlow<?> flow = getVirtualFlow(control);
-        
-//        Region clippedContainer = (Region) flow.getChildrenUnmodifiable().get(0);
-//        Group sheet = (Group) clippedContainer.getChildrenUnmodifiable().get(0);
-        
-//        final int sheetSize = sheet.getChildren().size();
+
+        //        Region clippedContainer = (Region) flow.getChildrenUnmodifiable().get(0);
+        //        Group sheet = (Group) clippedContainer.getChildrenUnmodifiable().get(0);
+
+        //        final int sheetSize = sheet.getChildren().size();
         final int sheetSize = flow.getCellCount();
         final int end = endRow == -1 ? sheetSize : Math.min(endRow, sheetSize);
         for (int row = startRow; row < end; row++) {
             // old approach:
             // callback.call((IndexedCell<?>)sheet.getChildren().get(row));
-            
+
             // new approach:
             IndexedCell cell = flow.getCell(row);
-//            System.out.println("cell index: " + cell.getIndex());
+            //            System.out.println("cell index: " + cell.getIndex());
             callback.call(cell);
         }
     }
-    
+
     public static void assertCellCount(final Control control, final int expected) {
         assertEquals(getVirtualFlow(control).getCellCount(), expected);
     }
-    
+
     public static VirtualFlow<?> getVirtualFlow(Control control) {
         Group group = new Group();
         Scene scene = new Scene(group);
@@ -237,23 +274,23 @@
         group.getChildren().setAll(control);
         stage.show();
 
-//        Toolkit.getToolkit().firePulse();
-        
+        //        Toolkit.getToolkit().firePulse();
+
         VirtualFlow<?> flow;
         if (control instanceof ComboBox) {
             final ComboBox cb = (ComboBox) control;
             final ComboBoxListViewSkin skin = (ComboBoxListViewSkin) cb.getSkin();
             control = skin.getListView();
         }
-        
+
         flow = (VirtualFlow<?>)control.lookup("#virtual-flow");
-        
+
         stage.close();
         stage = null;
-        
+
         return flow;
     }
-    
+
     public static VirtualScrollBar getVirtualFlowVerticalScrollbar(final Control control) {
         VirtualFlow<?> flow = getVirtualFlow(control);
         VirtualScrollBar scrollBar = null;
@@ -264,8 +301,8 @@
                 }
             }
         }
-        
-//        Toolkit.getToolkit().firePulse();
+
+        //        Toolkit.getToolkit().firePulse();
         return scrollBar;
     }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/CellTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/CellTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -242,6 +242,9 @@
     }
 
     @Test public void startEditWhenEditableIsTrue() {
+        if ((cell instanceof TableCell)) {
+            ((TableCell) cell).lockItemOnEdit = true;
+        }
         cell.updateItem("Apples", false);
         cell.startEdit();
         assertTrue(cell.isEditing());
@@ -255,6 +258,9 @@
     }
 
     @Test public void startEditWhileAlreadyEditingIsIgnored() {
+        if (cell instanceof TableCell) {
+            ((TableCell) cell).lockItemOnEdit = true;
+        }
         cell.updateItem("Apples", false);
         cell.startEdit();
         cell.startEdit();
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -31,11 +31,16 @@
 
 import java.util.List;
 
+import javafx.beans.property.ReadOnlyStringWrapper;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 import javafx.scene.input.KeyCode;
 import javafx.stage.Stage;
+import javafx.util.Callback;
 
+import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -1094,4 +1099,42 @@
         assertEquals(3, fm.getFocusedIndex());
         assertEquals(2, getAnchor());
     }
+
+    private int rt29849_start_count = 0;
+    private int rt29849_cancel_count = 0;
+    @Test public void test_rt29849() {
+        listView.setEditable(true);
+
+        listView.setOnEditStart(new EventHandler<ListView.EditEvent<String>>() {
+            @Override public void handle(ListView.EditEvent<String> t) {
+                rt29849_start_count++;
+            }
+        });
+        listView.setOnEditCancel(new EventHandler<ListView.EditEvent<String>>() {
+            @Override public void handle(ListView.EditEvent<String> t) {
+                rt29849_cancel_count++;
+            }
+        });
+
+        // initially the counts should be zero
+        assertEquals(0, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        IndexedCell cell = VirtualFlowTestUtils.getCell(listView, 0);
+        assertTrue(cell.isEditable());
+        assertFalse(cell.isEditing());
+        assertEquals(0, cell.getIndex());
+
+        // do an edit, start count should be one, cancel still zero
+        listView.edit(0);
+        assertTrue(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        // cancel edit, now both counts should be 1
+        keyboard.doKeyPress(KeyCode.ESCAPE);
+        assertFalse(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(1, rt29849_cancel_count);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -37,6 +37,7 @@
 import java.util.Arrays;
 
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
@@ -44,7 +45,9 @@
 import javafx.collections.ObservableList;
 import javafx.geometry.Orientation;
 import javafx.scene.Node;
+import javafx.scene.control.cell.CheckBoxListCell;
 import javafx.scene.layout.StackPane;
+import javafx.util.Callback;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,42 +63,42 @@
 public class ListViewTest {
     private ListView<String> listView;
     private MultipleSelectionModel<String> sm;
-    
+
     @Before public void setup() {
         listView = new ListView<String>();
         sm = listView.getSelectionModel();
     }
-    
-    
+
+
     /*********************************************************************
      * Tests for the constructors                                        *
      ********************************************************************/
-    
+
     @Test public void noArgConstructorSetsTheStyleClass() {
         assertStyleClassContains(listView, "list-view");
     }
-    
+
     @Test public void noArgConstructorSetsNonNullSelectionModel() {
         assertNotNull(sm);
     }
-    
+
     @Test public void noArgConstructorSetsNonNullItems() {
         assertNotNull(listView.getItems());
     }
-    
+
     @Test public void noArgConstructor_selectedItemIsNull() {
         assertNull(sm.getSelectedItem());
     }
-    
+
     @Test public void noArgConstructor_selectedIndexIsNegativeOne() {
         assertEquals(-1, sm.getSelectedIndex());
     }
-    
+
     @Test public void singleArgConstructorSetsTheStyleClass() {
         final ListView<String> b2 = new ListView<String>(FXCollections.observableArrayList("Hi"));
         assertStyleClassContains(b2, "list-view");
     }
-    
+
     @Test public void singleArgConstructorSetsNonNullSelectionModel() {
         final ListView<String> b2 = new ListView<String>(FXCollections.observableArrayList("Hi"));
         assertNotNull(b2.getSelectionModel());
@@ -105,27 +108,27 @@
         final ListView<String> b2 = new ListView<String>(null);
         assertNull(b2.getItems());
     }
-    
+
     @Test public void singleArgConstructorTakesItems() {
         ObservableList<String> items = FXCollections.observableArrayList("Hi");
         final ListView<String> b2 = new ListView<String>(items);
         assertSame(items, b2.getItems());
     }
-    
+
     @Test public void singleArgConstructor_selectedItemIsNull() {
         final ListView<String> b2 = new ListView<String>(FXCollections.observableArrayList("Hi"));
         assertNull(b2.getSelectionModel().getSelectedItem());
     }
-    
+
     @Test public void singleArgConstructor_selectedIndexIsNegativeOne() {
         final ListView<String> b2 = new ListView<String>(FXCollections.observableArrayList("Hi"));
         assertEquals(-1, b2.getSelectionModel().getSelectedIndex());
     }
-    
+
     /*********************************************************************
      * Tests for selection model                                         *
      ********************************************************************/
-    
+
     @Test public void selectionModelCanBeNull() {
         listView.setSelectionModel(null);
         assertNull(listView.getSelectionModel());
@@ -143,14 +146,14 @@
         listView.setSelectionModel(sm);
         assertSame(sm, sm);
     }
-    
+
     @Test public void canSetSelectedItemToAnItemEvenWhenThereAreNoItems() {
         final String randomString = new String("I AM A CRAZY RANDOM STRING");
         sm.select(randomString);
         assertEquals(-1, sm.getSelectedIndex());
         assertSame(randomString, sm.getSelectedItem());
     }
-        
+
     @Test public void canSetSelectedItemToAnItemNotInTheDataModel() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         final String randomString = new String("I AM A CRAZY RANDOM STRING");
@@ -158,21 +161,21 @@
         assertEquals(-1, sm.getSelectedIndex());
         assertSame(randomString, sm.getSelectedItem());
     }
-        
+
     @Test public void settingTheSelectedItemToAnItemInItemsResultsInTheCorrectSelectedIndex() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select("Orange");
         assertEquals(1, sm.getSelectedIndex());
         assertSame("Orange", sm.getSelectedItem());
     }
-    
+
     @Test public void settingTheSelectedItemToANonexistantItemAndThenSettingItemsWhichContainsItResultsInCorrectSelectedIndex() {
         sm.select("Orange");
         listView.getItems().addAll("Apple", "Orange", "Banana");
         assertEquals(1, sm.getSelectedIndex());
         assertSame("Orange", sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex0() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(0);
@@ -180,7 +183,7 @@
         assertEquals(-1, sm.getSelectedIndex());
         assertEquals(null, sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex2() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(2);
@@ -188,73 +191,73 @@
         assertEquals(-1, sm.getSelectedIndex());
         assertEquals(null, sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectedItemRemainsAccurateWhenItemsAreCleared() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(2);
         listView.getItems().clear();
         assertNull(sm.getSelectedItem());
         assertEquals(-1, sm.getSelectedIndex());
-        
+
         listView.getItems().addAll("Kiwifruit", "Mandarin", "Pineapple");
         sm.select(2);
         assertEquals("Pineapple", sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionShiftsDownWhenOneNewItemIsAdded() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(1);
         assertEquals(1, sm.getSelectedIndex());
         assertEquals("Orange", sm.getSelectedItem());
-        
+
         listView.getItems().add(0, "Kiwifruit");
         assertEquals(2, sm.getSelectedIndex());
         assertEquals("Orange", sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionShiftsDownWhenMultipleNewItemAreAdded() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(1);
         assertEquals(1, sm.getSelectedIndex());
         assertEquals("Orange", sm.getSelectedItem());
-        
+
         listView.getItems().addAll(0, Arrays.asList("Kiwifruit", "Pineapple", "Mandarin"));
         assertEquals("Orange", sm.getSelectedItem());
         assertEquals(4, sm.getSelectedIndex());
     }
-    
+
     @Test public void ensureSelectionShiftsUpWhenOneItemIsRemoved() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(1);
         assertEquals(1, sm.getSelectedIndex());
         assertEquals("Orange", sm.getSelectedItem());
-        
+
         listView.getItems().remove("Apple");
         assertEquals(0, sm.getSelectedIndex());
         assertEquals("Orange", sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionShiftsUpWheMultipleItemsAreRemoved() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.select(2);
         assertEquals(2, sm.getSelectedIndex());
         assertEquals("Banana", sm.getSelectedItem());
-        
+
         listView.getItems().removeAll(Arrays.asList("Apple", "Orange"));
         assertEquals(0, sm.getSelectedIndex());
         assertEquals("Banana", sm.getSelectedItem());
     }
-    
+
     @Test public void ensureSelectionIsCorrectWhenItemsChange() {
         listView.setItems(FXCollections.observableArrayList("Item 1"));
         sm.select(0);
         assertEquals("Item 1", sm.getSelectedItem());
-        
+
         listView.setItems(FXCollections.observableArrayList("Item 2"));
         assertEquals(-1, sm.getSelectedIndex());
         assertEquals(null, sm.getSelectedItem());
     }
-    
+
     @Test public void test_rt15793() {
         // ListView selectedIndex is 0 although the items list is empty
         final ListView lv = new ListView();
@@ -266,18 +269,18 @@
         list.remove(0);
         assertEquals(-1, lv.getSelectionModel().getSelectedIndex());
     }
-    
+
     @Test public void test_rt17522_focusShouldMoveWhenItemAddedAtFocusIndex() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
         lv.getItems().add("row1");
         fm.focus(0);
         assertTrue(fm.isFocused(0));
-        
+
         lv.getItems().add(0, "row0");
         assertTrue(fm.isFocused(1));
     }
-    
+
     @Test public void test_rt17522_focusShouldMoveWhenItemAddedBeforeFocusIndex() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
@@ -285,13 +288,13 @@
         fm.focus(1);
         assertTrue(fm.isFocused(1));
         assertEquals("row2", fm.getFocusedItem());
-        
+
         lv.getItems().add(1, "row0");
         assertTrue(fm.isFocused(2));
         assertEquals("row2", fm.getFocusedItem());
         assertFalse(fm.isFocused(1));
     }
-    
+
     @Test public void test_rt17522_focusShouldNotMoveWhenItemAddedAfterFocusIndex() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
@@ -299,25 +302,25 @@
         fm.focus(0);
         assertTrue(fm.isFocused(0));
         assertEquals("row1", fm.getFocusedItem());
-        
+
         lv.getItems().add(1, "row2");
         assertTrue(fm.isFocused(0));
         assertEquals("row1", fm.getFocusedItem());
         assertFalse(fm.isFocused(1));
     }
-    
+
     @Test public void test_rt17522_focusShouldBeResetWhenFocusedItemIsRemoved() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
         lv.getItems().add("row1");
         fm.focus(0);
         assertTrue(fm.isFocused(0));
-        
+
         lv.getItems().remove("row1");
         assertTrue(fm.getFocusedIndex() == -1);
         assertNull(fm.getFocusedItem());
     }
-    
+
     @Test public void test_rt17522_focusShouldMoveWhenItemRemovedBeforeFocusIndex() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
@@ -325,12 +328,12 @@
         fm.focus(1);
         assertTrue(fm.isFocused(1));
         assertEquals("row2", fm.getFocusedItem());
-        
+
         lv.getItems().remove("row1");
         assertTrue(fm.isFocused(0));
         assertEquals("row2", fm.getFocusedItem());
     }
-    
+
     @Test public void test_rt17522_focusShouldNotMoveWhenItemRemovedAfterFocusIndex() {
         final ListView lv = new ListView();
         FocusModel fm = lv.getFocusModel();
@@ -338,12 +341,12 @@
         fm.focus(0);
         assertTrue(fm.isFocused(0));
         assertEquals("row1", fm.getFocusedItem());
-        
+
         lv.getItems().remove("row2");
         assertTrue(fm.isFocused(0));
         assertEquals("row1", fm.getFocusedItem());
     }
-    
+
     @Test public void test_rt18385() {
         listView.getItems().addAll("row1", "row2", "row3");
         sm.select(1);
@@ -351,47 +354,47 @@
         assertEquals(1, sm.getSelectedIndices().size());
         assertEquals(1, sm.getSelectedItems().size());
     }
-    
+
     @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsFalse() {
         listView.setEditable(false);
         listView.edit(1);
         assertEquals(-1, listView.getEditingIndex());
     }
-    
+
     @Test public void test_rt18339_onlyEditWhenListViewIsEditable_editableIsTrue() {
         listView.setEditable(true);
         listView.edit(1);
         assertEquals(1, listView.getEditingIndex());
     }
-    
+
     @Test public void test_rt14451() {
         listView.getItems().addAll("Apple", "Orange", "Banana");
         sm.setSelectionMode(SelectionMode.MULTIPLE);
         sm.selectRange(0, 2); // select from 0 (inclusive) to 2 (exclusive)
         assertEquals(2, sm.getSelectedIndices().size());
     }
-    
+
     private int rt_18969_hitCount = 0;
     @Test public void test_rt18969() {
         rt_18969_hitCount = 0;
         ObservableList<String> emptyModel = FXCollections.observableArrayList();
         listView.setItems(emptyModel);
         assertTrue(listView.getItems().isEmpty());
-        
+
         sm.selectedItemProperty().addListener(new ChangeListener<String>() {
             @Override public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
                 rt_18969_hitCount++;
             }
         });
-        
+
         ObservableList<String> mod = FXCollections.observableArrayList();
         mod.add(System.currentTimeMillis()+"");
         listView.getItems().setAll(mod);
-        
+
         sm.select(0);
         assertTrue(sm.isSelected(0));
         assertEquals(1, rt_18969_hitCount);
-        
+
         // sleep for 100ms so that the currentTimeMillis is guaranteed to be
         // a different value than the first one
         try {
@@ -399,76 +402,76 @@
         } catch (InterruptedException ex) {
             ex.printStackTrace();
         }
-        
+
         // the list is totally changing (it is being cleared), so we should 
         // be nulling out the selection model state
         mod = FXCollections.observableArrayList();
         mod.add(System.currentTimeMillis()+"");
         listView.getItems().setAll(mod);
-        
+
         // it should be two, as there is no null event in between (although there
         // used to be, so the test used to be for three hits)
         assertEquals(2, rt_18969_hitCount);
     }
-    
+
     @Test public void test_rt21586() {
         listView.getItems().setAll("Apple", "Orange", "Banana");
         listView.getSelectionModel().select(1);
         assertEquals(1, listView.getSelectionModel().getSelectedIndex());
         assertEquals("Orange", listView.getSelectionModel().getSelectedItem());
-        
+
         listView.getItems().setAll("Kiwifruit", "Pineapple", "Grape");
         assertEquals(-1, listView.getSelectionModel().getSelectedIndex());
         assertNull(listView.getSelectionModel().getSelectedItem());
     }
-    
+
     @Test public void test_rt27820_1() {
         listView.getItems().setAll("Apple", "Orange");
         listView.getSelectionModel().select(0);
         assertEquals(1, listView.getSelectionModel().getSelectedItems().size());
         assertEquals("Apple", listView.getSelectionModel().getSelectedItem());
-        
+
         listView.getItems().clear();
         assertEquals(0, listView.getSelectionModel().getSelectedItems().size());
         assertNull(listView.getSelectionModel().getSelectedItem());
     }
-    
+
     @Test public void test_rt27820_2() {
         listView.getItems().setAll("Apple", "Orange");
         listView.getSelectionModel().select(1);
         assertEquals(1, listView.getSelectionModel().getSelectedItems().size());
         assertEquals("Orange", listView.getSelectionModel().getSelectedItem());
-        
+
         listView.getItems().clear();
         assertEquals(0, listView.getSelectionModel().getSelectedItems().size());
         assertNull(listView.getSelectionModel().getSelectedItem());
     }
-    
+
     @Test public void test_rt28534() {
         ListView<Person> list = new ListView<Person>();
         list.setItems(FXCollections.observableArrayList(
-            new Person("Jacob", "Smith", "jacob.smith@example.com"),
-            new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
-            new Person("Ethan", "Williams", "ethan.williams@example.com"),
-            new Person("Emma", "Jones", "emma.jones@example.com"),
-            new Person("Michael", "Brown", "michael.brown@example.com")));
-        
+                new Person("Jacob", "Smith", "jacob.smith@example.com"),
+                new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
+                new Person("Ethan", "Williams", "ethan.williams@example.com"),
+                new Person("Emma", "Jones", "emma.jones@example.com"),
+                new Person("Michael", "Brown", "michael.brown@example.com")));
+
         VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 5); // rows 0 - 5 should be filled
         VirtualFlowTestUtils.assertRowsEmpty(list, 5, -1); // rows 5+ should be empty
-        
+
         // now we replace the data and expect the cells that have no data
         // to be empty
         list.setItems(FXCollections.observableArrayList(
-            new Person("*_*Emma", "Jones", "emma.jones@example.com"),
-            new Person("_Michael", "Brown", "michael.brown@example.com")));
-        
+                new Person("*_*Emma", "Jones", "emma.jones@example.com"),
+                new Person("_Michael", "Brown", "michael.brown@example.com")));
+
         VirtualFlowTestUtils.assertRowsNotEmpty(list, 0, 2); // rows 0 - 2 should be filled
         VirtualFlowTestUtils.assertRowsEmpty(list, 2, -1); // rows 2+ should be empty
     }
-    
+
     @Test public void test_rt22463() {
         final ListView<RT_22463_Person> list = new ListView<RT_22463_Person>();
-        
+
         // before the change things display fine
         RT_22463_Person p1 = new RT_22463_Person();
         p1.setId(1l);
@@ -479,7 +482,7 @@
         list.setItems(FXCollections.observableArrayList(p1, p2));
         VirtualFlowTestUtils.assertCellTextEquals(list, 0, "name1");
         VirtualFlowTestUtils.assertCellTextEquals(list, 1, "name2");
-        
+
         // now we change the persons but they are still equal as the ID's don't
         // change - but the items list is cleared so the cells should update
         RT_22463_Person new_p1 = new RT_22463_Person();
@@ -493,31 +496,31 @@
         VirtualFlowTestUtils.assertCellTextEquals(list, 0, "updated name1");
         VirtualFlowTestUtils.assertCellTextEquals(list, 1, "updated name2");
     }
-    
+
     @Test public void test_rt28637() {
         ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4");
-        
+
         final ListView<String> listView = new ListView<String>();
         listView.setItems(items);
-        
+
         listView.getSelectionModel().select(0);
         assertEquals("String1", listView.getSelectionModel().getSelectedItem());
         assertEquals("String1", listView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, listView.getSelectionModel().getSelectedIndex());
-        
+
         items.remove(listView.getSelectionModel().getSelectedItem());
         assertEquals("String2", listView.getSelectionModel().getSelectedItem());
         assertEquals("String2", listView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, listView.getSelectionModel().getSelectedIndex());
     }
-    
+
     @Test public void test_rt28819_1() {
         ObservableList<String> emptyModel = FXCollections.observableArrayList();
-        
+
         final ListView<String> listView = new ListView<String>();
         listView.setItems(emptyModel);
         VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5);
-        
+
         ObservableList<String> mod = FXCollections.observableArrayList();
         String value = System.currentTimeMillis()+"";
         mod.add(value);
@@ -525,14 +528,14 @@
         VirtualFlowTestUtils.assertCellCount(listView, 1);
         VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value);
     }
-    
+
     @Test public void test_rt28819_2() {
         ObservableList<String> emptyModel = FXCollections.observableArrayList();
-        
+
         final ListView<String> listView = new ListView<String>();
         listView.setItems(emptyModel);
         VirtualFlowTestUtils.assertRowsEmpty(listView, 0, 5);
-        
+
         ObservableList<String> mod1 = FXCollections.observableArrayList();
         String value1 = System.currentTimeMillis()+"";
         mod1.add(value1);
@@ -540,7 +543,7 @@
         VirtualFlowTestUtils.assertCellCount(listView, 1);
         VirtualFlowTestUtils.assertCellTextEquals(listView, 0, value1);
     }
-    
+
     @Test public void test_rt29390() {
         ObservableList<String> items = FXCollections.observableArrayList(
                 "String1", "String2", "String3", "String4",
@@ -548,21 +551,42 @@
                 "String1", "String2", "String3", "String4",
                 "String1", "String2", "String3", "String4"
         );
-        
+
         final ListView<String> listView = new ListView<String>(items);
         listView.setMaxHeight(50);
         listView.setPrefHeight(50);
-        
+
         // we want the vertical scrollbar
         VirtualScrollBar scrollBar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(listView);
-        
+
         assertNotNull(scrollBar);
         assertTrue(scrollBar.isVisible());
         assertTrue(scrollBar.getVisibleAmount() > 0.0);
         assertTrue(scrollBar.getVisibleAmount() < 1.0);
-        
+
         // this next test is likely to be brittle, but we'll see...If it is the
         // cause of failure then it can be commented out
         assertEquals(0.125, scrollBar.getVisibleAmount(), 0.0);
     }
+
+    @Test public void test_rt30400() {
+        // create a listview that'll render cells using the check box cell factory
+        ObservableList<String> items = FXCollections.observableArrayList("String1");
+        final ListView<String> listView = new ListView<String>(items);
+        listView.setMinHeight(100);
+        listView.setPrefHeight(100);
+        listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
+            public javafx.beans.value.ObservableValue<Boolean> call(String param) {
+                return new ReadOnlyBooleanWrapper(true);
+            }
+        }));
+
+        // because only the first row has data, all other rows should be 
+        // empty (and not contain check boxes - we just check the first four here)
+        VirtualFlowTestUtils.assertRowsNotEmpty(listView, 0, 1);
+        VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(listView, 0));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 1));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 2));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(listView, 3));
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TableColumnTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableColumnTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -716,6 +716,39 @@
     }
 
     /*********************************************************************
+     * Tests for the reorderable property                                *
+     ********************************************************************/
+
+    @Test public void reorderableIsTrueByDefault() {
+        assertTrue(column.impl_isReorderable());
+        assertTrue(column.impl_reorderableProperty().get());
+    }
+
+    @Test public void reorderableCanBeSpecified() {
+        column.impl_setReorderable(false);
+        assertFalse(column.impl_isReorderable());
+        assertFalse(column.impl_reorderableProperty().get());
+    }
+
+    @Test public void reorderablePropertyBeanIsCorrect() {
+        assertSame(column, column.impl_reorderableProperty().getBean());
+    }
+
+    @Test public void reorderablePropertyNameIsCorrect() {
+        assertEquals("reorderable", column.impl_reorderableProperty().getName());
+    }
+
+    @Test public void reorderableCanBeBound() {
+        BooleanProperty other = new SimpleBooleanProperty(false);
+        column.impl_reorderableProperty().bind(other);
+        assertFalse(column.impl_isReorderable());
+        assertFalse(column.impl_reorderableProperty().get());
+        other.set(true);
+        assertTrue(column.impl_isReorderable());
+        assertTrue(column.impl_reorderableProperty().get());
+    }
+
+    /*********************************************************************
      * Tests for the comparator property                                 *
      ********************************************************************/
 
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -41,13 +41,17 @@
 
 import java.util.List;
 
+import javafx.beans.property.ReadOnlyStringWrapper;
+import javafx.beans.value.ObservableValue;
 import javafx.event.EventHandler;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 import javafx.scene.input.KeyCode;
 import javafx.scene.input.MouseEvent;
 import javafx.stage.Stage;
+import javafx.util.Callback;
 
+import com.sun.javafx.tk.Toolkit;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -1820,4 +1824,50 @@
         assertEquals(3, fm.getFocusedIndex());
         assertEquals(2, getAnchor().getRow());
     }
+
+    private int rt29849_start_count = 0;
+    private int rt29849_cancel_count = 0;
+    @Test public void test_rt29849() {
+        tableView.setEditable(true);
+        col0.setEditable(true);
+
+        col0.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String, String>, ObservableValue<String>>() {
+            @Override public ObservableValue<String> call(TableColumn.CellDataFeatures<String, String> param) {
+                return new ReadOnlyStringWrapper("DUMMY TEXT");
+            }
+        });
+
+        col0.setOnEditStart(new EventHandler<TableColumn.CellEditEvent<String, String>>() {
+            @Override public void handle(TableColumn.CellEditEvent<String, String> t) {
+                rt29849_start_count++;
+            }
+        });
+        col0.setOnEditCancel(new EventHandler<TableColumn.CellEditEvent<String, String>>() {
+            @Override public void handle(TableColumn.CellEditEvent<String, String> t) {
+                rt29849_cancel_count++;
+            }
+        });
+
+        // initially the counts should be zero
+        assertEquals(0, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        TableCell cell = (TableCell)VirtualFlowTestUtils.getCell(tableView, 0, 0);
+        cell.lockItemOnEdit = false;
+        assertTrue(cell.isEditable());
+        assertFalse(cell.isEditing());
+        assertEquals(0, cell.getIndex());
+
+        // do an edit, start count should be one, cancel still zero
+        tableView.edit(0, col0);
+        assertTrue(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        // cancel edit, now both counts should be 1
+        keyboard.doKeyPress(KeyCode.ESCAPE);
+        assertFalse(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(1, rt29849_cancel_count);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -40,12 +40,14 @@
 import java.util.Random;
 
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.event.EventHandler;
+import javafx.scene.control.cell.CheckBoxTableCell;
 import javafx.scene.control.cell.PropertyValueFactory;
 import javafx.util.Callback;
 
@@ -1296,4 +1298,31 @@
         // cause of failure then it can be commented out
         assertEquals(0.125, scrollBar.getVisibleAmount(), 0.0);
     }
+
+    @Test public void test_rt30400() {
+        // create a listview that'll render cells using the check box cell factory
+        final TableView<Person> tableView = new TableView<Person>();
+        tableView.setMinHeight(100);
+        tableView.setPrefHeight(100);
+        tableView.setItems(FXCollections.observableArrayList(
+                new Person("Jacob", "Smith", "jacob.smith@example.com")
+        ));
+
+        TableColumn firstNameCol = new TableColumn("First Name");
+        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
+        firstNameCol.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
+            public javafx.beans.value.ObservableValue<Boolean> call(Integer param) {
+                return new ReadOnlyBooleanWrapper(true);
+            }
+        }));
+        tableView.getColumns().add(firstNameCol);
+
+        // because only the first row has data, all other rows should be
+        // empty (and not contain check boxes - we just check the first four here)
+        VirtualFlowTestUtils.assertRowsNotEmpty(tableView, 0, 1);
+        VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(tableView, 0));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 1));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 2));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 3));
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeTableColumnTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeTableColumnTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -720,6 +720,39 @@
     }
 
     /*********************************************************************
+     * Tests for the reorderable property                                *
+     ********************************************************************/
+
+    @Test public void reorderableIsTrueByDefault() {
+        assertTrue(column.impl_isReorderable());
+        assertTrue(column.impl_reorderableProperty().get());
+    }
+
+    @Test public void reorderableCanBeSpecified() {
+        column.impl_setReorderable(false);
+        assertFalse(column.impl_isReorderable());
+        assertFalse(column.impl_reorderableProperty().get());
+    }
+
+    @Test public void reorderablePropertyBeanIsCorrect() {
+        assertSame(column, column.impl_reorderableProperty().getBean());
+    }
+
+    @Test public void reorderablePropertyNameIsCorrect() {
+        assertEquals("reorderable", column.impl_reorderableProperty().getName());
+    }
+
+    @Test public void reorderableCanBeBound() {
+        BooleanProperty other = new SimpleBooleanProperty(false);
+        column.impl_reorderableProperty().bind(other);
+        assertFalse(column.impl_isReorderable());
+        assertFalse(column.impl_reorderableProperty().get());
+        other.set(true);
+        assertTrue(column.impl_isReorderable());
+        assertTrue(column.impl_reorderableProperty().get());
+    }
+
+    /*********************************************************************
      * Tests for the comparator property                                 *
      ********************************************************************/
 
--- a/javafx-ui-controls/test/javafx/scene/control/TreeTableViewKeyInputTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeTableViewKeyInputTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -29,7 +29,9 @@
 import com.sun.javafx.scene.control.behavior.TreeTableViewAnchorRetriever;
 import com.sun.javafx.scene.control.infrastructure.KeyEventFirer;
 import com.sun.javafx.scene.control.infrastructure.KeyModifier;
+import com.sun.javafx.scene.control.infrastructure.MouseEventFirer;
 import com.sun.javafx.scene.control.infrastructure.StageLoader;
+import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
 import com.sun.javafx.scene.control.skin.TreeTableViewSkin;
 import com.sun.javafx.scene.control.test.Person;
 
@@ -37,14 +39,19 @@
 
 import java.util.List;
 
+import javafx.beans.property.ReadOnlyStringWrapper;
+import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
+import javafx.event.EventHandler;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 import javafx.scene.control.cell.TreeItemPropertyValueFactory;
 import javafx.scene.input.KeyCode;
 import javafx.stage.Stage;
+import javafx.util.Callback;
 
+import com.sun.javafx.tk.Toolkit;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -2333,4 +2340,49 @@
         assertEquals(3, fm.getFocusedIndex());
         assertEquals(2, getAnchor().getRow());
     }
+
+    private int rt29849_start_count = 0;
+    private int rt29849_cancel_count = 0;
+    @Test public void test_rt29849() {
+        tableView.setEditable(true);
+        col0.setEditable(true);
+
+        col0.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<String, String>, ObservableValue<String>>() {
+            @Override public ObservableValue<String> call(TreeTableColumn.CellDataFeatures<String, String> param) {
+                return new ReadOnlyStringWrapper("DUMMY TEXT");
+            }
+        });
+
+        col0.setOnEditStart(new EventHandler<TreeTableColumn.CellEditEvent<String, String>>() {
+            @Override public void handle(TreeTableColumn.CellEditEvent<String, String> t) {
+                rt29849_start_count++;
+            }
+        });
+        col0.setOnEditCancel(new EventHandler<TreeTableColumn.CellEditEvent<String, String>>() {
+            @Override public void handle(TreeTableColumn.CellEditEvent<String, String> t) {
+                rt29849_cancel_count++;
+            }
+        });
+
+        // initially the counts should be zero
+        assertEquals(0, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        IndexedCell cell = VirtualFlowTestUtils.getCell(tableView, 0, 0);
+        assertTrue(cell.isEditable());
+        assertFalse(cell.isEditing());
+        assertEquals(0, cell.getIndex());
+
+        // do an edit, start count should be one, cancel still zero
+        tableView.edit(0, col0);
+        assertTrue(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        // cancel edit, now both counts should be 1
+        keyboard.doKeyPress(KeyCode.ESCAPE);
+        assertFalse(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(1, rt29849_cancel_count);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeTableViewTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeTableViewTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -44,6 +44,7 @@
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.ReadOnlyStringWrapper;
 import javafx.beans.property.SimpleObjectProperty;
@@ -56,6 +57,7 @@
 import javafx.scene.Node;
 import javafx.scene.Scene;
 import javafx.scene.control.TreeTableView.TreeTableViewFocusModel;
+import javafx.scene.control.cell.CheckBoxTreeTableCell;
 import javafx.scene.control.cell.PropertyValueFactory;
 import javafx.scene.control.cell.TreeItemPropertyValueFactory;
 import javafx.scene.layout.HBox;
@@ -2034,4 +2036,29 @@
         assertTrue(treeTableView.getFocusModel().isFocused(10));
         assertEquals(3, sm.getSelectedIndices().size());
     }
+
+    @Test public void test_rt30400() {
+        // create a treetableview that'll render cells using the check box cell factory
+        TreeItem<String> rootItem = new TreeItem<>("root");
+        final TreeTableView<String> tableView = new TreeTableView<String>(rootItem);
+        tableView.setMinHeight(100);
+        tableView.setPrefHeight(100);
+
+        TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
+        firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("firstName"));
+        firstNameCol.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
+            public javafx.beans.value.ObservableValue<Boolean> call(Integer param) {
+                return new ReadOnlyBooleanWrapper(true);
+            }
+        }));
+        tableView.getColumns().add(firstNameCol);
+
+        // because only the first row has data, all other rows should be
+        // empty (and not contain check boxes - we just check the first four here)
+        VirtualFlowTestUtils.assertRowsNotEmpty(tableView, 0, 1);
+        VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(tableView, 0));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 1));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 2));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 3));
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -32,11 +32,16 @@
 
 import java.util.List;
 
+import javafx.beans.property.ReadOnlyStringWrapper;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 import javafx.scene.input.KeyCode;
 import javafx.stage.Stage;
+import javafx.util.Callback;
 
+import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -1264,4 +1269,43 @@
         assertEquals(3, fm.getFocusedIndex());
         assertEquals(2, getAnchor());
     }
+
+    private int rt29849_start_count = 0;
+    private int rt29849_cancel_count = 0;
+    @Test public void test_rt29849() {
+        treeView.setEditable(true);
+
+        treeView.setOnEditStart(new EventHandler<TreeView.EditEvent<String>>() {
+            @Override public void handle(TreeView.EditEvent<String> event) {
+                rt29849_start_count++;
+            }
+        });
+        treeView.setOnEditCancel(new EventHandler<TreeView.EditEvent<String>>() {
+            @Override public void handle(TreeView.EditEvent<String> event) {
+                rt29849_cancel_count++;
+            }
+        });
+
+        // initially the counts should be zero
+        assertEquals(0, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        IndexedCell cell = VirtualFlowTestUtils.getCell(treeView, 0);
+        assertTrue(cell.isEditable());
+        assertFalse(cell.isEditing());
+        assertEquals(0, cell.getIndex());
+
+        // do an edit, start count should be one, cancel still zero
+        treeView.edit(root);
+        assertTrue(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(0, rt29849_cancel_count);
+
+        // cancel edit, now both counts should be 1
+//        keyboard.doKeyPress(KeyCode.ESCAPE);
+        treeView.edit(null);
+        assertFalse(cell.isEditing());
+        assertEquals(1, rt29849_start_count);
+        assertEquals(1, rt29849_cancel_count);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeViewTest.java	Tue Jun 04 09:43:48 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewTest.java	Wed Jun 05 08:08:10 2013 -0400
@@ -44,6 +44,7 @@
 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains;
 import static org.junit.Assert.*;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.property.SimpleStringProperty;
 import javafx.beans.value.ChangeListener;
@@ -53,6 +54,7 @@
 import javafx.scene.Group;
 import javafx.scene.Node;
 import javafx.scene.Scene;
+import javafx.scene.control.cell.CheckBoxTreeCell;
 import javafx.scene.control.cell.PropertyValueFactory;
 import javafx.scene.control.cell.TreeItemPropertyValueFactory;
 import javafx.scene.layout.VBox;
@@ -999,4 +1001,25 @@
         assertTrue(treeView.getFocusModel().isFocused(10));
         assertEquals(3, sm.getSelectedIndices().size());
     }
+
+    @Test public void test_rt30400() {
+        // create a treeview that'll render cells using the check box cell factory
+        TreeItem<String> rootItem = new TreeItem<>("root");
+        treeView.setRoot(rootItem);
+        treeView.setMinHeight(100);
+        treeView.setPrefHeight(100);
+        treeView.setCellFactory(CheckBoxTreeCell.forTreeView(new Callback<TreeItem<String>, ObservableValue<Boolean>>() {
+            public javafx.beans.value.ObservableValue<Boolean> call(TreeItem<String> param) {
+                return new ReadOnlyBooleanWrapper(true);
+            }
+        }));
+
+        // because only the first row has data, all other rows should be
+        // empty (and not contain check boxes - we just check the first four here)
+        VirtualFlowTestUtils.assertRowsNotEmpty(treeView, 0, 1);
+        VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(treeView, 0));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(treeView, 1));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(treeView, 2));
+        VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(treeView, 3));
+    }
 }