graphql.util.NodeZipper Maven / Gradle / Ivy
package graphql.util;
import graphql.PublicApi;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static graphql.Assert.assertNotNull;
@PublicApi
public class NodeZipper {
public enum ModificationType {
REPLACE,
DELETE,
INSERT_AFTER,
INSERT_BEFORE
}
private final T curNode;
private final NodeAdapter nodeAdapter;
// reverse: the breadCrumbs start from curNode upwards
private final List> breadcrumbs;
private final ModificationType modificationType;
public NodeZipper(T curNode, List> breadcrumbs, NodeAdapter nodeAdapter) {
this(curNode, breadcrumbs, nodeAdapter, ModificationType.REPLACE);
}
public NodeZipper(T curNode, List> breadcrumbs, NodeAdapter nodeAdapter, ModificationType modificationType) {
this.curNode = assertNotNull(curNode);
this.breadcrumbs = assertNotNull(breadcrumbs);
this.nodeAdapter = nodeAdapter;
this.modificationType = modificationType;
}
public ModificationType getModificationType() {
return modificationType;
}
public T getCurNode() {
return curNode;
}
public List> getBreadcrumbs() {
return new ArrayList<>(breadcrumbs);
}
public T getParent() {
return breadcrumbs.get(0).getNode();
}
public static NodeZipper rootZipper(T rootNode, NodeAdapter nodeAdapter) {
return new NodeZipper(rootNode, new ArrayList<>(), nodeAdapter);
}
public NodeZipper modifyNode(Function transform) {
return new NodeZipper(transform.apply(curNode), breadcrumbs, nodeAdapter, this.modificationType);
}
public NodeZipper deleteNode() {
return new NodeZipper(this.curNode, breadcrumbs, nodeAdapter, ModificationType.DELETE);
}
public NodeZipper insertAfter(T toInsertAfter) {
return new NodeZipper(toInsertAfter, breadcrumbs, nodeAdapter, ModificationType.INSERT_AFTER);
}
public NodeZipper insertBefore(T toInsertBefore) {
return new NodeZipper(toInsertBefore, breadcrumbs, nodeAdapter, ModificationType.INSERT_BEFORE);
}
public NodeZipper withNewNode(T newNode) {
return new NodeZipper(newNode, breadcrumbs, nodeAdapter, this.modificationType);
}
public NodeZipper moveUp() {
T node = getParent();
List> newBreadcrumbs = breadcrumbs.subList(1, breadcrumbs.size());
return new NodeZipper<>(node, newBreadcrumbs, nodeAdapter, this.modificationType);
}
/**
* @return null if it is the root node and marked as deleted, otherwise never null
*/
public T toRoot() {
if (breadcrumbs.size() == 0 && modificationType != ModificationType.DELETE) {
return this.curNode;
}
if (breadcrumbs.size() == 0 && modificationType == ModificationType.DELETE) {
return null;
}
T curNode = this.curNode;
Breadcrumb firstBreadcrumb = breadcrumbs.get(0);
Map> childrenForParent = nodeAdapter.getNamedChildren(firstBreadcrumb.getNode());
NodeLocation locationInParent = firstBreadcrumb.getLocation();
int ix = locationInParent.getIndex();
String name = locationInParent.getName();
switch (modificationType) {
case REPLACE:
childrenForParent.get(name).set(ix, curNode);
break;
case DELETE:
childrenForParent.get(name).remove(ix);
break;
case INSERT_BEFORE:
childrenForParent.get(name).add(ix, curNode);
break;
case INSERT_AFTER:
childrenForParent.get(name).add(ix + 1, curNode);
break;
}
curNode = nodeAdapter.withNewChildren(firstBreadcrumb.getNode(), childrenForParent);
if (breadcrumbs.size() == 1) {
return curNode;
}
for (Breadcrumb breadcrumb : breadcrumbs.subList(1, breadcrumbs.size())) {
// just handle replace
Map> newChildren = nodeAdapter.getNamedChildren(breadcrumb.getNode());
final T newChild = curNode;
NodeLocation location = breadcrumb.getLocation();
newChildren.get(location.getName()).set(location.getIndex(), newChild);
curNode = nodeAdapter.withNewChildren(breadcrumb.getNode(), newChildren);
}
return curNode;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy