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

graphql.util.Traverser Maven / Gradle / Ivy

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

import graphql.Internal;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertShouldNeverHappen;
import static graphql.Assert.assertTrue;
import static graphql.util.TraversalControl.CONTINUE;
import static graphql.util.TraversalControl.QUIT;

@Internal
public class Traverser {

    private final TraverserState traverserState;
    private final Function> getChildren;
    private final Map, Object> rootVars = new ConcurrentHashMap<>();

    private final TraverserContext BARRIER = new SimpleTraverserContext<>(null);

    private static final List CONTINUE_OR_QUIT = Arrays.asList(CONTINUE, QUIT);

    private Traverser(TraverserState traverserState, Function> getChildren) {
        this.traverserState = assertNotNull(traverserState);
        this.getChildren = assertNotNull(getChildren);
    }

    public Traverser rootVars(Map, Object> rootVars) {
        this.rootVars.putAll(assertNotNull(rootVars));
        return this;
    }

    public Traverser rootVar(Class key, Object value) {
        rootVars.put(key, value);
        return this;
    }

    public static  Traverser depthFirst(Function> getChildren) {
        return depthFirst(getChildren, null);
    }

    public static  Traverser depthFirst(Function> getChildren, Object initialData) {
        return new Traverser<>(TraverserState.newStackState(initialData), getChildren);
    }


    public static  Traverser breadthFirst(Function> getChildren) {
        return breadthFirst(getChildren, null);
    }

    public static  Traverser breadthFirst(Function> getChildren, Object initialData) {
        return new Traverser<>(TraverserState.newQueueState(initialData), getChildren);
    }


    public TraverserResult traverse(T root, TraverserVisitor visitor) {
        return traverse(Collections.singleton(root), visitor);
    }

    public TraverserResult traverse(Collection roots, TraverserVisitor visitor) {
        assertNotNull(roots);
        assertNotNull(visitor);

        traverserState.addNewContexts(roots, traverserState.newContext(null, null, rootVars));

        TraverserContext currentContext = BARRIER;
        traverseLoop:
        while (!traverserState.isEmpty()) {
            Object top = traverserState.pop();

            if (top == TraverserState.Marker.END_LIST) {
                // end-of-list marker, we are done recursing children,
                // mark the current node as fully visited
                TraverserContext contextForLeave = (TraverserContext) traverserState.pop();
                currentContext = contextForLeave;
                TraversalControl traversalControl = visitor.leave(contextForLeave);
                assertNotNull(traversalControl, "result of leave must not be null");
                assertTrue(CONTINUE_OR_QUIT.contains(traversalControl), "result can only return CONTINUE or QUIT");
                switch (traversalControl) {
                    case QUIT:
                        break traverseLoop;
                    case CONTINUE:
                        continue;
                    default:
                        assertShouldNeverHappen();
                }
            }

            currentContext = (TraverserContext) top;

            if (currentContext.isVisited()) {
                TraversalControl traversalControl = visitor.backRef(currentContext);
                assertNotNull(traversalControl, "result of backRef must not be null");
                assertTrue(CONTINUE_OR_QUIT.contains(traversalControl), "backRef can only return CONTINUE or QUIT");
                if (traversalControl == QUIT) {
                    break traverseLoop;
                }
            } else {
                TraversalControl traversalControl = visitor.enter(currentContext);
                assertNotNull(traversalControl, "result of enter must not be null");
                this.traverserState.addVisited((T) currentContext.thisNode());
                switch (traversalControl) {
                    case QUIT:
                        break traverseLoop;
                    case ABORT:
                        continue;
                    case CONTINUE:
                        traverserState.pushAll(currentContext, getChildren);
                        continue;
                    default:
                        assertShouldNeverHappen();
                }
            }
        }
        TraverserResult traverserResult = new TraverserResult(currentContext.getResult());
        return traverserResult;
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy