Vojtech Szocs has posted comments on this change. Change subject: webadmin,userportal: Consolidate SortedListModel vs. SearchableListModel ......................................................................
Patch Set 1: (11 comments) http://gerrit.ovirt.org/#/c/31101/1/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SearchableListModel.java File frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SearchableListModel.java: Line 837: lastSelectedItems.add(item); Line 838: } Line 839: } Line 840: Line 841: if (useComparator(value)) { > This means: "perform sorting if there's something to sort by". Patch http://gerrit.ovirt.org/#/c/28392/ changed SortedListModel.sortItems method in the following way: - SortedSet<T> sortedItems = new TreeSet<T>(comparator); - sortedItems.addAll(items); - return sortedItems; + List<T> sortedList = new ArrayList<T>(items); + Collections.sort(sortedList, comparator); + return sortedList; As a result, TreeSet (implementation of SortedSet) is no longer used when applying comparator on model's items. In consequence, condition: value instanceof SortedSet is not relevant anymore. This is why I removed above condition. In current SortedListModel.sortItems implementation, we are returning a new (sorted) ArrayList containing model's items. There is no association between (sorted) ArrayList and the comparator being used, therefore condition: "if the collection isn't already sorted by that ordering" cannot be determined. In other words, condition: value instanceof SortedSet is not relevant anymore (as I wrote above). If you have objections or believe I missed something, please elaborate. Line 842: Collection<T> sortedItems = sortItems(value); Line 843: itemsChanging(sortedItems, items); Line 844: items = sortedItems; Line 845: } else { http://gerrit.ovirt.org/#/c/31101/1/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java File frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModel.java: Line 82: // Sort items after updating comparator Line 83: Collection<T> items = getItems(); Line 84: if (items != null) { Line 85: Collection<T> maybeSortedItems = useComparator(items) ? sortItems(items) : new ArrayList<T>(items); Line 86: super.setItems(maybeSortedItems); > Instead of the two lines above: > Can't you just call setItems(items) here, as what you are doing here is > essentially the same. UiCommon models love to override parent's methods without calling appropriate super method. This leads to inconsistent & copy-paste-like code that is hard to maintain. This is the general problem of UiCommon code. In this particular case, I did not simply call setItems(items) here because: * SearchableListModel overrides setItems(items) _without_ calling super method * if I called setItems(items) here, it would ultimately delegate to overridden setItems(items) in SearchableListModel (which doesn't call super method), so setItems(items) in this class would not be called at all > The only difference I see is the is the false case where you return new > ArrayList<T>(items) instead of just items. Not entirely sure why you do this > though. Wrapping items in new ArrayList is to ensure that condition in SearchableListModel.setItems(items): if (items != value) is passed in order to update model's items. For example, in a situation when comparator has been set and now is nullified, we wrap items in new ArrayList to ensure that model's items are really updated as they should be. Line 87: } Line 88: } Line 89: Line 90: @Override Line 82: // Sort items after updating comparator Line 83: Collection<T> items = getItems(); Line 84: if (items != null) { Line 85: Collection<T> maybeSortedItems = useComparator(items) ? sortItems(items) : new ArrayList<T>(items); Line 86: super.setItems(maybeSortedItems); > I agree. Plus if this is a SearchableListModel, you'll want the overridden > Plus if this is a SearchableListModel, you'll want the overridden method to > be triggered rather than that of ListModel. This is a valid point. However, as I wrote above, the general problem is bad code - method override doesn't call super method, it re-implements any logic provided by super method. This leads to inconsistency and situations like this one. I can call setItems(items) here, but it will mean that when using SortedListModel directly (no subclass/override), items which are already sorted in setComparator will be sorted _again_ in setItems. On the other hand, I fully agree that overidden setItems should be called rather than super setItems. So I'll change this to become: setItems(maybeSortedItems); Line 87: } Line 88: } Line 89: Line 90: @Override Line 88: } Line 89: Line 90: @Override Line 91: public void setItems(Collection<T> items) { Line 92: Collection<T> maybeSortedItems = useComparator(items) ? sortItems(items) : items; > I think this should be called without the useComparator() condition. Maybe Can you be more specific? What's wrong with useComparator() check? My understanding: * useComparator is a method (encapsulating any conditions that determine whether items should be sorted or not) - we can unit test it * items _can_ be null * comparator _can_ be null Maybe the name of the method could be changed from "useComparator" to "applySorting" or similar, though. Line 93: super.setItems(maybeSortedItems); Line 94: } Line 95: Line 96: protected boolean useComparator(Collection<T> items) { Line 92: Collection<T> maybeSortedItems = useComparator(items) ? sortItems(items) : items; Line 93: super.setItems(maybeSortedItems); Line 94: } Line 95: Line 96: protected boolean useComparator(Collection<T> items) { > Considering comments here and in SearchableListModel, I think this method i I disagree, please see my comment above. Line 97: return items != null && comparator != null; Line 98: } Line 99: Line 100: protected Collection<T> sortItems(Collection<T> items) { Line 97: return items != null && comparator != null; Line 98: } Line 99: Line 100: protected Collection<T> sortItems(Collection<T> items) { Line 101: if (comparator == null) { > I don't think this is an erroneous case - if no comparator is supplied, usu Yes, when comparator is null, Collections.sort will rely on natural ordering of objects. However, in order for natural ordering of objects to work, each object must implement Comparable interface. I don't think this is the case (in general) for business entities used in UI code. I don't think that we should rely on natural ordering of objects here. Natural ordering is already reflected by server providing items in a specific order (assuming no SORTBY or equivalent param is defined). Item sorting is based solely on the use of a comparator here. Line 102: throw new IllegalStateException("comparator cannot be null"); //$NON-NLS-1$ Line 103: } Line 104: Line 105: List<T> sortedList = new ArrayList<T>(items); http://gerrit.ovirt.org/#/c/31101/1/frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java File frontend/webadmin/modules/uicommonweb/src/test/java/org/ovirt/engine/ui/uicommonweb/models/SortedListModelTest.java: Line 93: }; Line 94: } Line 95: Line 96: @Test Line 97: public void testUseComparator_nullItems() { > I don't really care about conventions, but this underscore thing won't be l AFAIK, this is a common convention used when writing Java unit tests: @Test public void testMethodName_description() { ... } The goal here is to make method name human-readable (using underscores) and meaningful. I'm using "test" prefix to indicate that this is a specific test case. This prefix could be omitted as well, but I see no harm in it. References: * http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html * http://stackoverflow.com/a/1594049 * http://stackoverflow.com/a/2861186 Test code needs special conventions because a method name acts as a description of a particular test case. People should adapt conventions to match the code. People shouldn't blindly follow the same conventions for all code. Line 98: SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(makeValueComparator()); Line 99: assertThat(testedModel.useComparator(null), is(false)); Line 100: } Line 101: Line 117: testedModel.sortItems(null); Line 118: } Line 119: Line 120: @Test(expected = IllegalStateException.class) Line 121: public void testSortItems_nullComparator() { > As per my comment in SortedListModel, I think this is a valid case that sho Please see my response to that comment, I don't think that relying on natural ordering of objects is a good idea. Line 122: SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(); Line 123: testedModel.sortItems(SOME_ITEMS); Line 124: } Line 125: Line 123: testedModel.sortItems(SOME_ITEMS); Line 124: } Line 125: Line 126: @Test Line 127: public void testSortItems_noItems() { > Common functionality between sortItems() test methods (the assertions, basi In general, you are right, but I tend to avoid over-complicating the test code. When someone sees existing "test boilerplate" code, he must first learn/understand it in order to use it. When someone sees plain-old test methods, he immediately sees what's tested and how. The goal here is to keep unit tests simple and readable. As I mentioned above, people should adapt conventions to match the code. Line 128: SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(makeValueComparator()); Line 129: Collection<TestItem> initial = Collections.emptyList(); Line 130: Collection<TestItem> sorted = testedModel.sortItems(initial); Line 131: assertNotSame(sorted, initial); Line 168: SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(); Line 169: Collection<TestItem> initial = Arrays.asList(ITEM_2_1, ITEM_2_2, ITEM_1_2, ITEM_1_1); Line 170: testedModel.setItems(initial); Line 171: Collection<TestItem> sorted = testedModel.getItems(); Line 172: assertSame(sorted, initial); > Not sure this is what I would expect. My expectation would depend on whethe > My expectation would depend on whether the class implements Comparable or not. Current patch avoids any reliance on natural ordering of objects. Also, TestItem doesn't implement Comparable interface. Sorting is done solely via explicit comparator. > Otherwise, I would expect it to either keep the same ordering or be sorted by > hashCode() or whatever Java does by default, either would be fine. Items are either sorted (via comparator) or not sorted (retaining their original order). I don't understand what you mean by "sorted by hashCode() or whatever Java does by default". > But I don't think either of these will currently happen - won't you throw an > exception for the null comparator? Model can be created with or without comparator. SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(); creates the model without comparator. The expectation is that when no comparator is set, no sorting will occur, so items you set to model are the same as items retrieved via getItems() method. I don't understand why would we throw exception for null comparator. Null comparator is completely valid, it means that no sorting will occur on items. Line 173: } Line 174: Line 175: @Test Line 176: public void testSetItems_noItems() { Line 207: public void testSetComparator_nullToValueComparator() { Line 208: SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(); Line 209: Collection<TestItem> initial = Arrays.asList(ITEM_2_1, ITEM_2_2, ITEM_1_2, ITEM_1_1); Line 210: Collection<TestItem> expected = Arrays.asList(ITEM_1_2, ITEM_1_1, ITEM_2_1, ITEM_2_2); Line 211: testedModel.setItems(initial); > Here I would plug another line to test the ordering, and then again after t As I wrote above, if a model doesn't have comparator, no sorting (i.e. no re-order) will occur. SortedListModel<TestItem> testedModel = new SortedListModel<TestItem>(); testedModel.setItems(initial); // no comparator, no sorting Collection<TestItem> actual = testedModel.getItems(); // initial and actual are the same Line 212: testedModel.setComparator(makeValueComparator()); Line 213: Collection<TestItem> sorted = testedModel.getItems(); Line 214: assertNotSame(sorted, initial); Line 215: assertThat(sorted.isEmpty(), is(false)); -- To view, visit http://gerrit.ovirt.org/31101 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: comment Gerrit-Change-Id: Id10b84c2fb476ed2240c33d72f6432335650df33 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Vojtech Szocs <vsz...@redhat.com> Gerrit-Reviewer: Alexander Wels <aw...@redhat.com> Gerrit-Reviewer: Einav Cohen <eco...@redhat.com> Gerrit-Reviewer: Gilad Chaplik <gchap...@redhat.com> Gerrit-Reviewer: Lior Vernia <lver...@redhat.com> Gerrit-Reviewer: Vojtech Szocs <vsz...@redhat.com> Gerrit-Reviewer: automat...@ovirt.org Gerrit-Reviewer: oVirt Jenkins CI Server Gerrit-HasComments: Yes _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches