Skip to content

Commit e51c300

Browse files
committed
new changeParent functionality
1 parent 3be8ed4 commit e51c300

5 files changed

Lines changed: 90 additions & 13 deletions

File tree

src/main/java/graphql/language/AstTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public TraversalControl enter(TraverserContext<Node> context) {
3737
NodePosition generalNodePosition = context.getPosition();
3838
if (generalNodePosition != null) {
3939
NodeLocation location = new NodeLocation(generalNodePosition.getName(), generalNodePosition.getIndex());
40-
breadCrumbsStack.push(new AstBreadcrumb(context.getParentContext().thisNode(), location));
40+
breadCrumbsStack.push(new AstBreadcrumb(context.getParentNode(), location));
4141
}
4242
List<AstBreadcrumb> breadcrumbs = new ArrayList<>(breadCrumbsStack);
4343
AstZipper astZipper = new AstZipper(context.thisNode(), breadcrumbs);

src/main/java/graphql/language/AstTransformerUtil.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import graphql.util.TraversalControl;
55
import graphql.util.TraverserContext;
66

7+
import java.util.function.Function;
8+
79
import static graphql.Assert.assertTrue;
810

911
@PublicApi
@@ -29,19 +31,30 @@ public static TraversalControl changeNode(TraverserContext<Node> context, Node c
2931
}
3032

3133
public static TraversalControl deleteNode(TraverserContext<Node> context) {
34+
AstZipper curZipper = context.getVar(AstZipper.class);
35+
NodeLocation nodeLocation = curZipper.getBreadcrumbs().get(0).getLocation();
36+
37+
changeParentNode(context, parentNode -> NodeUtil.removeChild(parentNode, nodeLocation));
38+
39+
context.deleteNode();
40+
return TraversalControl.CONTINUE;
41+
}
42+
43+
public static TraversalControl changeParentNode(TraverserContext<Node> context, Function<Node, Node> changeNodeFunction) {
3244
assertTrue(context.getParentNode() != null, "can't delete root node");
3345
AstMultiZipper multiZipper = context.getCurrentAccumulate();
3446
AstZipper curZipper = context.getVar(AstZipper.class);
35-
AstZipper zipperForParent = multiZipper.getZipperForNode(context.getParentContext().thisNode());
47+
AstZipper zipperForParent = multiZipper.getZipperForNode(curZipper.getParent());
48+
3649
boolean zipperForParentAlreadyExisted = true;
3750
if (zipperForParent == null) {
3851
zipperForParent = curZipper.moveUp();
3952
zipperForParentAlreadyExisted = false;
4053
}
41-
4254
Node parentNode = zipperForParent.getCurNode();
43-
NodeLocation nodeLocation = curZipper.getBreadcrumbs().get(0).getLocation();
44-
Node newParent = NodeUtil.removeChild(parentNode, nodeLocation);
55+
56+
Node newParent = changeNodeFunction.apply(parentNode);
57+
4558
AstZipper newZipperForParent = zipperForParent.withNewNode(newParent);
4659

4760
AstMultiZipper newMultiZipper;
@@ -50,10 +63,10 @@ public static TraversalControl deleteNode(TraverserContext<Node> context) {
5063
} else {
5164
newMultiZipper = multiZipper.withNewZipper(newZipperForParent);
5265
}
53-
66+
context.getParentContext().changeNode(newParent);
5467
context.setAccumulate(newMultiZipper);
55-
context.deleteNode();
5668
return TraversalControl.CONTINUE;
69+
5770
}
5871

5972

src/main/java/graphql/language/SelectionSet.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public SelectionSet transform(Consumer<Builder> builderConsumer) {
108108

109109
public static final class Builder implements NodeBuilder {
110110

111-
private Collection<? extends Selection> selections = new ArrayList<>();
111+
private List<Selection> selections = new ArrayList<>();
112112
private SourceLocation sourceLocation;
113113
private List<Comment> comments = new ArrayList<>();
114114
private IgnoredChars ignoredChars = IgnoredChars.EMPTY;
@@ -124,7 +124,12 @@ private Builder(SelectionSet existing) {
124124
}
125125

126126
public Builder selections(Collection<? extends Selection> selections) {
127-
this.selections = selections;
127+
this.selections = new ArrayList<>(selections);
128+
return this;
129+
}
130+
131+
public Builder selection(Selection selection) {
132+
this.selections.add(selection);
128133
return this;
129134
}
130135

src/main/java/graphql/util/DefaultTraverserContext.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ public T originalThisNode() {
7272
@Override
7373
public void changeNode(T newNode) {
7474
assertNotNull(newNode);
75-
assertTrue(this.newNode == null && !this.nodeDeleted, "node can only be changed or deleted once");
75+
assertFalse(this.nodeDeleted, "node is deleted");
7676
this.newNode = newNode;
7777
}
7878

7979
@Override
8080
public void deleteNode() {
81-
assertNull(this.newNode, "node can only be changed or deleted once");
82-
assertFalse(this.nodeDeleted, "node can only be changed or deleted once");
81+
assertNull(this.newNode, "node is already changed");
82+
assertFalse(this.nodeDeleted, "node is already deleted");
8383
this.nodeDeleted = true;
8484
}
8585

src/test/groovy/graphql/language/AstTransformerTest.groovy

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ class AstTransformerTest extends Specification {
190190
return AstTransformerUtil.deleteNode(context);
191191
} else if (field.name == "a") {
192192
return changeNode(context, field.transform({ builder -> builder.name("aChanged") }))
193+
194+
} else if (field.name == "root") {
195+
Field addField = new Field("new")
196+
def newSelectionSet = field.getSelectionSet().transform({ builder -> builder.selection(addField) })
197+
changeNode(context, field.transform({ builder -> builder.selectionSet(newSelectionSet) }))
193198
} else {
194199
return TraversalControl.CONTINUE;
195200
}
@@ -201,8 +206,62 @@ class AstTransformerTest extends Specification {
201206

202207
then:
203208

204-
printAstCompact(newDocument) == "query {root {aChanged(arg:1) {y1} b {y2}}}"
209+
printAstCompact(newDocument) == "query {root {aChanged(arg:1) {y1} b {y2} new}}"
205210

206211
}
207212

213+
def "add sibling"() {
214+
def document = TestUtil.parseQuery("{foo}")
215+
216+
AstTransformer astTransformer = new AstTransformer()
217+
218+
def visitor = new NodeVisitorStub() {
219+
220+
@Override
221+
TraversalControl visitField(Field node, TraverserContext<Node> context) {
222+
return AstTransformerUtil.changeParentNode(context, { selectionSet ->
223+
selectionSet.transform({ builder -> builder.selection(new Field("foo2")) })
224+
})
225+
}
226+
}
227+
228+
229+
when:
230+
def newDocument = astTransformer.transform(document, visitor)
231+
232+
then:
233+
printAstCompact(newDocument) == "query {foo foo2}"
234+
235+
}
236+
237+
def "delete node and add sibling"() {
238+
def document = TestUtil.parseQuery("{root { a(arg: 1) { x y } toDelete { x y } } }")
239+
240+
AstTransformer astTransformer = new AstTransformer()
241+
242+
def visitor = new NodeVisitorStub() {
243+
244+
@Override
245+
TraversalControl visitField(Field field, TraverserContext<Node> context) {
246+
if (field.name == "toDelete") {
247+
return AstTransformerUtil.deleteNode(context);
248+
} else if (field.name == "a") {
249+
return AstTransformerUtil.changeParentNode(context, { selectionSet ->
250+
selectionSet.transform({ builder -> builder.selection(new Field("newOne")) })
251+
})
252+
} else {
253+
return TraversalControl.CONTINUE
254+
}
255+
}
256+
}
257+
258+
when:
259+
def newDocument = astTransformer.transform(document, visitor)
260+
261+
then:
262+
printAstCompact(newDocument) == "query {root {a(arg:1) {x y} newOne}}"
263+
264+
}
265+
266+
208267
}

0 commit comments

Comments
 (0)