Hello Lior Vernia,

I'd like you to do a code review.  Please visit

    http://gerrit.ovirt.org/28392

to review the following change.

Change subject: webadmin,userportal: Fix client-side sort (TreeSet approach)
......................................................................

webadmin,userportal: Fix client-side sort (TreeSet approach)

Change-Id: I437e9dd7ff2afdb2d87fb0ba6c17dcb081cd965d
Signed-off-by: Vojtech Szocs <[email protected]>
Signed-off-by: Lior Vernia <[email protected]>
---
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedCollection.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java
A 
frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabDataCenterView.java
4 files changed, 361 insertions(+), 10 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/92/28392/1

diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedCollection.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedCollection.java
new file mode 100644
index 0000000..30f0cef
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedCollection.java
@@ -0,0 +1,193 @@
+package org.ovirt.engine.ui.uicommonweb.models;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Collection implementation that uses {@link TreeSet} to maintain items 
ordered by
+ * means of provided comparator with the ability to track logical position of 
items.
+ *
+ * @param <E>
+ *            The type of element maintained by this collection.
+ */
+public class SortedCollection<E> implements Collection<E>, Serializable {
+
+    public interface PositionCallback<T> {
+        int getPosition(T item);
+    }
+
+    public interface ComparatorWithPositionCallback<T> extends Comparator<T> {
+        void setPositionCallback(PositionCallback<T> positionCallback);
+    }
+
+    private static final long serialVersionUID = 6150830451408750485L;
+
+    private final SortedSet<E> items;
+    private final Map<E, Integer> positions = new HashMap<E, Integer>();
+
+    private int nextPos = 0;
+
+    SortedCollection(ComparatorWithPositionCallback<E> comparator,
+            Collection<? extends E> initialValues) {
+        this.items = new TreeSet<E>(comparator);
+
+        initComparator(comparator);
+        initPositions(initialValues);
+        addAll(initialValues);
+    }
+
+    SortedCollection(ComparatorWithPositionCallback<E> comparator) {
+        this(comparator, Collections.<E> emptyList());
+    }
+
+    void initComparator(ComparatorWithPositionCallback<E> comparator) {
+        comparator.setPositionCallback(new PositionCallback<E>() {
+            @Override
+            public int getPosition(E item) {
+                Integer pos = positions.get(item);
+                return (pos != null) ? pos.intValue() : Integer.MAX_VALUE;
+            }
+        });
+    }
+
+    void initPositions(Collection<? extends E> c) {
+        Iterator<? extends E> i = c.iterator();
+        while (i.hasNext()) {
+            positions.put(i.next(), nextPos++);
+        }
+    }
+
+    @Override
+    public int size() {
+        return items.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return items.isEmpty();
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return items.contains(o);
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return new Iterator<E>() {
+
+            private final Iterator<? extends E> i = items.iterator();
+            private E lastRet;
+
+            @Override
+            public boolean hasNext() {
+                boolean hasNext = i.hasNext();
+                if (!hasNext) {
+                    // Reference cleanup
+                    lastRet = null;
+                }
+                return hasNext;
+            }
+
+            @Override
+            public E next() {
+                lastRet = i.next();
+                return lastRet;
+            }
+
+            @Override
+            public void remove() {
+                i.remove();
+                // In the edge case when user called next() and hasNext() 
returned false,
+                // lastRet was cleaned up so we can't remove the corresponding 
mapping from
+                // positions. This isn't very likely to happen, we prefer to 
avoid keeping
+                // references to items within the iterator after hasNext() 
returned false.
+                if (lastRet != null) {
+                    SortedCollection.this.positions.remove(lastRet);
+                    lastRet = null;
+                }
+            }
+
+        };
+    }
+
+    @Override
+    public Object[] toArray() {
+        return items.toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return items.toArray(a);
+    }
+
+    @Override
+    public boolean add(E e) {
+        if (!positions.containsKey(e)) {
+            // Make new item's logical position higher than that of other items
+            positions.put(e, nextPos++);
+        }
+        return items.add(e);
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        positions.remove(o);
+        return items.remove(o);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        return items.containsAll(c);
+    }
+
+    /**
+     * Retain original AbstractCollection.addAll method
+     * implementation that adds elements one by one.
+     * <p>
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
+        boolean ret = false;
+        for (E e : c) {
+            if (add(e)) {
+                ret = true;
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        // TreeSet delegates to AbstractCollection.removeAll method
+        // which removes elements one by one via collection's iterator
+        return items.removeAll(c);
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        // TreeSet delegates to AbstractCollection.retainAll method
+        // which removes elements one by one via collection's iterator
+        return items.retainAll(c);
+    }
+
+    @Override
+    public void clear() {
+        positions.clear();
+        items.clear();
+    }
+
+    @Override
+    public String toString() {
+        return items.toString();
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java
index 3244218..07a51f3 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java
@@ -2,27 +2,45 @@
 
 import java.util.Collection;
 import java.util.Comparator;
-import java.util.SortedSet;
-import java.util.TreeSet;
 
 import org.ovirt.engine.core.common.utils.ObjectUtils;
+import 
org.ovirt.engine.ui.uicommonweb.models.SortedCollection.ComparatorWithPositionCallback;
+import 
org.ovirt.engine.ui.uicommonweb.models.SortedCollection.PositionCallback;
 
 public class SortedListModel<T> extends ListModel<T> {
 
-    private static class SortSensitiveComparator<T> implements Comparator<T> {
+    public static class SortSensitiveComparator<T> implements 
ComparatorWithPositionCallback<T> {
 
         private final Comparator<? super T> comparator;
         private final boolean sortAscending;
 
-        public SortSensitiveComparator(Comparator<? super T> comparator, 
boolean sortAscending) {
+        private PositionCallback<T> positionCallback;
+
+        SortSensitiveComparator(Comparator<? super T> comparator, boolean 
sortAscending) {
             assert comparator != null : "comparator cannot be null"; 
//$NON-NLS-1$
             this.comparator = comparator;
             this.sortAscending = sortAscending;
         }
 
         @Override
+        public void setPositionCallback(PositionCallback<T> positionCallback) {
+            this.positionCallback = positionCallback;
+        }
+
+        @Override
         public int compare(T a, T b) {
-            return sortAscending ? comparator.compare(a, b) : 
comparator.compare(b, a);
+            int ret = comparator.compare(a, b);
+
+            if (ret == 0 && positionCallback != null) {
+                ret = positionCallback.getPosition(a) - 
positionCallback.getPosition(b);
+            }
+
+            if (ret == 0) {
+                ret = a.hashCode() - b.hashCode();
+            }
+
+            ret = Integer.signum(ret);
+            return sortAscending ? ret : -ret;
         }
 
         @Override
@@ -54,7 +72,7 @@
 
     }
 
-    protected Comparator<? super T> comparator;
+    protected SortSensitiveComparator<T> comparator;
 
     public SortedListModel(Comparator<? super T> comparator) {
         super();
@@ -83,10 +101,7 @@
         if (items == null || comparator == null) {
             return items;
         }
-
-        SortedSet<T> sortedItems = new TreeSet<T>(comparator);
-        sortedItems.addAll(items);
-        return sortedItems;
+        return new SortedCollection<T>(comparator, items);
     }
 
 }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java
 
b/frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java
new file mode 100644
index 0000000..4b6fc30
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java
@@ -0,0 +1,136 @@
+package org.ovirt.engine.ui.uicommonweb.models;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.ovirt.engine.ui.uicommonweb.Configurator;
+import org.ovirt.engine.ui.uicommonweb.ILogger;
+import 
org.ovirt.engine.ui.uicommonweb.models.SortedListModel.SortSensitiveComparator;
+
+public class SortedListModelTest {
+
+    static class TestItem {
+
+        private final int value;
+        private final int salt;
+
+        TestItem(int value, int salt) {
+            this.value = value;
+            this.salt = salt;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + salt;
+            result = prime * result + value;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            TestItem other = (TestItem) obj;
+            if (salt != other.salt) {
+                return false;
+            }
+            if (value != other.value) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Item " + value + "_" + salt;
+        }
+
+    }
+
+    private static final TestItem ITEM_1_1 = new TestItem(1, 1);
+    private static final TestItem ITEM_1_2 = new TestItem(1, 2);
+    private static final TestItem ITEM_2_1 = new TestItem(2, 1);
+    private static final TestItem ITEM_2_2 = new TestItem(2, 2);
+
+    private SortedListModel<TestItem> model;
+
+    SortSensitiveComparator<TestItem> makeValueAscComparator() {
+        return new SortSensitiveComparator<TestItem>(new 
Comparator<TestItem>() {
+            @Override
+            public int compare(TestItem a, TestItem b) {
+                return a.value - b.value;
+            }
+        }, true);
+    }
+
+    @Before
+    public void setUp() {
+        this.model = new SortedListModel<TestItem>() {
+            @Override
+            protected Configurator lookupConfigurator() {
+                return null;
+            }
+
+            @Override
+            protected ILogger lookupLogger() {
+                return null;
+            }
+        };
+        model.comparator = makeValueAscComparator();
+    }
+
+    @Test
+    public void testSortItems_nullComparator() {
+        model.comparator = null;
+
+        List<TestItem> initial = Arrays.asList(ITEM_1_2, ITEM_1_1);
+        Collection<TestItem> sorted = model.sortItems(initial);
+
+        assertEquals(sorted, initial);
+    }
+
+    @Test
+    public void testSortItems_nullItems() {
+        Collection<TestItem> sorted = model.sortItems(null);
+
+        assertNull(sorted);
+    }
+
+    @Test
+    public void testSortItems_retainOrder() {
+        List<TestItem> initial = Arrays.asList(ITEM_2_1, ITEM_2_2, ITEM_1_2, 
ITEM_1_1);
+        List<TestItem> expected = Arrays.asList(ITEM_1_2, ITEM_1_1, ITEM_2_1, 
ITEM_2_2);
+        Collection<TestItem> sorted = model.sortItems(initial);
+
+        assertArrayEquals(sorted.toArray(), expected.toArray());
+
+        sorted.remove(ITEM_1_2);
+        sorted.add(ITEM_1_2);
+        expected = Arrays.asList(ITEM_1_1, ITEM_1_2, ITEM_2_1, ITEM_2_2);
+
+        assertArrayEquals(sorted.toArray(), expected.toArray());
+
+        sorted.removeAll(Arrays.asList(ITEM_1_2, ITEM_2_2));
+        sorted.addAll(Arrays.asList(ITEM_2_2, ITEM_1_2));
+
+        assertArrayEquals(sorted.toArray(), expected.toArray());
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabDataCenterView.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabDataCenterView.java
index 13bb2a3..fa91bce 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabDataCenterView.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/section/main/view/tab/MainTabDataCenterView.java
@@ -1,5 +1,6 @@
 package org.ovirt.engine.ui.webadmin.section.main.view.tab;
 
+import java.util.Comparator;
 import java.util.List;
 
 import org.ovirt.engine.core.common.businessentities.StoragePool;
@@ -54,6 +55,12 @@
                 return object.getName();
             }
         };
+        nameColumn.makeSortable(new Comparator<StoragePool>() {
+            @Override
+            public int compare(StoragePool o1, StoragePool o2) {
+                return 0; // TODO TEST
+            }
+        });
         getTable().addColumn(nameColumn, constants.nameDc(), "150px"); 
//$NON-NLS-1$
 
         CommentColumn<StoragePool> commentColumn = new 
CommentColumn<StoragePool>();


-- 
To view, visit http://gerrit.ovirt.org/28392
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I437e9dd7ff2afdb2d87fb0ba6c17dcb081cd965d
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Vojtech Szocs <[email protected]>
Gerrit-Reviewer: Lior Vernia <[email protected]>
_______________________________________________
Engine-patches mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to