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

graphql.util.TraverserState Maven / Gradle / Ivy

package graphql.util;

import graphql.Internal;

import java.util.ArrayDeque;
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);
            super.state.push(Marker.END_LIST);
            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);
                    NodePosition nodePosition = new NodePosition(key, i);
                    super.state.push(super.newContext(child, traverserContext, nodePosition));
                }
            });
        }
    }

    // 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> 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);
                    NodePosition nodePosition = new NodePosition(key, i);
                    super.state.add(super.newContext(child, traverserContext, nodePosition));
                }
            });
            super.state.add(Marker.END_LIST);
            super.state.add(traverserContext);
        }
    }

    public enum Marker {
        END_LIST
    }

    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, NodePosition position) {
        return newContextImpl(o, parent, new LinkedHashMap<>(), position, false);
    }

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