1010import graphql .language .FragmentSpread ;
1111import graphql .language .InlineFragment ;
1212import graphql .language .Node ;
13+ import graphql .language .NodeTraverser ;
14+ import graphql .language .NodeTraverser .LeaveOrEnter ;
1315import graphql .language .NodeUtil ;
14- import graphql .language .NodeVisitor ;
1516import graphql .language .NodeVisitorStub ;
1617import graphql .language .OperationDefinition ;
1718import graphql .language .Selection ;
2324import graphql .schema .GraphQLSchema ;
2425import graphql .schema .GraphQLUnmodifiedType ;
2526import graphql .schema .SchemaUtil ;
26- import graphql .util .Traverser ;
27+ import graphql .util .TraversalControl ;
2728import graphql .util .TraverserContext ;
28- import graphql .util .TraverserMarkers ;
29- import graphql .util .TraverserStack ;
3029
30+ import java .util .LinkedHashMap ;
3131import java .util .List ;
32- import java .util .function .Consumer ;
33- import java .util .Collection ;
3432import java .util .Map ;
3533
3634import static graphql .Assert .assertNotNull ;
3735import static graphql .Assert .assertShouldNeverHappen ;
38- import java .util .Collections ;
39- import java .util .HashMap ;
36+ import static graphql .language .NodeTraverser .LeaveOrEnter .LEAVE ;
4037
4138@ Internal
4239public class QueryTraversal {
@@ -49,26 +46,27 @@ public class QueryTraversal {
4946 private final ConditionalNodes conditionalNodes = new ConditionalNodes ();
5047 private final ValuesResolver valuesResolver = new ValuesResolver ();
5148 private final SchemaUtil schemaUtil = new SchemaUtil ();
52- private final SelectionProvider selectionProvider = new SelectionProvider () ;
49+ private final ChildrenOfSelectionProvider childrenOfSelectionProvider ;
5350
5451 public QueryTraversal (GraphQLSchema schema ,
5552 Document document ,
5653 String operation ,
5754 Map <String , Object > variables ) {
5855 NodeUtil .GetOperationResult getOperationResult = NodeUtil .getOperation (document , operation );
59-
56+
6057 this .operationDefinition = getOperationResult .operationDefinition ;
6158 this .fragmentsByName = getOperationResult .fragmentsByName ;
59+ this .childrenOfSelectionProvider = new ChildrenOfSelectionProvider (fragmentsByName );
6260 this .schema = schema ;
6361 this .variables = variables ;
6462 }
6563
66- public void visitPostOrder (QueryVisitor visitor ) {
67- visitImpl (visitor , operationDefinition .getSelectionSet (), getRootType (), null , false );
64+ public void visitPostOrder (FieldVisitor visitor ) {
65+ visitImpl (visitor , operationDefinition .getSelectionSet (), getRootType (), false );
6866 }
6967
70- public void visitPreOrder (QueryVisitor visitor ) {
71- visitImpl (visitor , operationDefinition .getSelectionSet (), getRootType (), null , true );
68+ public void visitPreOrder (FieldVisitor visitor ) {
69+ visitImpl (visitor , operationDefinition .getSelectionSet (), getRootType (), true );
7270 }
7371
7472 private GraphQLObjectType getRootType () {
@@ -99,40 +97,36 @@ public <T> T reducePreOrder(QueryReducer<T> queryReducer, T initialValue) {
9997 visitPreOrder ((env ) -> acc [0 ] = queryReducer .reduceField (env , (T ) acc [0 ]));
10098 return (T ) acc [0 ];
10199 }
102-
103- private List <Selection > childrenOf ( Selection n ) {
104- return selectionProvider . getChildren ( n , fragmentsByName );
100+
101+ private List <Node > childrenOf ( Node selection ) {
102+ return childrenOfSelectionProvider . getSelections (( Selection ) selection );
105103 }
106-
107- private void visitImpl (QueryVisitor visitor , SelectionSet selectionSet , GraphQLCompositeType type , QueryVisitorEnvironment parent , boolean preOrder ) {
108- QueryTraversalContext context = new QueryTraversalContext (type , parent );
109- QueryTraversalDelegate delegate = preOrder
110- ? new QueryTraversalDelegate (visitor ::visitField , env -> {})
111- : new QueryTraversalDelegate (env -> {}, visitor ::visitField );
112-
113- // in order not to check parentContext for null and guarantee that
114- // we always can obtain the root QueryTraversalContext,
115- // we are subclassing here TraverserStack to set up a BARRIER
116- // parent that stores root QueryTraversalContext
117- Traverser .depthFirst (this ::childrenOf )
118- .rootVar (QueryTraversalContext .class , context )
119- .traverse (selectionSet .getSelections (), null , delegate );
104+
105+ private void visitImpl (FieldVisitor visitFieldCallback , SelectionSet selectionSet , GraphQLCompositeType type , boolean preOrder ) {
106+ Map <Class <?>, Object > rootVars = new LinkedHashMap <>();
107+ rootVars .put (QueryTraversalContext .class , new QueryTraversalContext (type , null ));
108+
109+ NodeTraverser nodeTraverser = new NodeTraverser (rootVars , this ::childrenOf );
110+ nodeTraverser .depthFirst (new NodeVisitorImpl (visitFieldCallback , preOrder ), selectionSet .getSelections ());
120111 }
121-
122- private class QueryTraversalDelegate extends NodeVisitorStub < TraverserContext < Selection >> {
123-
124- final Consumer < QueryVisitorEnvironment > preOrder ;
125- final Consumer < QueryVisitorEnvironment > postOrder ;
126-
127- QueryTraversalDelegate ( Consumer < QueryVisitorEnvironment > preOrder , Consumer < QueryVisitorEnvironment > postOrder ) {
128- this .preOrder = assertNotNull ( preOrder ) ;
129- this .postOrder = assertNotNull ( postOrder ) ;
112+
113+ private class NodeVisitorImpl extends NodeVisitorStub {
114+
115+ private FieldVisitor visitFieldCallback ;
116+ boolean preOrder ;
117+
118+ NodeVisitorImpl ( FieldVisitor visitFieldCallback , boolean preOrder ) {
119+ this .visitFieldCallback = visitFieldCallback ;
120+ this .preOrder = preOrder ;
130121 }
131-
122+
132123 @ Override
133- public Object visitInlineFragment (InlineFragment inlineFragment , TraverserContext <Selection > context ) {
124+ public TraversalControl visitInlineFragment (InlineFragment inlineFragment , TraverserContext <Node > context ) {
125+ if (context .getVar (LeaveOrEnter .class ) == LEAVE ) {
126+ return TraversalControl .CONTINUE ;
127+ }
134128 if (!conditionalNodes .shouldInclude (variables , inlineFragment .getDirectives ()))
135- return TraverserMarkers .ABORT ;
129+ return TraversalControl .ABORT ;
136130
137131 // inline fragments are allowed not have type conditions, if so the parent type counts
138132 QueryTraversalContext parentEnv = context
@@ -146,79 +140,67 @@ public Object visitInlineFragment(InlineFragment inlineFragment, TraverserContex
146140 } else {
147141 fragmentCondition = parentEnv .getType ();
148142 }
149-
150143 // for unions we only have other fragments inside
151- return context
152- . setVar ( QueryTraversalContext . class , new QueryTraversalContext ( fragmentCondition , parentEnv . getEnvironment ())) ;
144+ context . setVar ( QueryTraversalContext . class , new QueryTraversalContext ( fragmentCondition , parentEnv . getEnvironment ()));
145+ return TraversalControl . CONTINUE ;
153146 }
154147
155148 @ Override
156- public Object visitFragmentSpread (FragmentSpread fragmentSpread , TraverserContext <Selection > context ) {
149+ public TraversalControl visitFragmentSpread (FragmentSpread fragmentSpread , TraverserContext <Node > context ) {
150+ if (context .getVar (LeaveOrEnter .class ) == LEAVE ) {
151+ return TraversalControl .CONTINUE ;
152+ }
157153 if (!conditionalNodes .shouldInclude (variables , fragmentSpread .getDirectives ()))
158- return TraverserMarkers .ABORT ;
154+ return TraversalControl .ABORT ;
159155
160156 FragmentDefinition fragmentDefinition = fragmentsByName .get (fragmentSpread .getName ());
161157 if (!conditionalNodes .shouldInclude (variables , fragmentDefinition .getDirectives ()))
162- return TraverserMarkers .ABORT ;
158+ return TraversalControl .ABORT ;
163159
164160 QueryTraversalContext parentEnv = context
165161 .parentContext ()
166162 .getVar (QueryTraversalContext .class );
167163
168164 GraphQLCompositeType typeCondition = (GraphQLCompositeType ) schema .getType (fragmentDefinition .getTypeCondition ().getName ());
169165
170- return context
171- .setVar (QueryTraversalContext .class , new QueryTraversalContext (typeCondition , parentEnv .getEnvironment ()));
166+ context
167+ .setVar (QueryTraversalContext .class , new QueryTraversalContext (typeCondition , parentEnv .getEnvironment ()));
168+ return TraversalControl .CONTINUE ;
172169 }
173170
174171 @ Override
175- public Object visitField (Field field , TraverserContext <Selection > context ) {
176- if (!conditionalNodes .shouldInclude (variables , field .getDirectives ()))
177- return TraverserMarkers .ABORT ; // stop recursion
178-
172+ public TraversalControl visitField (Field field , TraverserContext <Node > context ) {
179173 QueryTraversalContext parentEnv = context
180174 .parentContext ()
181175 .getVar (QueryTraversalContext .class );
182176
183177 GraphQLFieldDefinition fieldDefinition = Introspection .getFieldDef (schema , parentEnv .getType (), field .getName ());
184178 Map <String , Object > argumentValues = valuesResolver .getArgumentValues (schema .getFieldVisibility (), fieldDefinition .getArguments (), field .getArguments (), variables );
179+ QueryVisitorEnvironment environment = new QueryVisitorEnvironment (field , fieldDefinition , parentEnv .getType (), parentEnv .getEnvironment (), argumentValues );
180+
181+ LeaveOrEnter leaveOrEnter = context .getVar (LeaveOrEnter .class );
182+ if (leaveOrEnter == LEAVE ) {
183+ if (!preOrder ) {
184+ visitFieldCallback .visitField (environment );
185+ }
186+ return TraversalControl .CONTINUE ;
187+ }
188+ if (preOrder ) {
189+ visitFieldCallback .visitField (environment );
190+ }
191+
192+ if (!conditionalNodes .shouldInclude (variables , field .getDirectives ()))
193+ return TraversalControl .ABORT ; // stop recursion
185194
186- QueryVisitorEnvironment environment = new QueryVisitorEnvironment (field , fieldDefinition , parentEnv .getType (), parentEnv .getEnvironment (), argumentValues );
187195 GraphQLUnmodifiedType unmodifiedType = schemaUtil .getUnmodifiedType (fieldDefinition .getType ());
188196 QueryTraversalContext fieldEnv = (unmodifiedType instanceof GraphQLCompositeType )
189- ? new QueryTraversalContext ((GraphQLCompositeType )unmodifiedType , environment )
190- : new QueryTraversalContext (null , environment );// Terminal (scalar) node, EMPTY FRAME
191- preOrder .accept (fieldEnv .getEnvironment ());
192-
193- return context
194- .setVar (QueryTraversalContext .class , fieldEnv );
195- }
197+ ? new QueryTraversalContext ((GraphQLCompositeType ) unmodifiedType , environment )
198+ : new QueryTraversalContext (null , environment );// Terminal (scalar) node, EMPTY FRAME
196199
197- @ Override
198- public Object enter (TraverserContext <Node > context , TraverserContext <Selection > data ) {
199- return context
200- .thisNode ()
201- // it is important to pass current traversal context as NodeVisitor's parameter
202- .accept (context , this );
203- }
204200
205- @ Override
206- public Object leave (TraverserContext <Node > context , TraverserContext <Selection > data ) {
207- return context
208- .thisNode ()
209- // it is important to pass current traversal context as NodeVisitor's parameter
210- .accept (context , postOrderVisitor );
201+ context .setVar (QueryTraversalContext .class , fieldEnv );
202+ return TraversalControl .CONTINUE ;
211203 }
212204
213- final NodeVisitor <TraverserContext <Selection >> postOrderVisitor = new NodeVisitorStub <TraverserContext <Selection >>() {
214- @ Override
215- public Object visitField (Field field , TraverserContext <Selection > context ) {
216- QueryTraversalContext fieldEnv = context
217- .getVar (QueryTraversalContext .class );
218- postOrder .accept (fieldEnv .getEnvironment ());
219-
220- return context ;
221- }
222- };
223205 }
224206}
0 commit comments