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

graphql.util.TraverserState Maven / Gradle / Ivy

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

import graphql.Internal;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import static graphql.Assert.assertNotNull;

@Internal
public abstract class TraverserState {

    private Object sharedContextData;

    private final Deque state;
    private final Set visited = new LinkedHashSet<>();


    // used for depth first traversal
    private static class StackTraverserState extends TraverserState {

        private StackTraverserState(Object sharedContextData) {
            super(sharedContextData);
        }

        @Override
        public void pushAll(TraverserContext traverserContext, Function>> getChildren) {
            super.state.push(traverserContext);

            EndList endList = new EndList<>();
            super.state.push(endList);
            Map>> childrenContextMap = new LinkedHashMap<>();

            if (!traverserContext.isDeleted()) {
                Map> childrenMap = getChildren.apply(traverserContext.thisNode());
                childrenMap.keySet().forEach(key -> {
                    List children = childrenMap.get(key);
                    for (int i = children.size() - 1; i >= 0; i--) {
                        U child = assertNotNull(children.get(i), () -> "null child for key " + key);
                        NodeLocation nodeLocation = new NodeLocation(key, i);
                        DefaultTraverserContext context = super.newContext(child, traverserContext, nodeLocation);
                        super.state.push(context);
                        childrenContextMap.computeIfAbsent(key, notUsed -> new ArrayList<>());
                        childrenContextMap.get(key).add(0, context);
                    }
                });
            }
            endList.childrenContextMap = childrenContextMap;
        }
    }

    // used for breadth first traversal
    private static class QueueTraverserState extends TraverserState {

        private QueueTraverserState(Object sharedContextData) {
            super(sharedContextData);
        }

        @Override
        public void pushAll(TraverserContext traverserContext, Function>> getChildren) {
            Map>> childrenContextMap = new LinkedHashMap<>();
            if (!traverserContext.isDeleted()) {
                Map> childrenMap = getChildren.apply(traverserContext.thisNode());
                childrenMap.keySet().forEach(key -> {
                    List children = childrenMap.get(key);
                    for (int i = 0; i < children.size(); i++) {
                        U child = assertNotNull(children.get(i), () -> "null child for key " + key);
                        NodeLocation nodeLocation = new NodeLocation(key, i);
                        DefaultTraverserContext context = super.newContext(child, traverserContext, nodeLocation);
                        childrenContextMap.computeIfAbsent(key, notUsed -> new ArrayList<>());
                        childrenContextMap.get(key).add(context);
                        super.state.add(context);
                    }
                });
            }
            EndList endList = new EndList<>();
            endList.childrenContextMap = childrenContextMap;
            super.state.add(endList);
            super.state.add(traverserContext);
        }
    }

    public static class EndList {
        public Map>> childrenContextMap;
    }

    private TraverserState(Object sharedContextData) {
        this.sharedContextData = sharedContextData;
        this.state = new ArrayDeque<>(32);
    }

    public static  TraverserState newQueueState(Object sharedContextData) {
        return new QueueTraverserState<>(sharedContextData);
    }

    public static  TraverserState newStackState(Object sharedContextData) {
        return new StackTraverserState<>(sharedContextData);
    }

    public abstract void pushAll(TraverserContext o, Function>> getChildren);

    public Object pop() {
        return this.state.pop();
    }


    public void addNewContexts(Collection children, TraverserContext parentContext) {
        assertNotNull(children).stream().map((child) -> newContext(child, parentContext, null)).forEach(this.state::add);
    }

    public boolean isEmpty() {
        return state.isEmpty();
    }


    public void addVisited(T visited) {
        this.visited.add(visited);
    }


    public DefaultTraverserContext newRootContext(Map, Object> vars) {
        return newContextImpl(null, null, vars, null, true);
    }

    private DefaultTraverserContext newContext(T o, TraverserContext parent, NodeLocation position) {
        return newContextImpl(o, parent, new LinkedHashMap<>(), position, false);
    }

    private DefaultTraverserContext newContextImpl(T curNode,
                                                      TraverserContext parent,
                                                      Map, Object> vars,
                                                      NodeLocation nodeLocation,
                                                      boolean isRootContext) {
        assertNotNull(vars);
        return new DefaultTraverserContext<>(curNode, parent, visited, vars, sharedContextData, nodeLocation, isRootContext, false);
    }
}