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

graphql.util.NodeMultiZipper Maven / Gradle / Ivy

There is a newer version: 230521-nf-execution
Show newest version
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