Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1f67b2c
add ast zipper, all Nodes immutable
andimarek Dec 1, 2018
05fb8cc
naming, cleanup
andimarek Dec 1, 2018
5c575f0
add AstMultiZipper
andimarek Dec 1, 2018
9a145f4
AstMultiZipper tests
andimarek Dec 1, 2018
1cc1222
refactor Traversal
andimarek Dec 1, 2018
40a4f7b
AstTransformer first test green
andimarek Dec 2, 2018
5f11a73
make test nicer
andimarek Dec 2, 2018
7a9f6cf
use linkedHashMap for groupBy
andimarek Dec 11, 2018
7b86cee
better error message
andimarek Dec 11, 2018
8000977
non javadoc
andimarek Dec 11, 2018
e7639a8
cleanup
andimarek Dec 11, 2018
ee8c62d
make all child String constants public
andimarek Dec 11, 2018
cb72a31
add static helper method
andimarek Dec 11, 2018
5558e3d
ast transformer tests
andimarek Dec 11, 2018
f228109
make sure all nodes are immutable
andimarek Dec 11, 2018
f92364c
move helper method to Util class
andimarek Dec 11, 2018
65a34a8
move assertion into helper method
andimarek Dec 11, 2018
7952846
move assertion into helper method
andimarek Dec 12, 2018
3a345de
remove not needed methods
andimarek Dec 12, 2018
49525f4
javadoc
andimarek Dec 12, 2018
8436b61
javadoc
andimarek Dec 12, 2018
ce19b7c
use static import NodeChildrenContainer
andimarek Dec 12, 2018
3833d72
improve traverser: add getParentNodes and isRootContext to
andimarek Dec 12, 2018
0e8b4f6
improve Traverser by allowing the node to be changed
andimarek Dec 13, 2018
1f29524
handle unchanged nodes correctly
andimarek Dec 13, 2018
84fdbc1
Added AstSorter to test zipper code
bbakerman Dec 13, 2018
e801153
Added better testing
bbakerman Dec 13, 2018
007567d
More code for SDL - not yet tested
bbakerman Dec 13, 2018
7215491
more ast transformer test
andimarek Dec 13, 2018
85da93c
Added SDL sorting with tests
bbakerman Dec 13, 2018
2ae091b
JavaDoc on AST Sorter
bbakerman Dec 14, 2018
0d35906
Merge remote-tracking branch 'upstream/zipper2' into zipper2
bbakerman Dec 14, 2018
cca5172
Now added signature work - not finished but close
bbakerman Dec 14, 2018
a0463cd
Removed copyright
bbakerman Dec 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {

@Override
public TraversalControl visitSelectionSet(SelectionSet node, TraverserContext<Node> context) {
context.setResult(node.getSelections());
context.setAccumulate(node.getSelections());
return TraversalControl.CONTINUE;
}

private void getSelectionSetChildren(SelectionSet node, TraverserContext<Node> context) {
if (node == null) {
context.setResult(Collections.emptyList());
context.setAccumulate(Collections.emptyList());
} else {
context.setResult(node.getSelections());
context.setAccumulate(node.getSelections());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

@Internal
public class ExecutionResultMultiZipper {


private final ExecutionResultNode commonRoot;
private final List<ExecutionResultZipper> zippers;

Expand Down Expand Up @@ -68,7 +73,7 @@ public ExecutionResultMultiZipper withReplacedZipper(ExecutionResultZipper oldZi

private List<ExecutionResultZipper> getDeepestZippers(List<ExecutionResultZipper> zippers) {
Map<Integer, List<ExecutionResultZipper>> grouped =
zippers.stream().collect(Collectors.groupingBy(executionResultZipper -> executionResultZipper.getBreadcrumbList().size()));
zippers.stream().collect(Collectors.groupingBy(executionResultZipper -> executionResultZipper.getBreadcrumbList().size(), LinkedHashMap::new, mapping(Function.identity(), toList())));
Integer maxLevel = Collections.max(grouped.keySet());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost deserves it is on Collector based puirely on how much boilerplate. Perhaps its own method

zippers.stream().collect(Collectors.groupingBy(thisGrouping(executionResultZipper -> executionResultZipper.getBreadcrumbList().size()));

return grouped.get(maxLevel);
}
Expand All @@ -85,7 +90,7 @@ private ExecutionResultZipper moveUp(ExecutionResultNode parent, List<ExecutionR
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO ^

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


private Map<ExecutionResultNode, List<ExecutionResultZipper>> zipperWithSameParent(List<ExecutionResultZipper> zippers) {
return zippers.stream().collect(Collectors.groupingBy(ExecutionResultZipper::getParent));
return zippers.stream().collect(Collectors.groupingBy(ExecutionResultZipper::getParent, LinkedHashMap::new, mapping(Function.identity(), toList())));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO ^

I think you put in a supplier of the Map

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I reckon create its own helper method


}
1 change: 0 additions & 1 deletion src/main/java/graphql/language/AbstractNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,4 @@ protected <V extends Node> List<V> deepCopy(List<? extends Node> list) {
}
return list.stream().map(Node::deepCopy).map(node -> (V) node).collect(Collectors.toList());
}

}
29 changes: 19 additions & 10 deletions src/main/java/graphql/language/Argument.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import java.util.List;
import java.util.function.Consumer;

import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;

@PublicApi
public class Argument extends AbstractNode<Argument> implements NamedNode<Argument> {

private String name;
private Value value;
private final String name;
private final Value value;

public static final String CHILD_VALUE = "value";

@Internal
protected Argument(String name, Value value, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars) {
Expand Down Expand Up @@ -42,21 +46,26 @@ public Value getValue() {
return value;
}

public void setName(String name) {
this.name = name;
}

public void setValue(Value value) {
this.value = value;
}

@Override
public List<Node> getChildren() {
List<Node> result = new ArrayList<>();
result.add(value);
return result;
}

@Override
public NodeChildrenContainer getNamedChildren() {
return newNodeChildrenContainer()
.child(CHILD_VALUE, value)
.build();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do static import in all the places to reduce the boiler plate

@Override
    public NodeChildrenContainer getNamedChildren() {
        return newNodeChildrenContainer()
                .child(CHILD_VALUE, value)
                .build();
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@Override
public Argument withNewChildren(NodeChildrenContainer newChildren) {
return transform(builder -> builder
.value(newChildren.getChildOrNull(CHILD_VALUE))
);
}

@Override
public boolean isEqualTo(Node o) {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/graphql/language/ArrayValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import java.util.List;
import java.util.function.Consumer;

import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;

@PublicApi
public class ArrayValue extends AbstractNode<ArrayValue> implements Value<ArrayValue> {

private final List<Value> values = new ArrayList<>();

public static final String CHILD_VALUES = "values";

@Internal
protected ArrayValue(List<Value> values, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars) {
super(sourceLocation, comments, ignoredChars);
Expand All @@ -39,6 +43,20 @@ public List<Node> getChildren() {
return new ArrayList<>(values);
}

@Override
public NodeChildrenContainer getNamedChildren() {
return newNodeChildrenContainer()
.children(CHILD_VALUES, values)
.build();
}

@Override
public ArrayValue withNewChildren(NodeChildrenContainer newChildren) {
return transform(builder -> builder
.values(newChildren.getChildren(CHILD_VALUES))
);
}

@Override
public boolean isEqualTo(Node o) {
if (this == o) {
Expand Down
57 changes: 57 additions & 0 deletions src/main/java/graphql/language/AstBreadcrumb.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package graphql.language;

import graphql.PublicApi;

import java.util.Objects;

/**
* Used by {@link AstZipper} to identify the position of the current node inside the tree:
* the breadcrumbs lead you to the root node.
*/
@PublicApi
public class AstBreadcrumb {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I woukld still love to see JavaDoc at the top to outline the class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added


private final Node node;
private final NodeLocation location;

public AstBreadcrumb(Node node, NodeLocation location) {
this.node = node;
this.location = location;
}

public Node getNode() {
return node;
}

public NodeLocation getLocation() {
return location;
}


@Override
public String toString() {
return "AstBreadcrumb{" +
"node=" + node +
", location=" + location +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AstBreadcrumb that = (AstBreadcrumb) o;
return Objects.equals(node, that.node) &&
Objects.equals(location, that.location);
}

@Override
public int hashCode() {
return Objects.hash(node, location);
}
}

127 changes: 127 additions & 0 deletions src/main/java/graphql/language/AstMultiZipper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package graphql.language;

import graphql.PublicApi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static graphql.Assert.assertNotEmpty;
import static graphql.Assert.assertTrue;
import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

/**
* A collection of {@link AstZipper} sharing all the same root node.
* It is used to track multiple changes at once, while {@link AstZipper} focus on one Node.
*/
@PublicApi
public class AstMultiZipper {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

private final Node commonRoot;
private final List<AstZipper> zippers;

public AstMultiZipper(Node commonRoot, List<AstZipper> zippers) {
this.commonRoot = commonRoot;
this.zippers = new ArrayList<>(zippers);
}

public Node toRootNode() {
if (zippers.size() == 0) {
return commonRoot;
}

List<AstZipper> curZippers = new ArrayList<>(zippers);
while (curZippers.size() > 1) {

List<AstZipper> deepestZippers = getDeepestZippers(curZippers);
Map<Node, List<AstZipper>> sameParent = zipperWithSameParent(deepestZippers);

List<AstZipper> newZippers = new ArrayList<>();
for (Map.Entry<Node, List<AstZipper>> entry : sameParent.entrySet()) {
AstZipper newZipper = moveUp(entry.getKey(), entry.getValue());
Optional<AstZipper> zipperToBeReplaced = curZippers.stream().filter(zipper -> zipper.getCurNode() == entry.getKey()).findFirst();
zipperToBeReplaced.ifPresent(curZippers::remove);
newZippers.add(newZipper);
}
curZippers.removeAll(deepestZippers);
curZippers.addAll(newZippers);
}
assertTrue(curZippers.size() == 1, "unexpected state: all zippers must share the same root node");
return curZippers.get(0).toRoot();
}

public Node getCommonRoot() {
return commonRoot;
}

public List<AstZipper> getZippers() {
return new ArrayList<>(zippers);
}

public AstMultiZipper withReplacedZippers(List<AstZipper> zippers) {
return new AstMultiZipper(commonRoot, zippers);
}

public AstMultiZipper withNewZipper(AstZipper newZipper) {
List<AstZipper> newZippers = getZippers();
newZippers.add(newZipper);
return new AstMultiZipper(commonRoot, newZippers);
}

public AstMultiZipper withReplacedZipper(AstZipper oldZipper, AstZipper newZipper) {
int index = zippers.indexOf(oldZipper);
assertTrue(index >= 0, "oldZipper not found");
List<AstZipper> newZippers = new ArrayList<>(zippers);
newZippers.set(index, newZipper);
return new AstMultiZipper(commonRoot, newZippers);
}


private List<AstZipper> getDeepestZippers(List<AstZipper> zippers) {
Map<Integer, List<AstZipper>> grouped = zippers
.stream()
.collect(groupingBy(astZipper -> astZipper.getBreadcrumbs().size(), LinkedHashMap::new, mapping(Function.identity(), toList())));

Integer maxLevel = Collections.max(grouped.keySet());
return grouped.get(maxLevel);
}

private AstZipper moveUp(Node parent, List<AstZipper> sameParent) {
assertNotEmpty(sameParent, "expected at least one zipper");
Map<String, List<Node>> childrenMap = parent.getNamedChildren().getChildren();

for (AstZipper zipper : sameParent) {
NodeLocation location = zipper.getBreadcrumbs().get(0).getLocation();
childrenMap.computeIfAbsent(location.getName(), (key) -> new ArrayList<>());
List<Node> childrenList = childrenMap.get(location.getName());
if (childrenList.size() > location.getIndex()) {
childrenList.set(location.getIndex(), zipper.getCurNode());
} else {
childrenList.add(zipper.getCurNode());
}
}
Node newNode = parent.withNewChildren(newNodeChildrenContainer(childrenMap).build());
List<AstBreadcrumb> newBreadcrumbs = sameParent.get(0).getBreadcrumbs().subList(1, sameParent.get(0).getBreadcrumbs().size());
return new AstZipper(newNode, newBreadcrumbs);
}

private Map<Node, List<AstZipper>> zipperWithSameParent(List<AstZipper> zippers) {
return zippers.stream().collect(groupingBy(AstZipper::getParent, LinkedHashMap::new,
mapping(Function.identity(), Collectors.toList())));
}

@Override
public String toString() {
return "AstMultiZipper{" +
"commonRoot=" + commonRoot.getClass() +
", zippersCount=" + zippers.size() +
'}';
}
}
Loading