use of graphql.util.Breadcrumb in project graphql-java by graphql-java.
the class SchemaTransformer method traverseAndTransform.
private boolean traverseAndTransform(DummyRoot dummyRoot, Map<String, GraphQLNamedType> changedTypes, Map<String, GraphQLTypeReference> typeReferences, GraphQLTypeVisitor visitor, GraphQLCodeRegistry.Builder codeRegistry) {
List<NodeZipper<GraphQLSchemaElement>> zippers = new LinkedList<>();
Map<GraphQLSchemaElement, NodeZipper<GraphQLSchemaElement>> zipperByNodeAfterTraversing = new LinkedHashMap<>();
Map<GraphQLSchemaElement, NodeZipper<GraphQLSchemaElement>> zipperByOriginalNode = new LinkedHashMap<>();
Map<NodeZipper<GraphQLSchemaElement>, List<List<Breadcrumb<GraphQLSchemaElement>>>> breadcrumbsByZipper = new LinkedHashMap<>();
Map<GraphQLSchemaElement, List<GraphQLSchemaElement>> reverseDependencies = new LinkedHashMap<>();
Map<String, List<GraphQLSchemaElement>> typeRefReverseDependencies = new LinkedHashMap<>();
TraverserVisitor<GraphQLSchemaElement> nodeTraverserVisitor = new TraverserVisitor<GraphQLSchemaElement>() {
@Override
public TraversalControl enter(TraverserContext<GraphQLSchemaElement> context) {
GraphQLSchemaElement currentSchemaElement = context.thisNode();
if (currentSchemaElement == dummyRoot) {
return TraversalControl.CONTINUE;
}
if (currentSchemaElement instanceof GraphQLTypeReference) {
GraphQLTypeReference typeRef = (GraphQLTypeReference) currentSchemaElement;
typeReferences.put(typeRef.getName(), typeRef);
}
NodeZipper<GraphQLSchemaElement> nodeZipper = new NodeZipper<>(currentSchemaElement, context.getBreadcrumbs(), SCHEMA_ELEMENT_ADAPTER);
context.setVar(NodeZipper.class, nodeZipper);
context.setVar(NodeAdapter.class, SCHEMA_ELEMENT_ADAPTER);
int zippersBefore = zippers.size();
TraversalControl result = currentSchemaElement.accept(context, visitor);
// detection if the node was changed
if (zippersBefore + 1 == zippers.size()) {
nodeZipper = zippers.get(zippers.size() - 1);
if (context.originalThisNode() instanceof GraphQLNamedType && context.isChanged()) {
GraphQLNamedType originalNamedType = (GraphQLNamedType) context.originalThisNode();
GraphQLNamedType changedNamedType = (GraphQLNamedType) context.thisNode();
if (!originalNamedType.getName().equals(changedNamedType.getName())) {
changedTypes.put(originalNamedType.getName(), changedNamedType);
}
}
}
zipperByOriginalNode.put(context.originalThisNode(), nodeZipper);
if (context.isDeleted()) {
zipperByNodeAfterTraversing.put(context.originalThisNode(), nodeZipper);
} else {
zipperByNodeAfterTraversing.put(context.thisNode(), nodeZipper);
}
breadcrumbsByZipper.put(nodeZipper, new ArrayList<>());
breadcrumbsByZipper.get(nodeZipper).add(context.getBreadcrumbs());
if (nodeZipper.getModificationType() != NodeZipper.ModificationType.DELETE) {
reverseDependencies.computeIfAbsent(context.thisNode(), ign -> new ArrayList<>()).add(context.getParentNode());
if (context.originalThisNode() instanceof GraphQLTypeReference) {
String typeName = ((GraphQLTypeReference) context.originalThisNode()).getName();
typeRefReverseDependencies.computeIfAbsent(typeName, ign -> new ArrayList<>()).add(context.getParentNode());
}
}
return result;
}
@Override
public TraversalControl leave(TraverserContext<GraphQLSchemaElement> context) {
return TraversalControl.CONTINUE;
}
@Override
public TraversalControl backRef(TraverserContext<GraphQLSchemaElement> context) {
NodeZipper<GraphQLSchemaElement> zipper = zipperByOriginalNode.get(context.thisNode());
breadcrumbsByZipper.get(zipper).add(context.getBreadcrumbs());
if (zipper.getModificationType() == DELETE) {
return CONTINUE;
}
visitor.visitBackRef(context);
List<GraphQLSchemaElement> reverseDependenciesForCurNode = reverseDependencies.get(zipper.getCurNode());
assertNotNull(reverseDependenciesForCurNode);
reverseDependenciesForCurNode.add(context.getParentNode());
return TraversalControl.CONTINUE;
}
};
Traverser<GraphQLSchemaElement> traverser = Traverser.depthFirstWithNamedChildren(SCHEMA_ELEMENT_ADAPTER::getNamedChildren, zippers, null);
if (codeRegistry != null) {
traverser.rootVar(GraphQLCodeRegistry.Builder.class, codeRegistry);
}
traverser.traverse(dummyRoot, nodeTraverserVisitor);
List<List<GraphQLSchemaElement>> stronglyConnectedTopologicallySorted = getStronglyConnectedComponentsTopologicallySorted(reverseDependencies, typeRefReverseDependencies);
return zipUpToDummyRoot(zippers, stronglyConnectedTopologicallySorted, breadcrumbsByZipper, zipperByNodeAfterTraversing);
}
use of graphql.util.Breadcrumb in project graphql-java by graphql-java.
the class SchemaTransformer method moveUp.
private NodeZipper<GraphQLSchemaElement> moveUp(GraphQLSchemaElement parent, Map<NodeZipper<GraphQLSchemaElement>, Breadcrumb<GraphQLSchemaElement>> sameParentsZipper) {
Set<NodeZipper<GraphQLSchemaElement>> sameParent = sameParentsZipper.keySet();
assertNotEmpty(sameParent, () -> "expected at least one zipper");
Map<String, List<GraphQLSchemaElement>> childrenMap = new HashMap<>(SCHEMA_ELEMENT_ADAPTER.getNamedChildren(parent));
Map<String, Integer> indexCorrection = new HashMap<>();
List<ZipperWithOneParent> zipperWithOneParents = new ArrayList<>();
for (NodeZipper<GraphQLSchemaElement> zipper : sameParent) {
Breadcrumb<GraphQLSchemaElement> breadcrumb = sameParentsZipper.get(zipper);
zipperWithOneParents.add(new ZipperWithOneParent(zipper, breadcrumb));
}
zipperWithOneParents.sort((zipperWithOneParent1, zipperWithOneParent2) -> {
NodeZipper<GraphQLSchemaElement> zipper1 = zipperWithOneParent1.zipper;
NodeZipper<GraphQLSchemaElement> zipper2 = zipperWithOneParent2.zipper;
Breadcrumb<GraphQLSchemaElement> breadcrumb1 = zipperWithOneParent1.parent;
Breadcrumb<GraphQLSchemaElement> breadcrumb2 = zipperWithOneParent2.parent;
int index1 = breadcrumb1.getLocation().getIndex();
int index2 = breadcrumb2.getLocation().getIndex();
if (index1 != index2) {
return Integer.compare(index1, index2);
}
NodeZipper.ModificationType modificationType1 = zipper1.getModificationType();
NodeZipper.ModificationType modificationType2 = zipper2.getModificationType();
if (modificationType1 == modificationType2) {
return 0;
}
// always first replacing the node
if (modificationType1 == REPLACE) {
return -1;
}
// and then INSERT_BEFORE before INSERT_AFTER
return modificationType1 == NodeZipper.ModificationType.INSERT_BEFORE ? -1 : 1;
});
for (ZipperWithOneParent zipperWithOneParent : zipperWithOneParents) {
NodeZipper<GraphQLSchemaElement> zipper = zipperWithOneParent.zipper;
Breadcrumb<GraphQLSchemaElement> breadcrumb = zipperWithOneParent.parent;
NodeLocation location = breadcrumb.getLocation();
Integer ixDiff = indexCorrection.getOrDefault(location.getName(), 0);
int ix = location.getIndex() + ixDiff;
String name = location.getName();
List<GraphQLSchemaElement> childList = new ArrayList<>(childrenMap.get(name));
switch(zipper.getModificationType()) {
case REPLACE:
childList.set(ix, zipper.getCurNode());
break;
case DELETE:
childList.remove(ix);
indexCorrection.put(name, ixDiff - 1);
break;
case INSERT_BEFORE:
childList.add(ix, zipper.getCurNode());
indexCorrection.put(name, ixDiff + 1);
break;
case INSERT_AFTER:
childList.add(ix + 1, zipper.getCurNode());
indexCorrection.put(name, ixDiff + 1);
break;
}
childrenMap.put(name, childList);
}
GraphQLSchemaElement newNode = SCHEMA_ELEMENT_ADAPTER.withNewChildren(parent, childrenMap);
final List<Breadcrumb<GraphQLSchemaElement>> oldBreadcrumbs = sameParent.iterator().next().getBreadcrumbs();
List<Breadcrumb<GraphQLSchemaElement>> newBreadcrumbs;
if (oldBreadcrumbs.size() > 1) {
newBreadcrumbs = oldBreadcrumbs.subList(1, oldBreadcrumbs.size());
} else {
newBreadcrumbs = Collections.emptyList();
}
return new NodeZipper<>(newNode, newBreadcrumbs, SCHEMA_ELEMENT_ADAPTER);
}
Aggregations