From 46c6d27d58dce7ee78951c7458076e99d1b0804f Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 16 Mar 2010 08:10:55 +0000 Subject: [PATCH] Updated test case and fix for #4192 "HierarchicalContainer implements Container.Filterable but does not allow filtering" svn changeset:11901/svn branch:6.3 --- .../data/util/HierarchicalContainer.java | 271 ++++++++++++++---- .../vaadin/data/util/IndexedContainer.java | 59 +++- .../tests/components/tree/TreeFiltering.java | 42 ++- .../AbstractHierarchicalContainerTest.java | 84 ++---- .../container/TestHierarchicalContainer.java | 129 ++++++++- 5 files changed, 437 insertions(+), 148 deletions(-) diff --git a/src/com/vaadin/data/util/HierarchicalContainer.java b/src/com/vaadin/data/util/HierarchicalContainer.java index 5d29283bcb..152d9b4e58 100644 --- a/src/com/vaadin/data/util/HierarchicalContainer.java +++ b/src/com/vaadin/data/util/HierarchicalContainer.java @@ -8,7 +8,9 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.LinkedList; +import java.util.Set; import com.vaadin.data.Container; import com.vaadin.data.Item; @@ -32,10 +34,16 @@ public class HierarchicalContainer extends IndexedContainer implements private final HashSet noChildrenAllowed = new HashSet(); /** - * Mapping from Item ID to parent Item. + * Mapping from Item ID to parent Item ID. */ private final HashMap parent = new HashMap(); + /** + * Mapping from Item ID to parent Item ID for items included in the filtered + * container. + */ + private HashMap filteredParent = null; + /** * Mapping from Item ID to a list of child IDs. */ @@ -56,6 +64,11 @@ public class HierarchicalContainer extends IndexedContainer implements */ private LinkedList filteredRoots = null; + /** + * Determines how filtering of the container is done. + */ + private boolean includeParentsWhenFiltering = true; + /* * Can the specified Item have any children? Don't add a JavaDoc comment * here, we use the default documentation from implemented interface. @@ -93,6 +106,9 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public Object getParent(Object itemId) { + if (filteredParent != null) { + return filteredParent.get(itemId); + } return parent.get(itemId); } @@ -115,12 +131,17 @@ public class HierarchicalContainer extends IndexedContainer implements * interface. */ public boolean isRoot(Object itemId) { - if (filteredRoots != null && !filteredRoots.contains(itemId)) { - return false; - } - - if (parent.containsKey(itemId)) { - return false; + // If the container is filtered the itemId must be among filteredRoots + // to be a root. + if (filteredRoots != null) { + if (!filteredRoots.contains(itemId)) { + return false; + } + } else { + // Container is not filtered + if (parent.containsKey(itemId)) { + return false; + } } return containsId(itemId); @@ -211,8 +232,7 @@ public class HierarchicalContainer extends IndexedContainer implements // Making root? if (newParentId == null) { // The itemId should become a root so we need to - // - Remove it from the old parent's children list (also filtered - // list) + // - Remove it from the old parent's children list // - Add it as a root // - Remove it from the item -> parent list (parent is null for // roots) @@ -225,28 +245,21 @@ public class HierarchicalContainer extends IndexedContainer implements children.remove(itemId); } - if (filteredChildren != null) { - LinkedList f = filteredChildren.get(itemId); - if (f != null) { - f.remove(itemId); - if (f.isEmpty()) { - filteredChildren.remove(f); - } - } - } } // Add to be a root roots.add(itemId); - if (filteredRoots != null) { - if (passesFilters(itemId)) { - filteredRoots.add(itemId); - } - } // Updates parent parent.remove(itemId); + if (hasFilters()) { + // Refilter the container if setParent is called when filters + // are applied. Changing parent can change what is included in + // the filtered version (if includeParentsWhenFiltering==true). + doFilterContainer(hasFilters()); + } + fireContentsChange(-1); return true; @@ -286,17 +299,6 @@ public class HierarchicalContainer extends IndexedContainer implements } pcl.add(itemId); - // Add children list for filtered case also - if (filteredChildren != null) { - LinkedList f = filteredChildren.get(newParentId); - if (f == null) { - // Create an empty list for holding children if one were not - // previously created - f = new LinkedList(); - filteredChildren.put(newParentId, f); - } - } - // Removes from old parent or root if (oldParentId == null) { roots.remove(itemId); @@ -308,15 +310,13 @@ public class HierarchicalContainer extends IndexedContainer implements children.remove(oldParentId); } } - if (filteredChildren != null) { - LinkedList f = filteredChildren.get(oldParentId); - if (f != null) { - f.remove(itemId); - if (f.isEmpty()) { - filteredChildren.remove(oldParentId); - } - } - } + } + + if (hasFilters()) { + // Refilter the container if setParent is called when filters + // are applied. Changing parent can change what is included in + // the filtered version (if includeParentsWhenFiltering==true). + doFilterContainer(hasFilters()); } fireContentsChange(-1); @@ -324,6 +324,10 @@ public class HierarchicalContainer extends IndexedContainer implements return true; } + private boolean hasFilters() { + return (filteredRoots != null); + } + /** * TODO javadoc * @@ -550,6 +554,33 @@ public class HierarchicalContainer extends IndexedContainer implements } } + /** + * Used to control how filtering works. @see + * {@link #setIncludeParentsWhenFiltering(boolean)} for more information. + * + * @return true if all parents for items that match the filter are included + * when filtering, false if only the matching items are included + */ + public boolean isIncludeParentsWhenFiltering() { + return includeParentsWhenFiltering; + } + + /** + * Controls how the filtering of the container works. Set this to true to + * make filtering include parents for all matched items in addition to the + * items themselves. Setting this to false causes the filtering to only + * include the matching items and make items with excluded parents into root + * items. + * + * @param includeParentsWhenFiltering + * true to include all parents for items that match the filter, + * false to only include the matching items + */ + public void setIncludeParentsWhenFiltering( + boolean includeParentsWhenFiltering) { + this.includeParentsWhenFiltering = includeParentsWhenFiltering; + } + /* * Overridden to provide filtering for root & children items. * @@ -558,32 +589,154 @@ public class HierarchicalContainer extends IndexedContainer implements * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering() */ @Override - protected void updateContainerFiltering() { - super.updateContainerFiltering(); + protected boolean doFilterContainer(boolean hasFilters) { + if (!hasFilters) { + // All filters removed + filteredRoots = null; + filteredChildren = null; + return super.doFilterContainer(hasFilters); + } + + // Reset data structures filteredRoots = new LinkedList(); filteredChildren = new HashMap>(); + filteredParent = new HashMap(); + + if (includeParentsWhenFiltering) { + // Filter so that parents for items that match the filter are also + // included + HashSet includedItems = new HashSet(); + for (Object rootId : roots) { + if (filterIncludingParents(rootId, includedItems)) { + filteredRoots.add(rootId); + addFilteredChildrenRecursively(rootId, includedItems); + } + } + // includedItemIds now contains all the item ids that should be + // included. Filter IndexedContainer based on this + filterOverride = includedItems; + super.doFilterContainer(hasFilters); + filterOverride = null; + + return true; + } else { + // Filter by including all items that pass the filter and make items + // with no parent new root items + + // Filter IndexedContainer first so getItemIds return the items that + // match + super.doFilterContainer(hasFilters); + + LinkedHashSet filteredItemIds = new LinkedHashSet( + getItemIds()); - // Filter root item ids - for (Object rootId : roots) { - if (passesFilters(rootId)) { - filteredRoots.add(rootId); + for (Object itemId : filteredItemIds) { + Object itemParent = parent.get(itemId); + if (itemParent == null || !filteredItemIds.contains(itemParent)) { + // Parent is not included or this was a root, in both cases + // this should be a filtered root + filteredRoots.add(itemId); + } else { + // Parent is included. Add this to the children list (create + // it first if necessary) + addFilteredChild(itemParent, itemId); + } } + + return true; } + } - // Filter children - for (Object parent : children.keySet()) { - if (passesFilters(parent)) { - LinkedList filtered = new LinkedList(); - filteredChildren.put(parent, filtered); - for (Object child : children.get(parent)) { - if (passesFilters(child)) { - filtered.add(child); - } - } + /** + * Adds the given childItemId as a filteredChildren for the parentItemId and + * sets it filteredParent. + * + * @param parentItemId + * @param childItemId + */ + private void addFilteredChild(Object parentItemId, Object childItemId) { + LinkedList parentToChildrenList = filteredChildren + .get(parentItemId); + if (parentToChildrenList == null) { + parentToChildrenList = new LinkedList(); + filteredChildren.put(parentItemId, parentToChildrenList); + } + filteredParent.put(childItemId, parentItemId); + parentToChildrenList.add(childItemId); + + } + + /** + * Recursively adds all items in the includedItems list to the + * filteredChildren map in the same order as they are in the children map. + * Starts from parentItemId and recurses down as long as child items that + * should be included are found. + * + * @param parentItemId + * The item id to start recurse from. Not added to a + * filteredChildren list + * @param includedItems + * Set containing the item ids for the items that should be + * included in the filteredChildren map + */ + private void addFilteredChildrenRecursively(Object parentItemId, + HashSet includedItems) { + LinkedList childList = children.get(parentItemId); + if (childList == null) { + return; + } + + for (Object childItemId : childList) { + if (includedItems.contains(childItemId)) { + addFilteredChild(parentItemId, childItemId); + addFilteredChildrenRecursively(childItemId, includedItems); } } + } + /** + * Scans the itemId and all its children for which items should be included + * when filtering. All items which passes the filters are included. + * Additionally all items that have a child node that should be included are + * also themselves included. + * + * @param itemId + * @param includedItems + * @return true if the itemId should be included in the filtered container. + */ + private boolean filterIncludingParents(Object itemId, + HashSet includedItems) { + boolean toBeIncluded = passesFilters(itemId); + + LinkedList childList = children.get(itemId); + if (childList != null) { + for (Object childItemId : children.get(itemId)) { + toBeIncluded |= filterIncludingParents(childItemId, + includedItems); + } + } + + if (toBeIncluded) { + includedItems.add(itemId); + } + return toBeIncluded; } + private Set filterOverride = null; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.IndexedContainer#passesFilters(java.lang.Object) + */ + @Override + protected boolean passesFilters(Object itemId) { + if (filterOverride != null) { + return filterOverride.contains(itemId); + } else { + return super.passesFilters(itemId); + } + } } diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java index bd3c14180d..2f6bae9851 100644 --- a/src/com/vaadin/data/util/IndexedContainer.java +++ b/src/com/vaadin/data/util/IndexedContainer.java @@ -58,7 +58,7 @@ public class IndexedContainer implements Container.Indexed, private ArrayList itemIds = new ArrayList(); /** List of item ids that passes the filtering */ - private LinkedHashSet filteredItemIds = null; + private LinkedHashSet filteredItemIds = null; /** * Linked list of ordered Property IDs. @@ -329,9 +329,8 @@ public class IndexedContainer implements Container.Indexed, // this optimization is why some code is duplicated with // addItemAtInternalIndex() - final Item item = new IndexedContainerItem(itemId); if (filteredItemIds != null) { - if (passesFilters(item)) { + if (passesFilters(itemId)) { filteredItemIds.add(itemId); } } @@ -339,7 +338,7 @@ public class IndexedContainer implements Container.Indexed, // Sends the event fireContentsChange(itemIds.size() - 1); - return item; + return new IndexedContainerItem(itemId); } /** @@ -1560,18 +1559,42 @@ public class IndexedContainer implements Container.Indexed, } } - protected void updateContainerFiltering() { + /** + * Called when the filters have changed or when another event that effects + * filtering has taken place. Updates internal data structures and fires an + * item set change if necessary. + */ + private void updateContainerFiltering() { // Clearing filters? - if (filters == null || filters.isEmpty()) { + boolean hasFilters = (filters != null && !filters.isEmpty()); + + if (doFilterContainer(hasFilters)) { + fireContentsChange(-1); + } + } + + /** + * Filters the data in the container and updates internal data structures. + * This method should reset any internal data structures and then repopulate + * them so {@link #getItemIds()} and other methods only return the filtered + * items. + * + * @param hasFilters + * true if filters has been set for the container, false + * otherwise + * @return true if the item set has changed as a result of the filtering + */ + protected boolean doFilterContainer(boolean hasFilters) { + if (!hasFilters) { filteredItemIds = null; if (filters != null) { filters = null; - fireContentsChange(-1); + return true; } - return; - } + return false; + } // Reset filtered list if (filteredItemIds == null) { filteredItemIds = new LinkedHashSet(); @@ -1587,14 +1610,22 @@ public class IndexedContainer implements Container.Indexed, } } - fireContentsChange(-1); - } + return true; - protected final boolean passesFilters(Object itemId) { - return passesFilters(new IndexedContainerItem(itemId)); } - private boolean passesFilters(Item item) { + /** + * Checks if the given itemId passes the filters set for the container. The + * caller should make sure the itemId exists in the container. For + * non-existing itemIds the behavior is undefined. + * + * @param itemId + * An itemId that exists in the container. + * @return true if the itemId passes all filters or no filters are set, + * false otherwise. + */ + protected boolean passesFilters(Object itemId) { + IndexedContainerItem item = new IndexedContainerItem(itemId); if (filters == null) { return true; } diff --git a/tests/src/com/vaadin/tests/components/tree/TreeFiltering.java b/tests/src/com/vaadin/tests/components/tree/TreeFiltering.java index 495e6cfb96..25d7a510f1 100644 --- a/tests/src/com/vaadin/tests/components/tree/TreeFiltering.java +++ b/tests/src/com/vaadin/tests/components/tree/TreeFiltering.java @@ -1,9 +1,12 @@ package com.vaadin.tests.components.tree; import com.vaadin.data.Item; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.util.HierarchicalContainer; import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; import com.vaadin.ui.Tree; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; @@ -39,6 +42,17 @@ public class TreeFiltering extends TestBase { ccTree.expandItem(o); } + final CheckBox filterType = new CheckBox( + "Include parent when filtering", true); + filterType.addListener(new ValueChangeListener() { + + public void valueChange(ValueChangeEvent event) { + cont.setIncludeParentsWhenFiltering(((CheckBox) event + .getProperty()).booleanValue()); + } + }); + addComponent(filterType); + final Button b = new Button("Add filter 'foo'", new ClickListener() { public void buttonClick(final ClickEvent event) { cont.addContainerFilter("caption", "foo", true, false); @@ -62,7 +76,33 @@ public class TreeFiltering extends TestBase { }); addComponent(num); - final Button r = new Button("Remove filter", new ClickListener() { + final Button num2 = new Button("Add filter '0-'", new ClickListener() { + public void buttonClick(final ClickEvent event) { + cont.addContainerFilter("caption", "0-", true, false); + + } + }); + + addComponent(num2); + final Button num3 = new Button("Add filter 'Number 4'", + new ClickListener() { + public void buttonClick(final ClickEvent event) { + cont.addContainerFilter("caption", "Number 4", true, + false); + + } + }); + + addComponent(num3); + final Button p1 = new Button("Set Number 3 parent to Number 0", + new ClickListener() { + public void buttonClick(final ClickEvent event) { + cont.setParent(3, 0); + + } + }); + addComponent(p1); + final Button r = new Button("Remove filters", new ClickListener() { public void buttonClick(final ClickEvent event) { cont.removeAllContainerFilters(); diff --git a/tests/src/com/vaadin/tests/server/container/AbstractHierarchicalContainerTest.java b/tests/src/com/vaadin/tests/server/container/AbstractHierarchicalContainerTest.java index 7aeef3e1b1..a85e0dd34b 100644 --- a/tests/src/com/vaadin/tests/server/container/AbstractHierarchicalContainerTest.java +++ b/tests/src/com/vaadin/tests/server/container/AbstractHierarchicalContainerTest.java @@ -28,12 +28,14 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { * down to all available nodes. * @param expectedRootSize * Expected number of root items + * @param rootsHaveChildren + * true if all roots have children, false otherwise (skips some + * asserts) */ - private void validateHierarchicalContainer(Hierarchical container, + protected void validateHierarchicalContainer(Hierarchical container, Object expectedFirstItemId, Object expectedLastItemId, - Object itemIdInSet, Object itemIdNotInSet, - boolean rootsHaveChildren, int expectedSize, - int expectedTraversalSize, int expectedRootSize) { + Object itemIdInSet, Object itemIdNotInSet, int expectedSize, + int expectedRootSize, boolean rootsHaveChildren) { validateContainer(container, expectedFirstItemId, expectedLastItemId, itemIdInSet, itemIdNotInSet, expectedSize); @@ -52,21 +54,19 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { // all roots must be roots assertTrue(container.isRoot(rootId)); - // all roots have children allowed in this case - assertTrue(container.areChildrenAllowed(rootId)); - - // all roots have children in this case if (rootsHaveChildren) { + // all roots have children allowed in this case + assertTrue(container.areChildrenAllowed(rootId)); + + // all roots have children in this case Collection children = container.getChildren(rootId); assertNotNull(rootId + " should have children", children); assertTrue(rootId + " should have children", (children.size() > 0)); - // getParent for (Object childId : children) { assertEquals(container.getParent(childId), rootId); } - } else { } } @@ -83,7 +83,7 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { // removeItem of unknown items should return false assertFalse(container.removeItem(itemIdNotInSet)); - assertEquals(expectedTraversalSize, countNodes(container)); + assertEquals(expectedSize, countNodes(container)); validateHierarchy(container); } @@ -143,8 +143,8 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { int expectedSize = sampleData.length + packages; validateHierarchicalContainer(container, "com", "com.vaadin.util.SerializerHelper", - "com.vaadin.terminal.ApplicationResource", "blah", true, - expectedSize, expectedSize, 1); + "com.vaadin.terminal.ApplicationResource", "blah", + expectedSize, 1, true); } @@ -166,8 +166,8 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { int expectedSize = sampleData.length + packages; validateHierarchicalContainer(container, "com", "com.vaadin.util.SerializerHelper", - "com.vaadin.terminal.ApplicationResource", "blah", true, - expectedSize, expectedSize, 1); + "com.vaadin.terminal.ApplicationResource", "blah", + expectedSize, 1, true); sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME }, new boolean[] { true }); @@ -175,58 +175,8 @@ public class AbstractHierarchicalContainerTest extends AbstractContainerTest { validateHierarchicalContainer(container, "com.vaadin.terminal.gwt.server.ApplicationPortlet2", "com.vaadin.data.util.ObjectProperty", - "com.vaadin.terminal.ApplicationResource", "blah", true, - expectedSize, expectedSize, 1); - - } - - protected void testHierarchicalFiltering(Container.Hierarchical container) { - Container.Filterable filterable = (Container.Filterable) container; - - initializeContainer(container); - - // Filter by "contains ab" - filterable.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); - - // 20 items should remain in the container but the root should be - // filtered - int expectedSize = 20; - int expectedTraversalSize = 0; - int expectedRoots = 0; - - validateHierarchicalContainer(container, - "com.vaadin.data.BufferedValidatable", - "com.vaadin.ui.TabSheet", - "com.vaadin.terminal.gwt.client.Focusable", "blah", true, - expectedSize, expectedTraversalSize, expectedRoots); - - // filter out every second item except hierarchy items - filterable.removeAllContainerFilters(); - filterable.addContainerFilter(ID_NUMBER, "1", false, false); - - int packages = 21; - int other = sampleData.length / 2; - - expectedSize = packages + other; - expectedRoots = 1; - expectedTraversalSize = expectedSize; - - validateHierarchicalContainer(container, "com", "com.vaadin.util", - "com.vaadin.data.util.IndexedContainer", "blah", true, - expectedSize, expectedTraversalSize, expectedRoots); - - // Additionally remove all without 'm' in the simple name. Hierarchy is - // now one root only. - filterable.addContainerFilter(SIMPLE_NAME, "m", false, false); - - expectedSize = 27; - expectedRoots = 1; - expectedTraversalSize = 1; - - validateHierarchicalContainer(container, "com", - "com.vaadin.ui.UriFragmentUtility", - "com.vaadin.terminal.gwt.client.ui.TreeImages", "blah", false, - expectedSize, expectedTraversalSize, expectedRoots); + "com.vaadin.terminal.ApplicationResource", "blah", + expectedSize, 1, true); } diff --git a/tests/src/com/vaadin/tests/server/container/TestHierarchicalContainer.java b/tests/src/com/vaadin/tests/server/container/TestHierarchicalContainer.java index 0f60e133c1..02973b35a6 100644 --- a/tests/src/com/vaadin/tests/server/container/TestHierarchicalContainer.java +++ b/tests/src/com/vaadin/tests/server/container/TestHierarchicalContainer.java @@ -13,10 +13,6 @@ public class TestHierarchicalContainer extends testContainerFiltering(new HierarchicalContainer()); } - public void testHierarchicalFiltering() { - testHierarchicalFiltering(new HierarchicalContainer()); - } - public void testSorting() { testContainerSorting(new HierarchicalContainer()); } @@ -33,8 +29,127 @@ public class TestHierarchicalContainer extends testContainerSortingAndFiltering(new HierarchicalContainer()); } - // public void testHierarchicalSortingAndFiltering() { - // testHierarchicalSortingAndFiltering(new HierarchicalContainer()); - // } + public void testHierarchicalFilteringWithParents() { + HierarchicalContainer container = new HierarchicalContainer(); + initializeContainer(container); + container.setIncludeParentsWhenFiltering(true); + + // Filter by "contains ab" + container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); + + // 20 items match the filters and the have 8 parents that should also be + // included + // only one root "com" should exist + // filtered + int expectedSize = 29; + int expectedRoots = 1; + + validateHierarchicalContainer(container, "com", + "com.vaadin.ui.TabSheet", + "com.vaadin.terminal.gwt.client.Focusable", "blah", + expectedSize, expectedRoots, true); + + // only include .gwt.client classes + container.removeAllContainerFilters(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", + false, false); + + int packages = 6; + int classes = 112; + + expectedSize = packages + classes; + expectedRoots = 1; + + validateHierarchicalContainer(container, "com", + "com.vaadin.terminal.gwt.client.WidgetSet", + "com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical", + "blah", expectedSize, expectedRoots, true); + + // Additionally remove all without 'm' in the simple name. + container.addContainerFilter(SIMPLE_NAME, "m", false, false); + + expectedSize = 7 + 18; + expectedRoots = 1; + + validateHierarchicalContainer( + container, + "com", + "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", + "com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer", + "blah", expectedSize, expectedRoots, true); + + } + public void testHierarchicalFilteringWithoutParents() { + HierarchicalContainer container = new HierarchicalContainer(); + + initializeContainer(container); + container.setIncludeParentsWhenFiltering(false); + + // Filter by "contains ab" + container.addContainerFilter(SIMPLE_NAME, "ab", false, false); + + // 20 items match the filter. + // com.vaadin.data.BufferedValidatable + // com.vaadin.data.Validatable + // com.vaadin.terminal.gwt.client.Focusable + // com.vaadin.terminal.gwt.client.Paintable + // com.vaadin.terminal.gwt.client.ui.Table + // com.vaadin.terminal.gwt.client.ui.VLabel + // com.vaadin.terminal.gwt.client.ui.VScrollTable + // com.vaadin.terminal.gwt.client.ui.VTablePaging + // com.vaadin.terminal.gwt.client.ui.VTabsheet + // com.vaadin.terminal.gwt.client.ui.VTabsheetBase + // com.vaadin.terminal.gwt.client.ui.VTabsheetPanel + // com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent + // com.vaadin.terminal.Paintable + // com.vaadin.terminal.Scrollable + // com.vaadin.terminal.Sizeable + // com.vaadin.terminal.VariableOwner + // com.vaadin.ui.Label + // com.vaadin.ui.Table + // com.vaadin.ui.TableFieldFactory + // com.vaadin.ui.TabSheet + // all become roots. + int expectedSize = 20; + int expectedRoots = 20; + + validateHierarchicalContainer(container, + "com.vaadin.data.BufferedValidatable", + "com.vaadin.ui.TabSheet", + "com.vaadin.terminal.gwt.client.ui.VTabsheetBase", "blah", + expectedSize, expectedRoots, false); + + // only include .gwt.client classes + container.removeAllContainerFilters(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", + false, false); + + int packages = 3; + int classes = 110; + + expectedSize = packages + classes; + expectedRoots = 35 + 1; // com.vaadin.terminal.gwt.client.ui + + // com.vaadin.terminal.gwt.client.* + + // Sorting is case insensitive + validateHierarchicalContainer(container, + "com.vaadin.terminal.gwt.client.ApplicationConfiguration", + "com.vaadin.terminal.gwt.client.WidgetSet", + "com.vaadin.terminal.gwt.client.ui.VOptionGroup", "blah", + expectedSize, expectedRoots, false); + + // Additionally remove all without 'P' in the simple name. + container.addContainerFilter(SIMPLE_NAME, "P", false, false); + + expectedSize = 13; + expectedRoots = expectedSize; + + validateHierarchicalContainer(container, + "com.vaadin.terminal.gwt.client.Paintable", + "com.vaadin.terminal.gwt.client.ui.VTabsheetPanel", + "com.vaadin.terminal.gwt.client.ui.VPopupCalendar", "blah", + expectedSize, expectedRoots, false); + + } } -- 2.39.5