package com.github.davidmoten.rtree.internal;
import static com.github.davidmoten.guavamini.Optional.of;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.github.davidmoten.guavamini.Optional;
import com.github.davidmoten.rtree.Context;
import com.github.davidmoten.rtree.Entry;
import com.github.davidmoten.rtree.Node;
import com.github.davidmoten.rtree.NonLeaf;
import com.github.davidmoten.rtree.geometry.Geometry;
import com.github.davidmoten.rtree.geometry.ListPair;
import rx.Subscriber;
import rx.functions.Func1;
public final class NonLeafHelper {
private NonLeafHelper() {
// prevent instantiation
}
public static <T, S extends Geometry> void search(Func1<? super Geometry, Boolean> criterion,
Subscriber<? super Entry<T, S>> subscriber, NonLeaf<T, S> node) {
if (!criterion.call(node.geometry().mbr()))
return;
int numChildren = node.count();
for (int i = 0; i < numChildren; i++) {
if (subscriber.isUnsubscribed()) {
return;
} else {
Node<T, S> child = node.child(i);
child.searchWithoutBackpressure(criterion, subscriber);
}
}
}
public static <T, S extends Geometry> List<Node<T, S>> add(
Entry<? extends T, ? extends S> entry, NonLeaf<T, S> node) {
Context<T, S> context = node.context();
List<Node<T, S>> children = node.children();
final Node<T, S> child = context.selector().select(entry.geometry().mbr(), children);
List<Node<T, S>> list = child.add(entry);
List<? extends Node<T, S>> children2 = Util.replace(children, child, list);
if (children2.size() <= context.maxChildren())
return Collections.singletonList(
(Node<T, S>) context.factory().createNonLeaf(children2, context));
else {
ListPair<? extends Node<T, S>> pair = context.splitter().split(children2,
context.minChildren());
return makeNonLeaves(pair, context);
}
}
private static <T, S extends Geometry> List<Node<T, S>> makeNonLeaves(
ListPair<? extends Node<T, S>> pair, Context<T, S> context) {
List<Node<T, S>> list = new ArrayList<Node<T, S>>();
list.add(context.factory().createNonLeaf(pair.group1().list(), context));
list.add(context.factory().createNonLeaf(pair.group2().list(), context));
return list;
}
public static <T, S extends Geometry> NodeAndEntries<T, S> delete(
Entry<? extends T, ? extends S> entry, boolean all, NonLeaf<T, S> node) {
// the result of performing a delete of the given entry from this node
// will be that zero or more entries will be needed to be added back to
// the root of the tree (because num entries of their node fell below
// minChildren),
// zero or more children will need to be removed from this node,
// zero or more nodes to be added as children to this node(because
// entries have been deleted from them and they still have enough
// members to be active)
List<Entry<T, S>> addTheseEntries = new ArrayList<Entry<T, S>>();
List<Node<T, S>> removeTheseNodes = new ArrayList<Node<T, S>>();
List<Node<T, S>> addTheseNodes = new ArrayList<Node<T, S>>();
int countDeleted = 0;
List<? extends Node<T, S>> children = node.children();
for (final Node<T, S> child : children) {
if (entry.geometry().intersects(child.geometry().mbr())) {
final NodeAndEntries<T, S> result = child.delete(entry, all);
if (result.node().isPresent()) {
if (result.node().get() != child) {
// deletion occurred and child is above minChildren so
// we update it
addTheseNodes.add(result.node().get());
removeTheseNodes.add(child);
addTheseEntries.addAll(result.entriesToAdd());
countDeleted += result.countDeleted();
if (!all)
break;
}
// else nothing was deleted from that child
} else {
// deletion occurred and brought child below minChildren
// so we redistribute its entries
removeTheseNodes.add(child);
addTheseEntries.addAll(result.entriesToAdd());
countDeleted += result.countDeleted();
if (!all)
break;
}
}
}
if (removeTheseNodes.isEmpty())
return new NodeAndEntries<T, S>(of(node), Collections.<Entry<T, S>> emptyList(), 0);
else {
List<Node<T, S>> nodes = Util.remove(children, removeTheseNodes);
nodes.addAll(addTheseNodes);
if (nodes.size() == 0)
return new NodeAndEntries<T, S>(Optional.<Node<T, S>> absent(), addTheseEntries,
countDeleted);
else {
NonLeaf<T, S> nd = node.context().factory().createNonLeaf(nodes, node.context());
return new NodeAndEntries<T, S>(of(nd), addTheseEntries, countDeleted);
}
}
}
}