graphql.util.NodeMultiZipper Maven / Gradle / Ivy
package graphql.util;
import graphql.Assert;
import graphql.PublicApi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import static graphql.Assert.assertNotEmpty;
import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertTrue;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
@PublicApi
public class NodeMultiZipper {
private final T commonRoot;
private final List> zippers;
private final NodeAdapter nodeAdapter;
public NodeMultiZipper(T commonRoot, List> zippers, NodeAdapter nodeAdapter) {
this.commonRoot = assertNotNull(commonRoot);
this.zippers = new ArrayList<>(zippers);
this.nodeAdapter = nodeAdapter;
}
public T toRootNode() {
if (zippers.size() == 0) {
return commonRoot;
}
List> curZippers = new ArrayList<>(zippers);
while (curZippers.size() > 1) {
List> deepestZippers = getDeepestZippers(curZippers);
Map>> sameParent = zipperWithSameParent(deepestZippers);
List> newZippers = new ArrayList<>();
for (Map.Entry>> entry : sameParent.entrySet()) {
NodeZipper newZipper = moveUp(entry.getKey(), entry.getValue());
Optional> zipperToBeReplaced = curZippers.stream().filter(zipper -> zipper.getCurNode() == entry.getKey()).findFirst();
zipperToBeReplaced.ifPresent(curZippers::remove);
newZippers.add(newZipper);
}
curZippers.removeAll(deepestZippers);
curZippers.addAll(newZippers);
}
assertTrue(curZippers.size() == 1, "unexpected state: all zippers must share the same root node");
return Assert.assertNotNull(curZippers.get(0).toRoot());
}
public T getCommonRoot() {
return commonRoot;
}
public List> getZippers() {
return new ArrayList<>(zippers);
}
public NodeZipper getZipperForNode(T node) {
return FpKit.findOneOrNull(zippers, zipper -> zipper.getCurNode() == node);
}
public NodeMultiZipper withReplacedZippers(List> zippers) {
return new NodeMultiZipper<>(commonRoot, zippers, this.nodeAdapter);
}
public NodeMultiZipper withNewZipper(NodeZipper newZipper) {
List> newZippers = getZippers();
newZippers.add(newZipper);
return new NodeMultiZipper<>(commonRoot, newZippers, this.nodeAdapter);
}
public NodeMultiZipper withReplacedZipper(NodeZipper oldZipper, NodeZipper newZipper) {
int index = zippers.indexOf(oldZipper);
assertTrue(index >= 0, "oldZipper not found");
List> newZippers = new ArrayList<>(zippers);
newZippers.set(index, newZipper);
return new NodeMultiZipper<>(commonRoot, newZippers, this.nodeAdapter);
}
private List> getDeepestZippers(List> zippers) {
Map>> grouped = zippers
.stream()
.collect(groupingBy(astZipper -> astZipper.getBreadcrumbs().size(), LinkedHashMap::new, mapping(Function.identity(), toList())));
Integer maxLevel = Collections.max(grouped.keySet());
return grouped.get(maxLevel);
}
private NodeZipper moveUp(T parent, List> sameParent) {
assertNotEmpty(sameParent, "expected at least one zipper");
Map> childrenMap = nodeAdapter.getNamedChildren(parent);
for (NodeZipper zipper : sameParent) {
NodeLocation location = zipper.getBreadcrumbs().get(0).getLocation();
childrenMap.computeIfAbsent(location.getName(), (key) -> new ArrayList<>());
List childrenList = childrenMap.get(location.getName());
if (childrenList.size() > location.getIndex()) {
childrenList.set(location.getIndex(), zipper.getCurNode());
} else {
childrenList.add(zipper.getCurNode());
}
}
T newNode = nodeAdapter.withNewChildren(parent, childrenMap);
List> newBreadcrumbs = sameParent.get(0).getBreadcrumbs().subList(1, sameParent.get(0).getBreadcrumbs().size());
return new NodeZipper<>(newNode, newBreadcrumbs, this.nodeAdapter);
}
private Map>> zipperWithSameParent(List> zippers) {
return FpKit.groupingBy(zippers, NodeZipper::getParent);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy