/** * Collapses the given item. * * @param item * the item to collapse * @param position * the index of item * * @return range of rows removed by collapsing the item * @deprecated Use {@link #collapse(Object, Integer)} instead. */ @Deprecated public Range doCollapse(T item, Optional<Integer> position) { return collapse(item, position.orElse(null)); }
/** * Create new {@code HierarchyMapper} for the given data provider. May be * overridden in subclasses. * * @param dataProvider * the data provider * @param <F> * Query type * @return new {@link HierarchyMapper} */ protected <F> HierarchyMapper<T, F> createHierarchyMapper( HierarchicalDataProvider<T, F> dataProvider) { return new HierarchyMapper<>(dataProvider); }
/** * 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); }
/** * 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; }
/** * 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); }
/** * 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); }
/** * 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; }
/** * 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)))); }
/** * Expands the given item. * * @param item * the item to expand * @param position * the index of the item * @return range of rows added by expanding the item */ public Range expand(T item, Integer position) { if (doExpand(item) && position != null) { return Range.withLength(position + 1, (int) getHierarchy(item, false).count()); } return Range.emptyRange(); }
/** * 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. Calling this method will have no effect if the * item is already expanded or if it has no children. The index is provided * by the client-side or calculated from a full data request. * {@code syncAndRefresh} indicates whether the changes should be * synchronised to the client and the data provider be notified. * * @param item * the item to expand * @param index * the index of the item * @param syncAndRefresh * {@code true} if the changes should be synchronised to the * client and the data provider should be notified of the * changes, {@code false} otherwise. */ private void doExpand(T item, Integer index, boolean syncAndRefresh) { Range addedRows = mapper.expand(item, index); if (syncAndRefresh) { if (!reset && !addedRows.isEmpty()) { getClientRpc().insertRows(addedRows.getStart(), addedRows.length()); Stream<T> children = mapper.fetchItems(item, Range.withLength(0, addedRows.length())); pushData(addedRows.getStart(), children.collect(Collectors.toList())); } refresh(item); } }
/** * 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); }
/** * 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 * @return the stream of all children under the parent, includes the parent */ private Stream<T> getChildrenStream(T parent) { return getChildrenStream(parent, true); }
/** * Expands the given item. * * @param item * the item to expand * @param position * the index of item * @return range of rows added by expanding the item * @deprecated Use {@link #expand(Object, Integer)} instead. */ @Deprecated public Range doExpand(T item, Optional<Integer> position) { return expand(item, position.orElse(null)); }
@Override public List<T> fetchItemsWithRange(int offset, int limit) { // Instead of adding logic to this class, delegate request to the // separate object handling hierarchies. return mapper.fetchItems(Range.withLength(offset, limit)) .collect(Collectors.toList()); }
/** * 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)); }
/** * Getst hte full hierarchy tree starting from given node. The starting node * can be omitted. * * @param parent * the parent node to start from * @param includeParent * {@code true} to include the parent; {@code false} if not * @return the flattened hierarchy as a stream */ private Stream<T> getHierarchy(T parent, boolean includeParent) { return Stream.of(parent) .flatMap(node -> getChildrenStream(node, includeParent)); }
/** * Collapses the given item and removes its sub-hierarchy. Calling this * method will have no effect if the row is already collapsed. The index is * provided by the client-side or calculated from a full data request. * {@code syncAndRefresh} indicates whether the changes should be * synchronised to the client and the data provider be notified. * * @param item * the item to collapse * @param index * the index of the item * @param syncAndRefresh * {@code true} if the changes should be synchronised to the * client and the data provider should be notified of the * changes, {@code false} otherwise. */ private void doCollapse(T item, Integer index, boolean syncAndRefresh) { Range removedRows = mapper.collapse(item, index); if (syncAndRefresh) { if (!reset && !removedRows.isEmpty()) { getClientRpc().removeRows(removedRows.getStart(), removedRows.length()); } refresh(item); } }
/** * 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)); }