/** * Returns whether given item has children. * * @param item * the node to test * @return {@code true} if node has children; {@code false} if not */ public boolean hasChildren(T item) { return getDataProvider().hasChildren(item); }
/** * Find parent for the given item among open folders. * * @param item * the item * @return parent item or {@code null} for root items or if the parent is * closed */ protected T getParentOfItem(T item) { Objects.requireNonNull(item, "Can not find the parent of null"); return parentIdMap.get(getDataProvider().getId(item)); }
/** * Register parent and children items into inner structures. May be * overridden in subclasses. * * @param parent * the parent item * @param childList * list of parents children to be registered. */ protected void registerChildren(T parent, List<T> childList) { childMap.put(parent, new HashSet<>(childList)); childList.forEach( x -> parentIdMap.put(getDataProvider().getId(x), parent)); }
/** * Returns whether the given item is expanded. * * @param item * the item to test * @return {@code true} if item is expanded; {@code false} if not */ public boolean isExpanded(T item) { if (item == null) { // Root nodes are always visible. return true; } return expandedItemIds.contains(getDataProvider().getId(item)); }
/** * Finds the current index of given object. This is based on a search in * flattened version of the hierarchy. * * @param target * the target object to find * @return optional index of given object */ public Optional<Integer> getIndexOf(T target) { if (target == null) { return Optional.empty(); } final List<Object> collect = getHierarchy(null).map(provider::getId) .collect(Collectors.toList()); int index = collect.indexOf(getDataProvider().getId(target)); return Optional.ofNullable(index < 0 ? null : index); }
/** * Removes all children of an item identified by a given id. Items removed * by this method as well as the original item are all marked to be * collapsed. May be overridden in subclasses for removing obsolete data to * avoid memory leaks. * * @param id * the item id */ protected void removeChildren(Object id) { // Clean up removed nodes from child map Iterator<Entry<T, Set<T>>> iterator = childMap.entrySet().iterator(); Set<T> invalidatedChildren = new HashSet<>(); while (iterator.hasNext()) { Entry<T, Set<T>> entry = iterator.next(); T key = entry.getKey(); if (key != null && getDataProvider().getId(key).equals(id)) { invalidatedChildren.addAll(entry.getValue()); iterator.remove(); } } expandedItemIds.remove(id); invalidatedChildren.stream().map(getDataProvider()::getId) .forEach(x -> { removeChildren(x); parentIdMap.remove(x); }); }
/** * Expands the given item if it is collapsed and has children, and returns * whether this method expanded the item. * * @param item * the item to expand * @return {@code true} if this method expanded the item, {@code false} * otherwise */ private boolean doExpand(T item) { boolean expanded = false; if (!isExpanded(item) && hasChildren(item)) { expandedItemIds.add(getDataProvider().getId(item)); expanded = true; } return expanded; }
/** * The method to recursively fetch the children of given parent. Used with * {@link Stream#flatMap} to expand a stream of parent nodes into a * flattened hierarchy. * * @param parent * the parent node * @param includeParent * {@code true} to include the parent in the stream; * {@code false} if not * @return the stream of all children under the parent */ private Stream<T> getChildrenStream(T parent, boolean includeParent) { List<T> childList = Collections.emptyList(); if (isExpanded(parent)) { childList = getDirectChildren(parent).collect(Collectors.toList()); if (childList.isEmpty()) { removeChildren(parent == null ? null : getDataProvider().getId(parent)); } else { registerChildren(parent, childList); } } return combineParentAndChildStreams(parent, childList.stream().flatMap(this::getChildrenStream), includeParent); }
/** * Gets the stream of direct children for given node. * * @param parent * the parent node * @return the stream of direct children */ private Stream<T> getDirectChildren(T parent) { return doFetchDirectChildren(parent, Range.between(0, getDataProvider() .getChildCount(new HierarchicalQuery<>(filter, parent)))); }
/** * Collapses the given item. * * @param item * the item to collapse * @param position * the index of the item * * @return range of rows removed by collapsing the item */ public Range collapse(T item, Integer position) { Range removedRows = Range.emptyRange(); if (isExpanded(item)) { if (position != null) { removedRows = Range.withLength(position + 1, (int) getHierarchy(item, false).count()); } expandedItemIds.remove(getDataProvider().getId(item)); } return removedRows; }
/** * Generic method for finding direct children of a given parent, limited by * given range. * * @param parent * the parent * @param range * the range of direct children to return * @return the requested children of the given parent */ @SuppressWarnings({ "rawtypes", "unchecked" }) private Stream<T> doFetchDirectChildren(T parent, Range range) { return getDataProvider().fetchChildren(new HierarchicalQuery( range.getStart(), range.length(), getBackEndSorting(), getInMemorySorting(), getFilter(), parent)); }
@Override public void generateData(T item, JsonObject jsonObject) { JsonObject hierarchyData = Json.createObject(); int depth = getDepth(item); if (depth >= 0) { hierarchyData.put(HierarchicalDataCommunicatorConstants.ROW_DEPTH, depth); } boolean isLeaf = !getDataProvider().hasChildren(item); if (isLeaf) { hierarchyData.put(HierarchicalDataCommunicatorConstants.ROW_LEAF, true); } else { hierarchyData.put( HierarchicalDataCommunicatorConstants.ROW_COLLAPSED, !isExpanded(item)); hierarchyData.put(HierarchicalDataCommunicatorConstants.ROW_LEAF, false); hierarchyData.put( HierarchicalDataCommunicatorConstants.ROW_COLLAPSE_ALLOWED, getItemCollapseAllowedProvider().test(item)); } // add hierarchy information to row as metadata jsonObject.put( HierarchicalDataCommunicatorConstants.ROW_HIERARCHY_DESCRIPTION, hierarchyData); }