All Downloads are FREE. Search and download functionalities are using the official Maven repository.

graphql.util.NodeZipper Maven / Gradle / Ivy

There is a newer version: 230521-nf-execution
Show newest version
package graphql.util;

import com.google.common.collect.ImmutableList;
import graphql.PublicApi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
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 = ImmutableList.copyOf(assertNotNull(breadcrumbs));
        this.nodeAdapter = nodeAdapter;
        this.modificationType = modificationType;
    }

    public ModificationType getModificationType() {
        return modificationType;
    }

    public T getCurNode() {
        return curNode;
    }

    public List> getBreadcrumbs() {
        return 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 = new HashMap<>(nodeAdapter.getNamedChildren(firstBreadcrumb.getNode()));
        NodeLocation locationInParent = firstBreadcrumb.getLocation();
        int ix = locationInParent.getIndex();
        String name = locationInParent.getName();
        List childList = new ArrayList<>(childrenForParent.get(name));
        switch (modificationType) {
            case REPLACE:
                childList.set(ix, curNode);
                break;
            case DELETE:
                childList.remove(ix);
                break;
            case INSERT_BEFORE:
                childList.add(ix, curNode);
                break;
            case INSERT_AFTER:
                childList.add(ix + 1, curNode);
                break;
        }
        childrenForParent.put(name, childList);
        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 = new LinkedHashMap<>(nodeAdapter.getNamedChildren(breadcrumb.getNode()));
            final T newChild = curNode;
            NodeLocation location = breadcrumb.getLocation();
            List list = new ArrayList<>(newChildren.get(location.getName()));
            list.set(location.getIndex(), newChild);
            newChildren.put(location.getName(), list);
            curNode = nodeAdapter.withNewChildren(breadcrumb.getNode(), newChildren);
        }
        return curNode;
    }

    @Override
    public String toString() {
        return "NodeZipper{" +
                "curNode=" + curNode +
                ", breadcrumbs.size=" + breadcrumbs.size() +
                ", modificationType=" + modificationType +
                '}';
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy