Skip to content
9 changes: 6 additions & 3 deletions src/main/java/graphql/language/AbstractDescribedNode.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package graphql.language;

import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.List;
import java.util.Map;

@PublicApi
@NullMarked
public abstract class AbstractDescribedNode<T extends Node> extends AbstractNode<T> implements DescribedNode<T> {

protected Description description;
protected @Nullable Description description;

public AbstractDescribedNode(SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData, Description description) {
public AbstractDescribedNode(@Nullable SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData, @Nullable Description description) {
super(sourceLocation, comments, ignoredChars, additionalData);
this.description = description;
}

@Override
public Description getDescription() {
public @Nullable Description getDescription() {
return description;
}
}
2 changes: 2 additions & 0 deletions src/main/java/graphql/language/AstNodeAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import graphql.PublicApi;
import graphql.util.NodeAdapter;
import graphql.util.NodeLocation;
import org.jspecify.annotations.NullMarked;

import java.util.List;
import java.util.Map;
Expand All @@ -11,6 +12,7 @@
* Adapts an Ast node to the general node from the util package
*/
@PublicApi
@NullMarked
public class AstNodeAdapter implements NodeAdapter<Node> {

public static final AstNodeAdapter AST_NODE_ADAPTER = new AstNodeAdapter();
Expand Down
23 changes: 11 additions & 12 deletions src/main/java/graphql/language/AstPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import graphql.PublicApi;
import graphql.collect.ImmutableKit;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.io.IOException;
import java.io.UncheckedIOException;
Expand All @@ -20,6 +22,7 @@
*/
@SuppressWarnings("UnnecessaryLocalVariable")
@PublicApi
@NullMarked
public class AstPrinter {

/**
Expand Down Expand Up @@ -567,13 +570,13 @@ private void node(StringBuilder out, Node<?> node) {
node(out, node, null);
}

private String node(Node<?> node, Class<?> startClass) {
private String node(Node<?> node, @Nullable Class<?> startClass) {
StringBuilder builder = new StringBuilder();
node(builder, node, startClass);
return builder.toString();
}

private void node(StringBuilder out, Node<?> node, Class<?> startClass) {
private void node(StringBuilder out, Node<?> node, @Nullable Class<?> startClass) {
if (startClass != null) {
assertTrue(startClass.isInstance(node), "The starting class must be in the inherit tree");
}
Expand All @@ -586,32 +589,28 @@ <T extends Node> NodePrinter<T> _findPrinter(Node node) {
return _findPrinter(node, null);
}

<T extends Node> NodePrinter<T> _findPrinter(Node node, Class startClass) {
if (node == null) {
return (out, type) -> {
};
}
Copy link
Copy Markdown
Member

@bbakerman bbakerman Feb 19, 2026

Choose a reason for hiding this comment

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

I feel like code get missed here in diff terms??

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I removed it because the input is not nullable - so the question becomes can the input be nullable?

Is it possible or normal behaviour to pass in a null node into the printer?

<T extends Node> NodePrinter<T> _findPrinter(Node node, @Nullable Class<?> startClass) {
Class<?> clazz = startClass != null ? startClass : node.getClass();
while (clazz != Object.class) {
NodePrinter nodePrinter = printers.get(clazz);
if (nodePrinter != null) {
//noinspection unchecked
// noinspection unchecked
return nodePrinter;
}
clazz = clazz.getSuperclass();
}
return assertShouldNeverHappen("We have a missing printer implementation for %s : report a bug!", clazz);
}

private static <T> boolean isEmpty(List<T> list) {
private static <T> boolean isEmpty(@Nullable List<T> list) {
return list == null || list.isEmpty();
}

private static boolean isEmpty(String s) {
private static boolean isEmpty(@Nullable String s) {
return s == null || s.isBlank();
}

private static <T> List<T> nvl(List<T> list) {
private static <T> List<T> nvl(@Nullable List<T> list) {
return list != null ? list : ImmutableKit.emptyList();
}

Expand Down Expand Up @@ -746,7 +745,7 @@ private static void indent(StringBuilder maybeString, int offset) {
}

@SuppressWarnings("SameParameterValue")
String wrap(String start, Node maybeNode, String end) {
String wrap(String start, @Nullable Node maybeNode, String end) {
if (maybeNode == null) {
return "";
}
Expand Down
11 changes: 7 additions & 4 deletions src/main/java/graphql/language/AstSignature.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import graphql.collect.ImmutableKit;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.math.BigDecimal;
import java.math.BigInteger;
Expand All @@ -18,6 +20,7 @@
* This will produce signature and privacy safe query documents that can be used for query categorisation and logging.
*/
@PublicApi
@NullMarked
public class AstSignature {

/**
Expand All @@ -34,7 +37,7 @@ public class AstSignature {
*
* @return the signature query in document form
*/
public Document signatureQuery(Document document, String operationName) {
public Document signatureQuery(Document document, @Nullable String operationName) {
return sortAST(
removeAliases(
hideLiterals(true,
Expand All @@ -57,7 +60,7 @@ public Document signatureQuery(Document document, String operationName) {
*
* @return the privacy safe query in document form
*/
public Document privacySafeQuery(Document document, String operationName) {
public Document privacySafeQuery(Document document, @Nullable String operationName) {
return sortAST(
removeAliases(
hideLiterals(false,
Expand Down Expand Up @@ -144,7 +147,7 @@ private Document sortAST(Document document) {
return new AstSorter().sort(document);
}

private Document dropUnusedQueryDefinitions(Document document, final String operationName) {
private Document dropUnusedQueryDefinitions(Document document, final @Nullable String operationName) {
NodeVisitorStub visitor = new NodeVisitorStub() {
@Override
public TraversalControl visitDocument(Document node, TraverserContext<Node> context) {
Expand All @@ -167,7 +170,7 @@ public TraversalControl visitDocument(Document node, TraverserContext<Node> cont
return transformDoc(document, visitor);
}

private boolean isThisOperation(OperationDefinition operationDefinition, String operationName) {
private boolean isThisOperation(OperationDefinition operationDefinition, @Nullable String operationName) {
String name = operationDefinition.getName();
if (operationName == null) {
return name == null;
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/graphql/language/AstSorter.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import graphql.schema.idl.TypeInfo;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.ArrayList;
import java.util.Comparator;
Expand All @@ -18,6 +20,7 @@
* A class that helps you sort AST nodes
*/
@PublicApi
@NullMarked
public class AstSorter {

/**
Expand Down Expand Up @@ -328,7 +331,7 @@ private Comparator<Definition> comparingDefinitions() {
return comparing(byType).thenComparing(byName);
}

private SelectionSet sortSelectionSet(SelectionSet selectionSet) {
private @Nullable SelectionSet sortSelectionSet(@Nullable SelectionSet selectionSet) {
if (selectionSet == null) {
return null;
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/graphql/language/AstTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import graphql.util.TraverserVisitorStub;
import graphql.util.TreeParallelTransformer;
import graphql.util.TreeTransformer;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.Map;
import java.util.concurrent.ForkJoinPool;
Expand All @@ -19,6 +21,7 @@
* containing the changed nodes while everything else is the same.
*/
@PublicApi
@NullMarked
public class AstTransformer {

/**
Expand Down Expand Up @@ -46,7 +49,7 @@ public Node transform(Node root, NodeVisitor nodeVisitor) {
* can be retrieved within the visitor by calling context.getVarFromParents().
* @return the transformed tree.
*/
public Node transform(Node root, NodeVisitor nodeVisitor, Map<Class<?>, Object> rootVars) {
public Node transform(Node root, NodeVisitor nodeVisitor, @Nullable Map<Class<?>, Object> rootVars) {
assertNotNull(root);
assertNotNull(nodeVisitor);

Expand Down
5 changes: 4 additions & 1 deletion src/main/java/graphql/language/DescribedNode.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package graphql.language;

import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Represents a node that can contain a description.
*/
@PublicApi
@NullMarked
public interface DescribedNode<T extends Node> extends Node<T> {

/**
* @return the description of this node
*/
Description getDescription();
@Nullable Description getDescription();

}
9 changes: 6 additions & 3 deletions src/main/java/graphql/language/Description.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package graphql.language;

import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.io.Serializable;

@PublicApi
@NullMarked
public class Description implements Serializable {
public final String content;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An interesting note: descriptions do not need to exist on GraphQL schema elements. However this is the AST representation - this Description element is only created when one exists.

public final SourceLocation sourceLocation;
public final @Nullable SourceLocation sourceLocation;
public final boolean multiLine;

public Description(String content, SourceLocation sourceLocation, boolean multiLine) {
public Description(String content, @Nullable SourceLocation sourceLocation, boolean multiLine) {
this.content = content;
this.sourceLocation = sourceLocation;
this.multiLine = multiLine;
Expand All @@ -20,7 +23,7 @@ public String getContent() {
return content;
}

public SourceLocation getSourceLocation() {
public @Nullable SourceLocation getSourceLocation() {
return sourceLocation;
}

Expand Down
13 changes: 9 additions & 4 deletions src/main/java/graphql/language/Directive.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import graphql.collect.ImmutableKit;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;

import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -21,14 +24,15 @@
import static graphql.language.NodeUtil.nodeByName;

@PublicApi
@NullMarked
public class Directive extends AbstractNode<Directive> implements NamedNode<Directive> {
private final String name;
private final ImmutableList<Argument> arguments;

public static final String CHILD_ARGUMENTS = "arguments";

@Internal
protected Directive(String name, List<Argument> arguments, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData) {
protected Directive(String name, List<Argument> arguments, @Nullable SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData) {
super(sourceLocation, comments, ignoredChars, additionalData);
this.name = name;
this.arguments = ImmutableList.copyOf(arguments);
Expand Down Expand Up @@ -63,7 +67,7 @@ public Map<String, Argument> getArgumentsByName() {
return nodeByName(arguments);
}

public Argument getArgument(String argumentName) {
public @Nullable Argument getArgument(String argumentName) {
return NodeUtil.findNodeByName(arguments, argumentName);
}

Expand Down Expand Up @@ -93,7 +97,7 @@ public Directive withNewChildren(NodeChildrenContainer newChildren) {
}

@Override
public boolean isEqualTo(Node o) {
public boolean isEqualTo(@Nullable Node o) {
if (this == o) {
return true;
}
Expand All @@ -109,7 +113,7 @@ public boolean isEqualTo(Node o) {

@Override
public Directive deepCopy() {
return new Directive(name, deepCopy(arguments), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData());
return new Directive(name, assertNotNull(deepCopy(arguments), "arguments cannot be null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData());
}

@Override
Expand All @@ -135,6 +139,7 @@ public Directive transform(Consumer<Builder> builderConsumer) {
return builder.build();
}

@NullUnmarked
public static final class Builder implements NodeBuilder {
private SourceLocation sourceLocation;
private ImmutableList<Comment> comments = emptyList();
Expand Down
Loading