changeset 5560:ca4ad7f14aeb

RT-32872: com.sun.javafx.css.BitSet retainAll yielding in an empty intersection should result in bits being set to EMPTY_SET. In other words, if bitSet.isEmpty(), then bits should be long[0]
author David Grieve<david.grieve@oracle.com>
date Tue, 29 Oct 2013 08:15:51 -0400
parents f66ed6275113
children 89e41f057bd9
files modules/graphics/src/main/java/com/sun/javafx/css/BitSet.java modules/graphics/src/test/java/com/sun/javafx/css/PseudoClassTest.java
diffstat 2 files changed, 147 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/java/com/sun/javafx/css/BitSet.java	Tue Oct 29 17:49:28 2013 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/BitSet.java	Tue Oct 29 08:15:51 2013 -0400
@@ -196,8 +196,17 @@
 
         // if index[element] == temp, then the bit was not there
         final boolean modified = (bits[element] != temp);
-        if (modified && SetListenerHelper.hasListeners(listenerHelper)) {
-            notifyObservers(t, Change.ELEMENT_REMOVED);
+        if (modified) {
+            if (SetListenerHelper.hasListeners(listenerHelper)) {
+                notifyObservers(t, Change.ELEMENT_REMOVED);
+            }
+
+            // did removing the bit leave an empty set?
+            boolean isEmpty = true;
+            for (int n=0; n<bits.length && isEmpty; n++) {
+                isEmpty &= bits[n] == 0;
+            }
+            if (isEmpty) bits = EMPTY_SET;
         }
         return modified;
     }
@@ -268,7 +277,7 @@
         // Math.max(maskOne.length, maskTwo.length) is too slow
         final int max = a < b ? b : a;
 
-        final long[] union = max > 0 ? new long[max] : null;
+        final long[] union = max > 0 ? new long[max] : EMPTY_SET;
 
         for(int n = 0; n < max; n++) {
 
@@ -312,7 +321,7 @@
                 }
             }
 
-            this.bits = union != null ? union : new long[0];
+            this.bits = union;
         }
 
         return modified;
@@ -340,8 +349,7 @@
         // Math.min(maskOne.length, maskTwo.length) is too slow
         final int max = a < b ? a : b;
 
-        final long[] intersection = max > 0 ? new long[max] : null;
-
+        final long[] intersection = max > 0 ? new long[max] : EMPTY_SET;
 
         //
         // Make sure modified is set if maskOne has more bits than maskTwo.
@@ -352,9 +360,21 @@
         //
         modified |= (maskOne.length > max);
 
+        //
+        // RT-32872 - If the intersection is empty, then set bits to the EMPTY_SET.
+        // This ensures that {a,b,c}.retainAll({}) yields bits = EMPTY_SET as
+        // opposed to bits = long[1] and bits[0] == 0.
+        //
+        // Assume isEmpty is true. If any intersection[n] != 0,
+        // isEmpty will be set to false and will remain false.
+        //
+        //
+        boolean isEmpty = true;
+
         for(int n = 0; n < max; n++) {
             intersection[n] = maskOne[n] & maskTwo[n];
             modified |= intersection[n] != maskOne[n];
+            isEmpty &= intersection[n] == 0;
         }
 
         if (modified) {
@@ -383,7 +403,7 @@
                 }
             }
 
-            this.bits = intersection != null ? intersection : new long[0];
+            this.bits = isEmpty == false ? intersection : EMPTY_SET;
         }
 
         return modified;
@@ -411,12 +431,23 @@
         // Math.min(maskOne.length, maskTwo.length) is too slow
         final int max = a < b ? a : b;
 
-        final long[] difference = max > 0 ? new long[max] : null;
+        final long[] difference = max > 0 ? new long[max] : EMPTY_SET;
+
+        //
+        // RT-32872 - If the intersection is empty, then set bits to the EMPTY_SET.
+        // This ensures that {a,b,c}.retainAll({}) yields bits = EMPTY_SET as
+        // opposed to bits = long[1] and bits[0] == 0.
+        //
+        // Assume isEmpty is true. If any difference[n] != 0,
+        // isEmpty will be set to false and will remain false.
+        //
+        //
+        boolean isEmpty = true;
 
         for(int n = 0; n < max; n++) {
             difference[n] = maskOne[n] & ~maskTwo[n];
-
             modified |= difference[n] != maskOne[n];
+            isEmpty &= difference[n] == 0;
         }
 
         if (modified) {
@@ -437,7 +468,7 @@
                 }
             }
 
-            this.bits = difference != null ? difference : new long[0];
+            this.bits = isEmpty == false ? difference : EMPTY_SET;
         }
 
         return modified;
@@ -460,13 +491,18 @@
             }
         }
 
-        bits = new long[0];
+        bits = EMPTY_SET;
     }
     
     @Override
     public int hashCode() {
         int hash = 7;
-        hash = 71 * (hash + Arrays.hashCode(this.bits));
+        if (bits.length > 0) {
+            for (int n = 0; n < bits.length; n++) {
+                final long mask = bits[n];
+                hash = 71 * hash + (int)(mask ^ (mask >>> 32));
+            }
+        }
         return hash;
     }
 
--- a/modules/graphics/src/test/java/com/sun/javafx/css/PseudoClassTest.java	Tue Oct 29 17:49:28 2013 +1300
+++ b/modules/graphics/src/test/java/com/sun/javafx/css/PseudoClassTest.java	Tue Oct 29 08:15:51 2013 -0400
@@ -1174,5 +1174,104 @@
 
     }
 
+    @Test public void testRetainAllOfEmptySetResultsInEmptySet() {
+
+        final PseudoClass[] pseudoClasses = new PseudoClass[] {
+                PseudoClass.getPseudoClass("ONE"),
+                PseudoClass.getPseudoClass("TWO"),
+                PseudoClass.getPseudoClass("THREE"),
+                PseudoClass.getPseudoClass("FOUR")
+
+        };
+
+        Set<PseudoClass> setA = new PseudoClassState();
+        for (int n=0; n<pseudoClasses.length; n++) {
+            setA.add(pseudoClasses[n]);
+        };
+
+        Set<PseudoClass> setB = new PseudoClassState();
+
+        assertTrue(setA.retainAll(setB));
+
+        assertArrayEquals(new long[0], ((PseudoClassState)setA).getBits());
+
+    }
+
+    @Test public void testRemoveAllOfSameSetResultsInEmptySet() {
+
+        final PseudoClass[] pseudoClasses = new PseudoClass[] {
+                PseudoClass.getPseudoClass("ONE"),
+                PseudoClass.getPseudoClass("TWO"),
+                PseudoClass.getPseudoClass("THREE"),
+                PseudoClass.getPseudoClass("FOUR")
+
+        };
+
+        Set<PseudoClass> setA = new PseudoClassState();
+        for (int n=0; n<pseudoClasses.length; n++) {
+            setA.add(pseudoClasses[n]);
+        };
+
+        assertTrue(setA.removeAll(setA));
+
+        assertArrayEquals(new long[0], ((PseudoClassState)setA).getBits());
+
+    }
+
+    @Test public void testRemoveLastBitResultsInEmptySet() {
+
+        final PseudoClass[] pseudoClasses = new PseudoClass[] {
+                PseudoClass.getPseudoClass("ONE"),
+                PseudoClass.getPseudoClass("TWO"),
+                PseudoClass.getPseudoClass("THREE"),
+                PseudoClass.getPseudoClass("FOUR")
+
+        };
+
+        Set<PseudoClass> setA = new PseudoClassState();
+        for (int n=0; n<pseudoClasses.length; n++) {
+            setA.add(pseudoClasses[n]);
+        };
+
+        for (int n=0; n<pseudoClasses.length; n++) {
+            assertTrue(setA.remove(pseudoClasses[n]));
+        };
+
+        assertArrayEquals(new long[0], ((PseudoClassState)setA).getBits());
+
+    }
+
+    @Test public void testRemoveLastBitByIteratorResultsInEmptySet() {
+
+        final PseudoClass[] pseudoClasses = new PseudoClass[] {
+                PseudoClass.getPseudoClass("ONE"),
+                PseudoClass.getPseudoClass("TWO"),
+                PseudoClass.getPseudoClass("THREE"),
+                PseudoClass.getPseudoClass("FOUR")
+
+        };
+
+        Set<PseudoClass> setA = new PseudoClassState();
+        for (int n=0; n<pseudoClasses.length; n++) {
+            setA.add(pseudoClasses[n]);
+        };
+
+        Iterator<PseudoClass> iterator = setA.iterator();
+        while (iterator.hasNext()) {
+            iterator.remove();
+        }
+
+        assertArrayEquals(new long[0], ((PseudoClassState)setA).getBits());
+
+    }
+
+    @Test public void testAddEmptyToEmptyResultsInEmptySet() {
+
+        Set<PseudoClass> setA = new PseudoClassState();
+        Set<PseudoClass> setB = new PseudoClassState();
+        assertFalse(setA.addAll(setB));
+        assertArrayEquals(new long[0], ((PseudoClassState)setA).getBits());
+
+    }
 
 }