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

graphql.language.AstMultiZipper Maven / Gradle / Ivy

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

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 java.util.stream.Collectors;

import static graphql.Assert.assertNotEmpty;
import static graphql.Assert.assertTrue;
import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

/**
 * A collection of {@link AstZipper} sharing all the same root node.
 * It is used to track multiple changes at once, while {@link AstZipper} focus on one Node.
 */
@PublicApi
public class AstMultiZipper {
    private final Node commonRoot;
    private final List zippers;

    public AstMultiZipper(Node commonRoot, List zippers) {
        this.commonRoot = commonRoot;
        this.zippers = new ArrayList<>(zippers);
    }

    public Node 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()) {
                AstZipper 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 curZippers.get(0).toRoot();
    }

    public Node getCommonRoot() {
        return commonRoot;
    }

    public List getZippers() {
        return new ArrayList<>(zippers);
    }

    public AstMultiZipper withReplacedZippers(List zippers) {
        return new AstMultiZipper(commonRoot, zippers);
    }

    public AstMultiZipper withNewZipper(AstZipper newZipper) {
        List newZippers = getZippers();
        newZippers.add(newZipper);
        return new AstMultiZipper(commonRoot, newZippers);
    }

    public AstMultiZipper withReplacedZipper(AstZipper oldZipper, AstZipper newZipper) {
        int index = zippers.indexOf(oldZipper);
        assertTrue(index >= 0, "oldZipper not found");
        List newZippers = new ArrayList<>(zippers);
        newZippers.set(index, newZipper);
        return new AstMultiZipper(commonRoot, newZippers);
    }


    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 AstZipper moveUp(Node parent, List sameParent) {
        assertNotEmpty(sameParent, "expected at least one zipper");
        Map> childrenMap = parent.getNamedChildren().getChildren();

        for (AstZipper 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());
            }
        }
        Node newNode = parent.withNewChildren(newNodeChildrenContainer(childrenMap).build());
        List newBreadcrumbs = sameParent.get(0).getBreadcrumbs().subList(1, sameParent.get(0).getBreadcrumbs().size());
        return new AstZipper(newNode, newBreadcrumbs);
    }

    private Map> zipperWithSameParent(List zippers) {
        return zippers.stream().collect(groupingBy(AstZipper::getParent, LinkedHashMap::new,
                mapping(Function.identity(), Collectors.toList())));
    }

    @Override
    public String toString() {
        return "AstMultiZipper{" +
                "commonRoot=" + commonRoot.getClass() +
                ", zippersCount=" + zippers.size() +
                '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy