*/
private final HashMap<Object, LinkedList<Object>> children = new HashMap<Object, LinkedList<Object>>();
+ /**
+ * Mapping from Item ID to a list of child IDs when filtered
+ */
+ private HashMap<Object, LinkedList<Object>> filteredChildren = null;
+
/**
* List that contains all root elements of the container.
*/
private final LinkedList<Object> roots = new LinkedList<Object>();
+ /**
+ * List that contains all filtered root elements of the container.
+ */
+ private LinkedList<Object> filteredRoots = null;
+
/*
* Can the specified Item have any children? Don't add a JavaDoc comment
* here, we use the default documentation from implemented interface.
* interface.
*/
public Collection getChildren(Object itemId) {
- final Collection c = children.get(itemId);
+ LinkedList<Object> c;
+
+ if (filteredChildren != null) {
+ c = filteredChildren.get(itemId);
+ } else {
+ c = children.get(itemId);
+ }
+
if (c == null) {
return null;
}
* interface.
*/
public boolean hasChildren(Object itemId) {
- return children.get(itemId) != null;
+ if (filteredChildren != null) {
+ return filteredChildren.containsKey(itemId);
+ } else {
+ return children.containsKey(itemId);
+ }
}
/*
* interface.
*/
public boolean isRoot(Object itemId) {
+ if (filteredRoots != null && !filteredRoots.contains(itemId)) {
+ return false;
+ }
+
if (parent.containsKey(itemId)) {
return false;
}
* interface.
*/
public Collection rootItemIds() {
- return Collections.unmodifiableCollection(roots);
+ if (filteredRoots != null) {
+ return Collections.unmodifiableCollection(filteredRoots);
+ } else {
+ return Collections.unmodifiableCollection(roots);
+ }
}
/**
return true;
}
- // Making root
+ // 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)
+ // - Add it as a root
+ // - Remove it from the item -> parent list (parent is null for roots)
+
// Removes from old parents children list
- final LinkedList l = children.get(itemId);
+ final LinkedList<Object> l = children.get(itemId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(itemId);
}
+
+ if (filteredChildren != null) {
+ LinkedList<Object> 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);
return true;
}
+ // We get here when the item should not become a root and we need to
+ // - Verify the new parent exists and can have children
+ // - Check that the new parent is not a child of the selected itemId
+ // - Updated the item -> parent mapping to point to the new parent
+ // - Remove the item from the roots list if it was a root
+ // - Remove the item from the old parent's children list if it was not a root
+
// Checks that the new parent exists in container and can have
// children
if (!containsId(newParentId) || noChildrenAllowed.contains(newParentId)) {
// Updates parent
parent.put(itemId, newParentId);
- LinkedList pcl = children.get(newParentId);
+ LinkedList<Object> pcl = children.get(newParentId);
if (pcl == null) {
- pcl = new LinkedList();
+ // Create an empty list for holding children if one were not
+ // previously created
+ pcl = new LinkedList<Object>();
children.put(newParentId, pcl);
}
pcl.add(itemId);
+ // Add children list for filtered case also
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(newParentId);
+ if (f == null) {
+ // Create an empty list for holding children if one were not
+ // previously created
+ f = new LinkedList<Object>();
+ filteredChildren.put(newParentId, f);
+ }
+ }
+
// Removes from old parent or root
if (oldParentId == null) {
roots.remove(itemId);
} else {
- final LinkedList l = children.get(oldParentId);
+ final LinkedList<Object> l = children.get(oldParentId);
if (l != null) {
l.remove(itemId);
if (l.isEmpty()) {
children.remove(oldParentId);
}
}
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(oldParentId);
+ if (f != null) {
+ f.remove(itemId);
+ if (f.isEmpty()) {
+ filteredChildren.remove(oldParentId);
+ }
+ }
+ }
}
return true;
*/
@Override
public Object addItem() {
- final Object id = super.addItem();
- if (id != null && !roots.contains(id)) {
- roots.add(id);
+ final Object itemId = super.addItem();
+ if (itemId == null) {
+ return null;
+ }
+
+ if (!roots.contains(itemId)) {
+ roots.add(itemId);
+ if (filteredRoots != null) {
+ if (passesFilters(itemId)) {
+ filteredRoots.add(itemId);
+ }
+ }
}
- return id;
+ return itemId;
}
/*
@Override
public Item addItem(Object itemId) {
final Item item = super.addItem(itemId);
- if (item != null) {
- roots.add(itemId);
+ if (item == null) {
+ return null;
}
+
+ roots.add(itemId);
+
+ if (filteredRoots != null) {
+ if (passesFilters(itemId)) {
+ filteredRoots.add(itemId);
+ }
+ }
+
return item;
}
parent.clear();
children.clear();
noChildrenAllowed.clear();
+ if (filteredRoots != null) {
+ filteredRoots = null;
+ }
+ if (filteredChildren != null) {
+ filteredChildren = null;
+ }
}
return success;
}
final boolean success = super.removeItem(itemId);
if (success) {
- if (isRoot(itemId)) {
- roots.remove(itemId);
+
+ // Remove from roots if this was a root
+ if (roots.remove(itemId)) {
+
+ // If filtering is enabled we might need to remove it from the
+ // filtered list also
+ if (filteredRoots != null) {
+ filteredRoots.remove(itemId);
+ }
+ }
+
+ // Clear the children list. Old children will now be unattached
+ // FIXME Should these be made into roots?
+ if (children.remove(itemId) != null) {
+ if (filteredChildren != null) {
+ filteredChildren.remove(itemId);
+ }
}
- children.remove(itemId);
- final Object p = parent.get(itemId);
- if (p != null) {
- final LinkedList c = children.get(p);
+
+ // Parent of the item that we are removing will contain the item id
+ // in its children list
+ final Object parentItemId = parent.get(itemId);
+ if (parentItemId != null) {
+ final LinkedList<Object> c = children.get(parentItemId);
if (c != null) {
c.remove(itemId);
+
+ // Found in the children list so might also be in the
+ // filteredChildren list
+ if (filteredChildren != null) {
+ LinkedList<Object> f = filteredChildren.get(parentItemId);
+ if (f != null) {
+ f.remove(parentItemId);
+ }
+ }
}
}
parent.remove(itemId);
}
}
+ /*
+ * Overridden to provide filtering for root & children items.
+ *
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering()
+ */
+ @Override
+ protected void updateContainerFiltering() {
+ super.updateContainerFiltering();
+
+ filteredRoots = new LinkedList<Object>();
+ filteredChildren = new HashMap<Object, LinkedList<Object>>();
+
+ // Filter root item ids
+ for (Object rootId : roots) {
+ if (passesFilters(rootId)) {
+ filteredRoots.add(rootId);
+ }
+ }
+
+ // Filter children
+ for (Object parent : children.keySet()) {
+ if (passesFilters(parent)) {
+ LinkedList<Object> filtered = new LinkedList<Object>();
+ filteredChildren.put(parent, filtered);
+ for (Object child : children.get(parent)) {
+ if (passesFilters(child)) {
+ filtered.add(child);
+ }
+ }
+ }
+ }
+
+ }
}
}
}
- private void updateContainerFiltering() {
+ protected void updateContainerFiltering() {
// Clearing filters?
if (filters == null || filters.isEmpty()) {
// Filter
for (final Iterator i = itemIds.iterator(); i.hasNext();) {
final Object id = i.next();
- if (passesFilters(new IndexedContainerItem(id))) {
+ if (passesFilters(id)) {
filteredItemIds.add(id);
}
}
fireContentsChange(-1);
}
+ protected final boolean passesFilters(Object itemId) {
+ return passesFilters(new IndexedContainerItem(itemId));
+ }
+
private boolean passesFilters(Item item) {
if (filters == null) {
return true;
\r
}\r
\r
- protected static final Object PROP1 = "PROP1";\r
- protected static final Object PROP2 = "PROP2";\r
- protected static final Object PROP3 = "PROP3";\r
+ protected static final Object FULLY_QUALIFIED_NAME = "PROP1";\r
+ protected static final Object SIMPLE_NAME = "simplename";\r
+ protected static final Object REVERSE_FULLY_QUALIFIED_NAME = "PROP2";\r
+ protected static final Object ID_NUMBER = "PROP3";\r
\r
protected void testBasicContainerOperations(Container container) {\r
initializeContainer(container);\r
initializeContainer(container);\r
\r
// Filter by "contains ab"\r
- container.addContainerFilter(PROP1, "ab", false, false);\r
+ container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false);\r
\r
validateContainer(container, "com.vaadin.data.BufferedValidatable",\r
"com.vaadin.ui.TabSheet",\r
\r
// Filter by "contains da" (reversed as ad here)\r
container.removeAllContainerFilters();\r
- container.addContainerFilter(PROP2, "ad", false, false);\r
+ container.addContainerFilter(REVERSE_FULLY_QUALIFIED_NAME, "ad", false,\r
+ false);\r
\r
validateContainer(container, "com.vaadin.data.Buffered",\r
"com.vaadin.terminal.gwt.server.ComponentSizeValidator",\r
initializeContainer(sortable);\r
\r
// Filter by "contains ab"\r
- filterable.addContainerFilter(PROP1, "ab", false, false);\r
+ filterable.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false);\r
\r
// Must be able to sort based on PROP1 for this test\r
- assertTrue(sortable.getSortableContainerPropertyIds().contains(PROP1));\r
+ assertTrue(sortable.getSortableContainerPropertyIds().contains(\r
+ FULLY_QUALIFIED_NAME));\r
\r
- sortable.sort(new Object[] { PROP1 }, new boolean[] { true });\r
+ sortable.sort(new Object[] { FULLY_QUALIFIED_NAME },\r
+ new boolean[] { true });\r
\r
validateContainer(sortable, "com.vaadin.data.BufferedValidatable",\r
"com.vaadin.ui.TableFieldFactory",\r
initializeContainer(container);\r
\r
// Must be able to sort based on PROP1 for this test\r
- assertTrue(sortable.getSortableContainerPropertyIds().contains(PROP1));\r
- assertTrue(sortable.getSortableContainerPropertyIds().contains(PROP2));\r
+ assertTrue(sortable.getSortableContainerPropertyIds().contains(\r
+ FULLY_QUALIFIED_NAME));\r
+ assertTrue(sortable.getSortableContainerPropertyIds().contains(\r
+ REVERSE_FULLY_QUALIFIED_NAME));\r
\r
- sortable.sort(new Object[] { PROP1 }, new boolean[] { true });\r
+ sortable.sort(new Object[] { FULLY_QUALIFIED_NAME },\r
+ new boolean[] { true });\r
\r
validateContainer(container, "com.vaadin.Application",\r
"com.vaadin.util.SerializerHelper",\r
"com.vaadin.terminal.ApplicationResource", "blah",\r
sampleData.length);\r
\r
- sortable.sort(new Object[] { PROP2 }, new boolean[] { true });\r
+ sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME },\r
+ new boolean[] { true });\r
\r
validateContainer(container,\r
"com.vaadin.terminal.gwt.server.ApplicationPortlet2",\r
container.removeContainerProperty(propertyId);\r
}\r
\r
- container.addContainerProperty(PROP1, String.class, "");\r
- container.addContainerProperty(PROP2, String.class, null);\r
- container.addContainerProperty(PROP3, Integer.class, null);\r
+ container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, "");\r
+ container.addContainerProperty(SIMPLE_NAME, String.class, "");\r
+ container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME,\r
+ String.class, null);\r
+ container.addContainerProperty(ID_NUMBER, Integer.class, null);\r
\r
for (int i = 0; i < sampleData.length; i++) {\r
String id = sampleData[i];\r
Item item = container.addItem(id);\r
\r
- item.getItemProperty(PROP1).setValue(sampleData[i]);\r
- item.getItemProperty(PROP2).setValue(reverse(sampleData[i]));\r
- item.getItemProperty(PROP3).setValue(i);\r
+ item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]);\r
+ item.getItemProperty(SIMPLE_NAME).setValue(\r
+ getSimpleName(sampleData[i]));\r
+ item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME).setValue(\r
+ reverse(sampleData[i]));\r
+ item.getItemProperty(ID_NUMBER).setValue(i);\r
+ }\r
+ }\r
+\r
+ protected String getSimpleName(String name) {\r
+ if (name.contains(".")) {\r
+ return name.substring(name.lastIndexOf('.') + 1);\r
+ } else {\r
+ return name;\r
}\r
}\r
\r
\r
public class AbstractHierarchicalContainerTest extends AbstractContainerTest {\r
\r
+ /**\r
+ * @param container\r
+ * The container to validate\r
+ * @param expectedFirstItemId\r
+ * Expected first item id\r
+ * @param expectedLastItemId\r
+ * Expected last item id\r
+ * @param itemIdInSet\r
+ * An item id that is in the container\r
+ * @param itemIdNotInSet\r
+ * An item id that is not in the container\r
+ * @param expectedSize\r
+ * Expected number of items in the container. Not related to\r
+ * hierarchy.\r
+ * @param expectedTraversalSize\r
+ * Expected number of items found when traversing from the roots\r
+ * down to all available nodes.\r
+ * @param expectedRootSize\r
+ * Expected number of root items\r
+ */\r
private void validateHierarchicalContainer(Hierarchical container,\r
Object expectedFirstItemId, Object expectedLastItemId,\r
- Object itemIdInSet, Object itemIdNotInSet, int expectedSize,\r
- int expectedRootSize) {\r
+ Object itemIdInSet, Object itemIdNotInSet,\r
+ boolean rootsHaveChildren, int expectedSize,\r
+ int expectedTraversalSize, int expectedRootSize) {\r
\r
validateContainer(container, expectedFirstItemId, expectedLastItemId,\r
itemIdInSet, itemIdNotInSet, expectedSize);\r
assertTrue(container.areChildrenAllowed(rootId));\r
\r
// all roots have children in this case\r
- Collection<?> children = container.getChildren(rootId);\r
- assertNotNull(rootId + " should have children", children);\r
- assertTrue(rootId + " should have children", (children.size() > 0));\r
+ if (rootsHaveChildren) {\r
+ Collection<?> children = container.getChildren(rootId);\r
+ assertNotNull(rootId + " should have children", children);\r
+ assertTrue(rootId + " should have children",\r
+ (children.size() > 0));\r
+\r
+ // getParent\r
+ for (Object childId : children) {\r
+ assertEquals(container.getParent(childId), rootId);\r
+ }\r
+ } else {\r
\r
- // getParent\r
- for (Object childId : children) {\r
- assertEquals(container.getParent(childId), rootId);\r
}\r
}\r
\r
// removeItem of unknown items should return false\r
assertFalse(container.removeItem(itemIdNotInSet));\r
\r
- assertEquals(expectedSize, countNodes(container));\r
+ assertEquals(expectedTraversalSize, countNodes(container));\r
\r
validateHierarchy(container);\r
}\r
initializeContainer(container);\r
\r
int packages = 21;\r
+ int expectedSize = sampleData.length + packages;\r
validateHierarchicalContainer(container, "com",\r
"com.vaadin.util.SerializerHelper",\r
- "com.vaadin.terminal.ApplicationResource", "blah",\r
- sampleData.length + packages, 1);\r
+ "com.vaadin.terminal.ApplicationResource", "blah", true,\r
+ expectedSize, expectedSize, 1);\r
\r
}\r
\r
initializeContainer(container);\r
\r
// Must be able to sort based on PROP1 and PROP2 for this test\r
- assertTrue(sortable.getSortableContainerPropertyIds().contains(PROP1));\r
- assertTrue(sortable.getSortableContainerPropertyIds().contains(PROP2));\r
+ assertTrue(sortable.getSortableContainerPropertyIds().contains(\r
+ FULLY_QUALIFIED_NAME));\r
+ assertTrue(sortable.getSortableContainerPropertyIds().contains(\r
+ REVERSE_FULLY_QUALIFIED_NAME));\r
\r
- sortable.sort(new Object[] { PROP1 }, new boolean[] { true });\r
+ sortable.sort(new Object[] { FULLY_QUALIFIED_NAME },\r
+ new boolean[] { true });\r
\r
int packages = 21;\r
+ int expectedSize = sampleData.length + packages;\r
validateHierarchicalContainer(container, "com",\r
"com.vaadin.util.SerializerHelper",\r
- "com.vaadin.terminal.ApplicationResource", "blah",\r
- sampleData.length + packages, 1);\r
+ "com.vaadin.terminal.ApplicationResource", "blah", true,\r
+ expectedSize, expectedSize, 1);\r
\r
- sortable.sort(new Object[] { PROP2 }, new boolean[] { true });\r
+ sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME },\r
+ new boolean[] { true });\r
\r
validateHierarchicalContainer(container,\r
"com.vaadin.terminal.gwt.server.ApplicationPortlet2",\r
"com.vaadin.data.util.ObjectProperty",\r
- "com.vaadin.terminal.ApplicationResource", "blah",\r
- sampleData.length + packages, 1);\r
+ "com.vaadin.terminal.ApplicationResource", "blah", true,\r
+ expectedSize, expectedSize, 1);\r
\r
}\r
\r
initializeContainer(container);\r
\r
// Filter by "contains ab"\r
- filterable.addContainerFilter(PROP1, "ab", false, false);\r
+ filterable.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false);\r
+\r
+ // 20 items should remain in the container but the root should be\r
+ // filtered\r
+ int expectedSize = 20;\r
+ int expectedTraversalSize = 0;\r
+ int expectedRoots = 0;\r
\r
validateHierarchicalContainer(container,\r
"com.vaadin.data.BufferedValidatable",\r
"com.vaadin.ui.TabSheet",\r
- "com.vaadin.terminal.gwt.client.Focusable", "blah", 20, 0);\r
+ "com.vaadin.terminal.gwt.client.Focusable", "blah", true,\r
+ expectedSize, expectedTraversalSize, expectedRoots);\r
\r
// filter out every second item except hierarchy items\r
filterable.removeAllContainerFilters();\r
- filterable.addContainerFilter(PROP3, "1", false, false);\r
+ filterable.addContainerFilter(ID_NUMBER, "1", false, false);\r
\r
int packages = 21;\r
int other = sampleData.length / 2;\r
+\r
+ expectedSize = packages + other;\r
+ expectedRoots = 1;\r
+ expectedTraversalSize = expectedSize;\r
+\r
validateHierarchicalContainer(container, "com", "com.vaadin.util",\r
- "com.vaadin.data.util.IndexedContainer",\r
- "com.vaadin.data.util.ObjectProperty", packages + other, 0);\r
+ "com.vaadin.data.util.IndexedContainer", "blah", true,\r
+ expectedSize, expectedTraversalSize, expectedRoots);\r
+\r
+ // Additionally remove all without 'm' in the simple name. Hierarchy is\r
+ // now one root only.\r
+ filterable.addContainerFilter(SIMPLE_NAME, "m", false, false);\r
\r
- // Additionally remove all without 'm'. Hierarchy is now one root only.\r
- filterable.addContainerFilter(PROP1, "m", false, false);\r
+ expectedSize = 27;\r
+ expectedRoots = 1;\r
+ expectedTraversalSize = 1;\r
\r
- validateHierarchicalContainer(container, "com.vaadin.data.Buffered",\r
- "com.vaadin.terminal.gwt.server.ComponentSizeValidator",\r
- "com.vaadin.data.util.IndexedContainer",\r
- "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility",\r
- packages + other, 0);\r
+ validateHierarchicalContainer(container, "com",\r
+ "com.vaadin.ui.UriFragmentUtility",\r
+ "com.vaadin.terminal.gwt.client.ui.TreeImages", "blah", false,\r
+ expectedSize, expectedTraversalSize, expectedRoots);\r
\r
}\r
\r
container.removeContainerProperty(propertyId);\r
}\r
\r
- container.addContainerProperty(PROP1, String.class, "");\r
- container.addContainerProperty(PROP2, String.class, null);\r
- container.addContainerProperty(PROP3, Integer.class, null);\r
+ container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, "");\r
+ container.addContainerProperty(SIMPLE_NAME, String.class, "");\r
+ container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME,\r
+ String.class, null);\r
+ container.addContainerProperty(ID_NUMBER, Integer.class, null);\r
\r
for (int i = 0; i < sampleData.length; i++) {\r
String id = sampleData[i];\r
if (container.addItem(path) != null) {\r
assertTrue(container.setChildrenAllowed(path, false));\r
Item item = container.getItem(path);\r
- item.getItemProperty(PROP1).setValue(path);\r
- item.getItemProperty(PROP2).setValue(reverse(path));\r
- item.getItemProperty(PROP3).setValue(1);\r
+ item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path);\r
+ item.getItemProperty(SIMPLE_NAME).setValue(getSimpleName(path));\r
+ item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME).setValue(\r
+ reverse(path));\r
+ item.getItemProperty(ID_NUMBER).setValue(1);\r
}\r
for (int j = 1; j < paths.length; j++) {\r
String parent = path;\r
assertTrue(container.setChildrenAllowed(path, false));\r
\r
Item item = container.getItem(path);\r
- item.getItemProperty(PROP1).setValue(path);\r
- item.getItemProperty(PROP2).setValue(reverse(path));\r
- item.getItemProperty(PROP3).setValue(1);\r
+ item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path);\r
+ item.getItemProperty(SIMPLE_NAME).setValue(\r
+ getSimpleName(path));\r
+ item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME)\r
+ .setValue(reverse(path));\r
+ item.getItemProperty(ID_NUMBER).setValue(1);\r
\r
}\r
assertTrue(container.setChildrenAllowed(parent, true));\r
assertNotNull(item);\r
String parent = id.substring(0, id.lastIndexOf('.'));\r
assertTrue(container.setParent(id, parent));\r
- item.getItemProperty(PROP1).setValue(sampleData[i]);\r
- item.getItemProperty(PROP2).setValue(reverse(sampleData[i]));\r
- item.getItemProperty(PROP3).setValue(i % 2);\r
+ item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]);\r
+ item.getItemProperty(SIMPLE_NAME).setValue(\r
+ getSimpleName(sampleData[i]));\r
+ item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME).setValue(\r
+ reverse(sampleData[i]));\r
+ item.getItemProperty(ID_NUMBER).setValue(i % 2);\r
}\r
}\r
\r
testContainerSorting(new HierarchicalContainer());\r
}\r
\r
+ public void testOrdered() {\r
+ testContainerOrdered(new HierarchicalContainer());\r
+ }\r
+\r
public void testHierarchicalSorting() {\r
testHierarchicalSorting(new HierarchicalContainer());\r
}\r