123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.v7.data.util;
-
- 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 java.util.logging.Level;
- import java.util.logging.Logger;
-
- import com.vaadin.v7.data.Container;
- import com.vaadin.v7.data.Item;
-
- /**
- * A specialized Container whose contents can be accessed like it was a
- * tree-like structure.
- *
- * @author Vaadin Ltd.
- * @since 3.0
- *
- * @deprecated Use an appropriate implementation of
- * {@code HierarchicalDataProvider} such as {@code TreeDataProvider}
- * or {@code AbstractBackEndHierarchicalDataProvider}.
- */
- @Deprecated
- @SuppressWarnings("serial")
- public class HierarchicalContainer extends IndexedContainer
- implements Container.Hierarchical {
-
- /**
- * Set of IDs of those contained Items that can't have children.
- */
- private final HashSet<Object> noChildrenAllowed = new HashSet<Object>();
-
- /**
- * Mapping from Item ID to parent Item ID.
- */
- private final HashMap<Object, Object> parent = new HashMap<Object, Object>();
-
- /**
- * Mapping from Item ID to parent Item ID for items included in the filtered
- * container.
- */
- private HashMap<Object, Object> filteredParent = null;
-
- /**
- * Mapping from Item ID to a list of child IDs.
- */
- 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;
-
- /**
- * Determines how filtering of the container is done.
- */
- private boolean includeParentsWhenFiltering = true;
-
- /**
- * Counts how many nested contents change disable calls are in progress.
- *
- * Pending events are only fired when the counter reaches zero again.
- */
- private int contentChangedEventsDisabledCount = 0;
-
- private boolean contentsChangedEventPending;
-
- /*
- * Can the specified Item have any children? Don't add a JavaDoc comment
- * here, we use the default documentation from implemented interface.
- */
- @Override
- public boolean areChildrenAllowed(Object itemId) {
- if (noChildrenAllowed.contains(itemId)) {
- return false;
- }
- return containsId(itemId);
- }
-
- /*
- * Gets the IDs of the children of the specified Item. Don't add a JavaDoc
- * comment here, we use the default documentation from implemented
- * interface.
- */
- @Override
- public Collection<?> getChildren(Object itemId) {
- LinkedList<Object> c;
-
- if (filteredChildren != null) {
- c = filteredChildren.get(itemId);
- } else {
- c = children.get(itemId);
- }
-
- if (c == null) {
- return null;
- }
- return Collections.unmodifiableCollection(c);
- }
-
- /*
- * Gets the ID of the parent of the specified Item. Don't add a JavaDoc
- * comment here, we use the default documentation from implemented
- * interface.
- */
- @Override
- public Object getParent(Object itemId) {
- if (filteredParent != null) {
- return filteredParent.get(itemId);
- }
- return parent.get(itemId);
- }
-
- /*
- * Is the Item corresponding to the given ID a leaf node? Don't add a
- * JavaDoc comment here, we use the default documentation from implemented
- * interface.
- */
- @Override
- public boolean hasChildren(Object itemId) {
- if (filteredChildren != null) {
- return filteredChildren.containsKey(itemId);
- } else {
- return children.containsKey(itemId);
- }
- }
-
- /*
- * Is the Item corresponding to the given ID a root node? Don't add a
- * JavaDoc comment here, we use the default documentation from implemented
- * interface.
- */
- @Override
- public boolean isRoot(Object itemId) {
- // 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);
- }
-
- /*
- * Gets the IDs of the root elements in the container. Don't add a JavaDoc
- * comment here, we use the default documentation from implemented
- * interface.
- */
- @Override
- public Collection<?> rootItemIds() {
- if (filteredRoots != null) {
- return Collections.unmodifiableCollection(filteredRoots);
- } else {
- return Collections.unmodifiableCollection(roots);
- }
- }
-
- /**
- * <p>
- * Sets the given Item's capability to have children. If the Item identified
- * with the itemId already has children and the areChildrenAllowed is false
- * this method fails and <code>false</code> is returned; the children must
- * be first explicitly removed with
- * {@link #setParent(Object itemId, Object newParentId)} or
- * {@link Container#removeItem(Object itemId)}.
- * </p>
- *
- * @param itemId
- * the ID of the Item in the container whose child capability is
- * to be set.
- * @param childrenAllowed
- * the boolean value specifying if the Item can have children or
- * not.
- * @return <code>true</code> if the operation succeeded, <code>false</code>
- * if not
- */
- @Override
- public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) {
-
- // Checks that the item is in the container
- if (!containsId(itemId)) {
- return false;
- }
-
- // Updates status
- if (childrenAllowed) {
- noChildrenAllowed.remove(itemId);
- } else {
- noChildrenAllowed.add(itemId);
- }
-
- return true;
- }
-
- /**
- * <p>
- * Sets the parent of an Item. The new parent item must exist and be able to
- * have children. (<code>canHaveChildren(newParentId) == true</code>). It is
- * also possible to detach a node from the hierarchy (and thus make it root)
- * by setting the parent <code>null</code>.
- * </p>
- *
- * @param itemId
- * the ID of the item to be set as the child of the Item
- * identified with newParentId.
- * @param newParentId
- * the ID of the Item that's to be the new parent of the Item
- * identified with itemId.
- * @return <code>true</code> if the operation succeeded, <code>false</code>
- * if not
- */
- @Override
- public boolean setParent(Object itemId, Object newParentId) {
-
- // Checks that the item is in the container
- if (!containsId(itemId)) {
- return false;
- }
-
- // Gets the old parent
- final Object oldParentId = parent.get(itemId);
-
- // Checks if no change is necessary
- if ((newParentId == null && oldParentId == null)
- || ((newParentId != null) && newParentId.equals(oldParentId))) {
- return true;
- }
-
- // Making root?
- if (newParentId == null) {
- // The itemId should become a root so we need to
- // - 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)
-
- // Removes from old parents children list
- final LinkedList<Object> l = children.get(oldParentId);
- if (l != null) {
- l.remove(itemId);
- if (l.isEmpty()) {
- children.remove(oldParentId);
- }
-
- }
-
- // Add to be a root
- roots.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());
- }
-
- fireItemSetChange();
-
- 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)) {
- return false;
- }
-
- // Checks that setting parent doesn't result to a loop
- Object o = newParentId;
- while (o != null && !o.equals(itemId)) {
- o = parent.get(o);
- }
- if (o != null) {
- return false;
- }
-
- // Updates parent
- parent.put(itemId, newParentId);
- LinkedList<Object> pcl = children.get(newParentId);
- if (pcl == null) {
- // Create an empty list for holding children if one were not
- // previously created
- pcl = new LinkedList<Object>();
- children.put(newParentId, pcl);
- }
- pcl.add(itemId);
-
- // Removes from old parent or root
- if (oldParentId == null) {
- roots.remove(itemId);
- } else {
- final LinkedList<Object> l = children.get(oldParentId);
- if (l != null) {
- l.remove(itemId);
- if (l.isEmpty()) {
- children.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());
- }
-
- fireItemSetChange();
-
- return true;
- }
-
- private boolean hasFilters() {
- return (filteredRoots != null);
- }
-
- /**
- * Moves a node (an Item) in the container immediately after a sibling node.
- * The two nodes must have the same parent in the container.
- *
- * @param itemId
- * the identifier of the moved node (Item)
- * @param siblingId
- * the identifier of the reference node (Item), after which the
- * other node will be located
- */
- public void moveAfterSibling(Object itemId, Object siblingId) {
- Object parent2 = getParent(itemId);
- LinkedList<Object> childrenList;
- if (parent2 == null) {
- childrenList = roots;
- } else {
- childrenList = children.get(parent2);
- }
- if (siblingId == null) {
- childrenList.remove(itemId);
- childrenList.addFirst(itemId);
-
- } else {
- int oldIndex = childrenList.indexOf(itemId);
- int indexOfSibling = childrenList.indexOf(siblingId);
- if (indexOfSibling != -1 && oldIndex != -1) {
- int newIndex;
- if (oldIndex > indexOfSibling) {
- newIndex = indexOfSibling + 1;
- } else {
- newIndex = indexOfSibling;
- }
- childrenList.remove(oldIndex);
- childrenList.add(newIndex, itemId);
- } else {
- throw new IllegalArgumentException(
- "Given identifiers no not have the same parent.");
- }
- }
- fireItemSetChange();
-
- }
-
- @Override
- public Object addItem() {
- disableContentsChangeEvents();
- try {
- 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 itemId;
- } finally {
- enableAndFireContentsChangeEvents();
- }
- }
-
- @Override
- protected void fireItemSetChange(Container.ItemSetChangeEvent event) {
- if (contentsChangeEventsOn()) {
- super.fireItemSetChange(event);
- } else {
- contentsChangedEventPending = true;
- }
- }
-
- private boolean contentsChangeEventsOn() {
- return contentChangedEventsDisabledCount == 0;
- }
-
- private void disableContentsChangeEvents() {
- contentChangedEventsDisabledCount++;
- }
-
- private void enableAndFireContentsChangeEvents() {
- if (contentChangedEventsDisabledCount <= 0) {
- getLogger().log(Level.WARNING,
- "Mismatched calls to disable and enable contents change events in HierarchicalContainer");
- contentChangedEventsDisabledCount = 0;
- } else {
- contentChangedEventsDisabledCount--;
- }
- if (contentChangedEventsDisabledCount == 0) {
- if (contentsChangedEventPending) {
- fireItemSetChange();
- }
- contentsChangedEventPending = false;
- }
- }
-
- @Override
- public Item addItem(Object itemId) {
- disableContentsChangeEvents();
- try {
- final Item item = super.addItem(itemId);
- if (item == null) {
- return null;
- }
-
- roots.add(itemId);
-
- if (filteredRoots != null) {
- if (passesFilters(itemId)) {
- filteredRoots.add(itemId);
- }
- }
- return item;
- } finally {
- enableAndFireContentsChangeEvents();
- }
- }
-
- @Override
- public boolean removeAllItems() {
- disableContentsChangeEvents();
- try {
- final boolean success = super.removeAllItems();
-
- if (success) {
- roots.clear();
- parent.clear();
- children.clear();
- noChildrenAllowed.clear();
- if (filteredRoots != null) {
- filteredRoots = null;
- }
- if (filteredChildren != null) {
- filteredChildren = null;
- }
- if (filteredParent != null) {
- filteredParent = null;
- }
- }
- return success;
- } finally {
- enableAndFireContentsChangeEvents();
- }
- }
-
- @Override
- public boolean removeItem(Object itemId) {
- disableContentsChangeEvents();
- try {
- final boolean success = super.removeItem(itemId);
-
- if (success) {
- // 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 become root
- // nodes
- LinkedList<Object> childNodeIds = children.remove(itemId);
- if (childNodeIds != null) {
- if (filteredChildren != null) {
- filteredChildren.remove(itemId);
- }
- for (Object childId : childNodeIds) {
- setParent(childId, null);
- }
- }
-
- // 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);
-
- if (c.isEmpty()) {
- children.remove(parentItemId);
- }
-
- // 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(itemId);
- if (f.isEmpty()) {
- filteredChildren.remove(parentItemId);
- }
- }
- }
- }
- }
- parent.remove(itemId);
- if (filteredParent != null) {
- // Item id no longer has a parent as the item id is not in
- // the container.
- filteredParent.remove(itemId);
- }
- noChildrenAllowed.remove(itemId);
- }
-
- return success;
- } finally {
- enableAndFireContentsChangeEvents();
- }
- }
-
- /**
- * Removes the Item identified by given itemId and all its children.
- *
- * @see #removeItem(Object)
- * @param itemId
- * the identifier of the Item to be removed
- * @return true if the operation succeeded
- */
- public boolean removeItemRecursively(Object itemId) {
- disableContentsChangeEvents();
- try {
- boolean removeItemRecursively = removeItemRecursively(this, itemId);
- return removeItemRecursively;
- } finally {
- enableAndFireContentsChangeEvents();
- }
- }
-
- /**
- * Removes the Item identified by given itemId and all its children from the
- * given Container.
- *
- * @param container
- * the container where the item is to be removed
- * @param itemId
- * the identifier of the Item to be removed
- * @return true if the operation succeeded
- */
- public static boolean removeItemRecursively(
- Container.Hierarchical container, Object itemId) {
- boolean success = true;
- Collection<?> children2 = container.getChildren(itemId);
- if (children2 != null) {
- Object[] array = children2.toArray();
- for (int i = 0; i < array.length; i++) {
- boolean removeItemRecursively = removeItemRecursively(container,
- array[i]);
- if (!removeItemRecursively) {
- success = false;
- }
- }
- }
- // remove the root of subtree if children where succesfully removed
- if (success) {
- success = container.removeItem(itemId);
- }
- return success;
-
- }
-
- @Override
- protected void doSort() {
- super.doSort();
-
- Collections.sort(roots, getItemSorter());
- for (LinkedList<Object> childList : children.values()) {
- Collections.sort(childList, getItemSorter());
- }
- }
-
- /**
- * 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;
- if (filteredRoots != null) {
- // Currently filtered so needs to be re-filtered
- doFilterContainer(true);
- }
- }
-
- @Override
- protected boolean doFilterContainer(boolean hasFilters) {
- if (!hasFilters) {
- // All filters removed
- filteredRoots = null;
- filteredChildren = null;
- filteredParent = null;
-
- return super.doFilterContainer(hasFilters);
- }
-
- // Reset data structures
- filteredRoots = new LinkedList<Object>();
- filteredChildren = new HashMap<Object, LinkedList<Object>>();
- filteredParent = new HashMap<Object, Object>();
-
- if (includeParentsWhenFiltering) {
- // Filter so that parents for items that match the filter are also
- // included
- HashSet<Object> includedItems = new HashSet<Object>();
- 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<Object> filteredItemIds = new LinkedHashSet<Object>(
- getItemIds());
-
- 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;
- }
- }
-
- /**
- * 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<Object> parentToChildrenList = filteredChildren
- .get(parentItemId);
- if (parentToChildrenList == null) {
- parentToChildrenList = new LinkedList<Object>();
- 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<Object> includedItems) {
- LinkedList<Object> 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<Object> includedItems) {
- boolean toBeIncluded = passesFilters(itemId);
-
- LinkedList<Object> 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<Object> filterOverride = null;
-
- @Override
- protected boolean passesFilters(Object itemId) {
- if (filterOverride != null) {
- return filterOverride.contains(itemId);
- } else {
- return super.passesFilters(itemId);
- }
- }
-
- private static final Logger getLogger() {
- return Logger.getLogger(HierarchicalContainer.class.getName());
- }
- }
|