diff --git a/.claude/commands/jspecify-annotate.md b/.claude/commands/jspecify-annotate.md new file mode 100644 index 0000000000..e1df47e632 --- /dev/null +++ b/.claude/commands/jspecify-annotate.md @@ -0,0 +1,65 @@ +The task is to annotate public API classes (marked with `@PublicApi` or `@PublicSpi`) with JSpecify nullability annotations. + +Note that JSpecify is already used in this repository so it's already imported. + +## Scope: Public API only + +Only annotate classes marked `@PublicApi` or `@PublicSpi`. Do **not** add JSpecify annotations to `@Internal` classes — these are implementation details and are intentionally left unannotated. + +If you see a builder static class, you can label it `@NullUnmarked` and not need to do anymore for this static class in terms of annotations. + +Analyze this Java class and add JSpecify annotations based on: +1. Set the class to be `@NullMarked` +2. Remove all the redundant `@NonNull` annotations that IntelliJ added +3. Check Javadoc @param tags mentioning "null", "nullable", "may be null" +4. Check Javadoc @return tags mentioning "null", "optional", "if available" +5. Method implementations that return null or check for null +6. GraphQL specification details (see details below) + +## GraphQL Specification Compliance +This is a GraphQL implementation. When determining nullability, consult the GraphQL specification (https://spec.graphql.org/draft/) for the relevant concept. Key principles: + +The spec defines which elements are required (non-null) vs optional (nullable). Look for keywords like "MUST" to indicate when an element is required, and conditional words such as "IF" to indicate when an element is optional. + +If a class implements or represents a GraphQL specification concept, prioritize the spec's nullability requirements over what IntelliJ inferred. + +## How to validate +Finally, please check all this works by running the NullAway compile check. + +If you find NullAway errors, try and make the smallest possible change to fix them. If you must, you can use assertNotNull. Make sure to include a message as well. + +## Formatting Guidelines + +Do not make spacing or formatting changes. Avoid adjusting whitespace, line breaks, or other formatting when editing code. These changes make diffs messy and harder to review. Only make the minimal changes necessary to accomplish the task. + +## Cleaning up +Finally, remove this class from the exemption list in `JSpecifyAnnotationsCheck.groovy`. + +The exemption list contains fully-qualified class names. Remove **only** the class you just annotated — do not remove entries for `@Internal` classes or any other class you did not annotate. + +You do not need to run the JSpecifyAnnotationsCheck. Removing the completed class from the list is enough. + +Remember to delete all unused imports when you're done from the class you've just annotated. + +## Generics Annotations + +When annotating generic types and methods, follow these JSpecify rules: + +### Type Parameter Bounds + +The bound on a type parameter determines whether nullable type arguments are allowed: + +| Declaration | Allows `@Nullable` type argument? | +|-------------|----------------------------------| +| `` | ❌ No — `Box<@Nullable String>` is illegal | +| `` | ✅ Yes — `Box<@Nullable String>` is legal | + +**When to use ``:** +- When callers genuinely need to parameterize with nullable types +- Example: `DataFetcherResult` — data fetchers may return nullable types + +**When to keep ``:** +- When the type parameter represents a concrete non-null object +- Even if some methods return `@Nullable T` (meaning "can be null even if T is non-null") +- Example: `Edge` with `@Nullable T getNode()` — node may be null, but T represents the object type + diff --git a/.claude/commands/jspecify-team-prompt.md b/.claude/commands/jspecify-team-prompt.md new file mode 100644 index 0000000000..33c0832002 --- /dev/null +++ b/.claude/commands/jspecify-team-prompt.md @@ -0,0 +1,375 @@ +# JSpecify Annotation Team Orchestrator Prompt + +You are an orchestrator agent responsible for coordinating a team of worker +agents to add JSpecify nullability annotations to public API classes in the +graphql-java project. + +## Your Job + +1. Read the current exemption list from: + `src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy` + Before building batch assignments, check which classes from the lists below + are still present in the exemption list. Only assign classes that remain. + This makes the orchestrator safe to re-run after interruption. + +2. Group the remaining classes into batches (see batches below). + +3. Launch worker agents in **waves of 5** to maximize throughput. + After each wave completes, launch the reviewer before starting the next wave. + Only proceed to the next wave if the reviewer reports no NullAway errors. + +4. After all waves are complete, launch the validator. + +## Execution Flow + +### Wave 1 +Launch Workers 1, 2, 3, 4, 5 in parallel. Wait for all to complete. +Launch Reviewer for Wave 1. Wait for completion and NullAway confirmation. + +### Wave 2 +Launch Workers 6, 7 in parallel. Wait for all to complete. +Launch Reviewer for Wave 2. Wait for completion and NullAway confirmation. + +### Final Step +Launch the validator to remove completed classes from +`JSpecifyAnnotationsCheck.groovy` and push the branch. + +--- + +## Batch Assignments + +### Worker 1 — graphql.language (remaining) + graphql.normalized (23 classes) +``` +graphql.language.ScalarTypeDefinition +graphql.language.ScalarTypeExtensionDefinition +graphql.language.SchemaDefinition +graphql.language.SchemaExtensionDefinition +graphql.language.Selection +graphql.language.SelectionSet +graphql.language.SelectionSetContainer +graphql.language.TypeKind +graphql.language.TypeName +graphql.language.UnionTypeDefinition +graphql.language.UnionTypeExtensionDefinition +graphql.language.VariableDefinition +graphql.normalized.ExecutableNormalizedField +graphql.normalized.ExecutableNormalizedOperation +graphql.normalized.ExecutableNormalizedOperationFactory +graphql.normalized.ExecutableNormalizedOperationToAstCompiler +graphql.normalized.NormalizedInputValue +graphql.normalized.incremental.NormalizedDeferredExecution +graphql.normalized.nf.NormalizedDocument +graphql.normalized.nf.NormalizedDocumentFactory +graphql.normalized.nf.NormalizedField +graphql.normalized.nf.NormalizedOperation +graphql.normalized.nf.NormalizedOperationToAstCompiler +``` + +### Worker 2 — graphql.schema core types (38 classes) +``` +graphql.schema.AsyncDataFetcher +graphql.schema.CoercingParseLiteralException +graphql.schema.CoercingParseValueException +graphql.schema.CoercingSerializeException +graphql.schema.DataFetcherFactories +graphql.schema.DataFetcherFactoryEnvironment +graphql.schema.DataFetchingFieldSelectionSet +graphql.schema.DefaultGraphqlTypeComparatorRegistry +graphql.schema.DelegatingDataFetchingEnvironment +graphql.schema.FieldCoordinates +graphql.schema.GraphQLAppliedDirectiveArgument +graphql.schema.GraphQLArgument +graphql.schema.GraphQLCompositeType +graphql.schema.GraphQLDirective +graphql.schema.GraphQLDirectiveContainer +graphql.schema.GraphQLEnumValueDefinition +graphql.schema.GraphQLFieldDefinition +graphql.schema.GraphQLFieldsContainer +graphql.schema.GraphQLImplementingType +graphql.schema.GraphQLInputFieldsContainer +graphql.schema.GraphQLInputObjectField +graphql.schema.GraphQLInputObjectType +graphql.schema.GraphQLInputSchemaElement +graphql.schema.GraphQLInputType +graphql.schema.GraphQLInputValueDefinition +graphql.schema.GraphQLInterfaceType +graphql.schema.GraphQLModifiedType +graphql.schema.GraphQLNamedOutputType +graphql.schema.GraphQLNamedSchemaElement +graphql.schema.GraphQLNamedType +graphql.schema.GraphQLNonNull +graphql.schema.GraphQLNullableType +graphql.schema.GraphQLObjectType +graphql.schema.GraphQLOutputType +graphql.schema.GraphQLSchemaElement +graphql.schema.GraphQLTypeReference +graphql.schema.GraphQLTypeVisitor +graphql.schema.GraphQLTypeVisitorStub +graphql.schema.GraphQLUnmodifiedType +``` + +### Worker 3 — graphql.schema utilities + sub-packages (41 classes) +``` +graphql.schema.GraphqlElementParentTree +graphql.schema.GraphqlTypeComparatorEnvironment +graphql.schema.GraphqlTypeComparatorRegistry +graphql.schema.InputValueWithState +graphql.schema.SchemaElementChildrenContainer +graphql.schema.SchemaTransformer +graphql.schema.SchemaTraverser +graphql.schema.SelectedField +graphql.schema.StaticDataFetcher +graphql.schema.diff.DiffCategory +graphql.schema.diff.DiffEvent +graphql.schema.diff.DiffLevel +graphql.schema.diff.DiffSet +graphql.schema.diff.SchemaDiffSet +graphql.schema.diff.reporting.CapturingReporter +graphql.schema.diff.reporting.ChainedReporter +graphql.schema.diff.reporting.PrintStreamReporter +graphql.schema.idl.CombinedWiringFactory +graphql.schema.idl.MapEnumValuesProvider +graphql.schema.idl.NaturalEnumValuesProvider +graphql.schema.idl.RuntimeWiring +graphql.schema.idl.SchemaDirectiveWiring +graphql.schema.idl.SchemaDirectiveWiringEnvironment +graphql.schema.idl.SchemaGenerator +graphql.schema.idl.SchemaPrinter +graphql.schema.idl.TypeRuntimeWiring +graphql.schema.idl.errors.SchemaProblem +graphql.schema.idl.errors.StrictModeWiringException +graphql.schema.transform.FieldVisibilitySchemaTransformation +graphql.schema.transform.VisibleFieldPredicateEnvironment +graphql.schema.usage.SchemaUsage +graphql.schema.usage.SchemaUsageSupport +graphql.schema.validation.OneOfInputObjectRules +graphql.schema.validation.SchemaValidationErrorClassification +graphql.schema.visibility.BlockedFields +graphql.schema.visibility.DefaultGraphqlFieldVisibility +graphql.schema.visibility.GraphqlFieldVisibility +graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility +graphql.schema.visitor.GraphQLSchemaTraversalControl +``` + +### Worker 4 — graphql.extensions + graphql.incremental + graphql.introspection (15 classes) +``` +graphql.extensions.ExtensionsBuilder +graphql.incremental.DeferPayload +graphql.incremental.DelayedIncrementalPartialResult +graphql.incremental.DelayedIncrementalPartialResultImpl +graphql.incremental.IncrementalExecutionResult +graphql.incremental.IncrementalExecutionResultImpl +graphql.incremental.IncrementalPayload +graphql.incremental.StreamPayload +graphql.introspection.GoodFaithIntrospection +graphql.introspection.Introspection +graphql.introspection.IntrospectionQuery +graphql.introspection.IntrospectionQueryBuilder +graphql.introspection.IntrospectionResultToSchema +graphql.introspection.IntrospectionWithDirectivesSupport +graphql.introspection.IntrospectionWithDirectivesSupport$DirectivePredicateEnvironment +``` + +### Worker 5 — graphql.parser + graphql.util (18 classes) +``` +graphql.parser.InvalidSyntaxException +graphql.parser.MultiSourceReader +graphql.parser.Parser +graphql.parser.ParserEnvironment +graphql.parser.ParserOptions +graphql.util.CyclicSchemaAnalyzer +graphql.util.NodeAdapter +graphql.util.NodeLocation +graphql.util.NodeMultiZipper +graphql.util.NodeZipper +graphql.util.querygenerator.QueryGenerator +graphql.util.querygenerator.QueryGeneratorOptions +graphql.util.querygenerator.QueryGeneratorOptions$QueryGeneratorOptionsBuilder +graphql.util.querygenerator.QueryGeneratorResult +graphql.util.TraversalControl +graphql.util.TraverserContext +graphql.util.TreeTransformer +graphql.util.TreeTransformerUtil +``` + +### Worker 6 — graphql.execution core (26 classes) +``` +graphql.execution.AbortExecutionException +graphql.execution.AsyncExecutionStrategy +graphql.execution.AsyncSerialExecutionStrategy +graphql.execution.CoercedVariables +graphql.execution.DataFetcherExceptionHandlerParameters +graphql.execution.DataFetcherExceptionHandlerResult +graphql.execution.DefaultValueUnboxer +graphql.execution.ExecutionContext +graphql.execution.ExecutionId +graphql.execution.ExecutionStepInfo +graphql.execution.ExecutionStrategyParameters +graphql.execution.FetchedValue +graphql.execution.FieldValueInfo +graphql.execution.InputMapDefinesTooManyFieldsException +graphql.execution.MergedSelectionSet +graphql.execution.MissingRootTypeException +graphql.execution.NonNullableValueCoercedAsNullException +graphql.execution.NormalizedVariables +graphql.execution.OneOfNullValueException +graphql.execution.OneOfTooManyKeysException +graphql.execution.ResultNodesInfo +graphql.execution.ResultPath +graphql.execution.SimpleDataFetcherExceptionHandler +graphql.execution.SubscriptionExecutionStrategy +graphql.execution.UnknownOperationException +graphql.execution.UnresolvedTypeException +``` + +### Worker 7 — graphql.execution sub-packages (24 classes) +``` +graphql.execution.conditional.ConditionalNodeDecision +graphql.execution.directives.QueryAppliedDirective +graphql.execution.directives.QueryAppliedDirectiveArgument +graphql.execution.directives.QueryDirectives +graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation +graphql.execution.instrumentation.fieldvalidation.SimpleFieldValidation +graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters +graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters +graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters +graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters +graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters +graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters +graphql.execution.instrumentation.parameters.InstrumentationFieldParameters +graphql.execution.instrumentation.parameters.InstrumentationValidationParameters +graphql.execution.instrumentation.tracing.TracingInstrumentation +graphql.execution.instrumentation.tracing.TracingSupport +graphql.execution.preparsed.PreparsedDocumentEntry +graphql.execution.preparsed.persisted.ApolloPersistedQuerySupport +graphql.execution.preparsed.persisted.InMemoryPersistedQueryCache +graphql.execution.preparsed.persisted.PersistedQueryCacheMiss +graphql.execution.preparsed.persisted.PersistedQueryIdInvalid +graphql.execution.preparsed.persisted.PersistedQueryNotFound +graphql.execution.reactive.DelegatingSubscription +graphql.execution.reactive.SubscriptionPublisher +``` + +--- + +## Instructions for Each Worker Agent + +Each worker agent must follow these rules for every class in its batch: + +### Per-class steps +1. Find the class at `src/main/java/.java` +2. Add `@NullMarked` at the class level +3. Remove redundant `@NonNull` annotations added by IntelliJ +4. Check Javadoc `@param` tags mentioning "null", "nullable", "may be null" +5. Check Javadoc `@return` tags mentioning "null", "optional", "if available" +6. Check method bodies that return null or check for null +7. Consult the GraphQL spec (https://spec.graphql.org/draft/) for the + relevant concept — prioritize spec nullability over IntelliJ inference +8. If the class has a static Builder inner class, annotate it `@NullUnmarked` + and skip further annotation of that inner class +9. Delete unused imports from the class after editing +10. Do NOT modify `JSpecifyAnnotationsCheck.groovy` + +### IntelliJ inspection check +After editing each file, use the IntelliJ MCP `inspections` tool to check +for warnings in the modified file. Fix any nullability-related warnings +before moving on. This catches issues that NullAway alone may miss. + +### Generics rules +- `` does NOT allow `@Nullable` type arguments (`Box<@Nullable String>` illegal) +- `` DOES allow `@Nullable` type arguments +- Use `` only when callers genuinely need nullable types + +### After each class +- Commit with message: `"Add JSpecify annotations to ClassName"` +- `git pull` before starting the next class to pick up any concurrent changes + +### If a class has NullAway errors +- Make the smallest possible fix +- Use `Objects.requireNonNull(x, "message")` or `assertNotNull(x, "message")` + only as a last resort +- Do NOT run the full build — leave that for the reviewer + +--- + +## Reviewer Agent Instructions + +After each wave of workers completes, launch a single reviewer agent. +The reviewer must NOT read any worker agent output or context — it works +only from the git diff. + +### Step 1 — Get the diffs + +For each class annotated in this wave, get its diff: +``` +git diff origin/main...HEAD -- +``` + +### Step 2 — Review each diff independently + +For each changed file, check: + +**JSpecify correctness:** +- `@NullMarked` is present at the class level +- No redundant `@NonNull` annotations remain +- `@Nullable` is used where null is genuinely possible, not speculatively +- `` only used when callers truly need nullable + type arguments +- Builder inner classes are marked `@NullUnmarked` and nothing more + +**GraphQL spec compliance:** +Fetch https://spec.graphql.org/draft/ for the relevant concept. +`MUST` = non-null. Conditional/IF = nullable. +Does the annotation match the spec? + +**Unused imports:** +Are there leftover imports (e.g. `org.jetbrains.annotations.NotNull`)? + +### Step 3 — Fix any issues found + +Make the minimal fix directly in the file. +Do NOT revert the whole annotation — fix only the specific problem. +Do NOT modify `JSpecifyAnnotationsCheck.groovy`. + +If you make any fixes, commit with: +``` +"Review fixes: correct JSpecify annotations for ClassName" +``` + +### Step 4 — Run the compile check + +``` +./gradlew compileJava +``` + +If it passes, report success and stop. + +If it fails: +- Read the NullAway error output carefully +- Make the smallest possible fix (prefer `assertNotNull(x, "message")` + over restructuring code) +- Re-run `./gradlew compileJava` +- Repeat until it passes or you have tried 3 times +- If still failing after 3 attempts, report the error details to the + orchestrator so it can decide whether to continue to the next wave + +--- + +## Validator Agent Instructions + +All waves are complete and the reviewer has confirmed `compileJava` passes +for each wave. Your job is cleanup and push only. + +1. Read the current exemption list from `JSpecifyAnnotationsCheck.groovy` +2. For each class that now has `@NullMarked` in its source file, remove it + from the `JSPECIFY_EXEMPTION_LIST` in `JSpecifyAnnotationsCheck.groovy` +3. Run `./gradlew compileJava` one final time to confirm the full build passes +4. Commit: `"Remove annotated classes from JSpecify exemption list"` +5. Push: `git push -u origin claude/jspecify-wave4` + +--- + +## Repository Details +- Working directory: `/Users/dondonz/Development/graphql-java` +- Branch: `claude/jspecify-wave4` +- NullAway compile check: `./gradlew compileJava` diff --git a/src/main/java/graphql/analysis/QueryTransformer.java b/src/main/java/graphql/analysis/QueryTransformer.java index b1a8b63c20..2d526f0973 100644 --- a/src/main/java/graphql/analysis/QueryTransformer.java +++ b/src/main/java/graphql/analysis/QueryTransformer.java @@ -84,7 +84,7 @@ public Node transform(QueryVisitor queryVisitor) { @Override public TraversalControl enter(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } @Override @@ -93,7 +93,7 @@ public TraversalControl leave(TraverserContext context) { return TraversalControl.CONTINUE; } }; - return new TreeTransformer<>(AST_NODE_ADAPTER).transform(root, nodeTraverserVisitor, rootVars); + return assertNotNull(new TreeTransformer<>(AST_NODE_ADAPTER).transform(root, nodeTraverserVisitor, rootVars), "transform result should not be null"); } public static Builder newQueryTransformer() { diff --git a/src/main/java/graphql/collect/ImmutableKit.java b/src/main/java/graphql/collect/ImmutableKit.java index 15b148c2fa..fe2842486e 100644 --- a/src/main/java/graphql/collect/ImmutableKit.java +++ b/src/main/java/graphql/collect/ImmutableKit.java @@ -208,13 +208,13 @@ public static ImmutableSet addToSet(Collection existing, T n * @return a filtered list */ @SafeVarargs - public static List filterVarArgs(Predicate filter, T... args) { + public static List filterVarArgs(Predicate filter, @Nullable T... args) { if (args.length == 0) { return ImmutableList.of(); } ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(args.length); - for (T arg : args) { - if (filter.test(arg)) { + for (@Nullable T arg : args) { + if (arg != null && filter.test(arg)) { builder.add(arg); } } diff --git a/src/main/java/graphql/execution/preparsed/PreparsedDocumentEntry.java b/src/main/java/graphql/execution/preparsed/PreparsedDocumentEntry.java index 322c9978f7..cd878497c0 100644 --- a/src/main/java/graphql/execution/preparsed/PreparsedDocumentEntry.java +++ b/src/main/java/graphql/execution/preparsed/PreparsedDocumentEntry.java @@ -58,6 +58,7 @@ public Document getDocument() { return document; } + @Nullable public List getErrors() { return errors; } diff --git a/src/main/java/graphql/extensions/ExtensionsBuilder.java b/src/main/java/graphql/extensions/ExtensionsBuilder.java index e65e315e2f..6234ca5066 100644 --- a/src/main/java/graphql/extensions/ExtensionsBuilder.java +++ b/src/main/java/graphql/extensions/ExtensionsBuilder.java @@ -3,7 +3,7 @@ import com.google.common.collect.ImmutableMap; import graphql.ExecutionResult; import graphql.PublicApi; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.Collections; @@ -26,6 +26,7 @@ * is placed in the {@link ExecutionResult} */ @PublicApi +@NullMarked public class ExtensionsBuilder { // thread safe since there can be many changes say in DFs across threads @@ -69,7 +70,7 @@ public int getChangeCount() { * * @return this builder for fluent style reasons */ - public ExtensionsBuilder addValues(@NonNull Map newValues) { + public ExtensionsBuilder addValues(Map newValues) { assertNotNull(newValues); if (!newValues.isEmpty()) { changes.add(newValues); @@ -85,7 +86,7 @@ public ExtensionsBuilder addValues(@NonNull Map newValues) { * * @return this builder for fluent style reasons */ - public ExtensionsBuilder addValue(@NonNull Object key, @Nullable Object value) { + public ExtensionsBuilder addValue(Object key, @Nullable Object value) { assertNotNull(key); return addValues(Collections.singletonMap(key, value)); } diff --git a/src/main/java/graphql/incremental/DeferPayload.java b/src/main/java/graphql/incremental/DeferPayload.java index ddec3d531b..f8ee1ee223 100644 --- a/src/main/java/graphql/incremental/DeferPayload.java +++ b/src/main/java/graphql/incremental/DeferPayload.java @@ -3,6 +3,8 @@ import graphql.ExecutionResult; import graphql.ExperimentalApi; import graphql.GraphQLError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; @@ -14,10 +16,11 @@ * Represents a defer payload */ @ExperimentalApi +@NullMarked public class DeferPayload extends IncrementalPayload { - private final Object data; + private final @Nullable Object data; - private DeferPayload(Object data, List path, String label, List errors, Map extensions) { + private DeferPayload(@Nullable Object data, List path, @Nullable String label, @Nullable List errors, @Nullable Map extensions) { super(path, label, errors, extensions); this.data = data; } @@ -48,7 +51,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; if (!super.equals(obj)) return false; @@ -63,6 +66,7 @@ public static DeferPayload.Builder newDeferredItem() { return new DeferPayload.Builder(); } + @NullUnmarked public static class Builder extends IncrementalPayload.Builder { private Object data = null; diff --git a/src/main/java/graphql/incremental/DelayedIncrementalPartialResult.java b/src/main/java/graphql/incremental/DelayedIncrementalPartialResult.java index 5992e3c83a..dfbdecf88a 100644 --- a/src/main/java/graphql/incremental/DelayedIncrementalPartialResult.java +++ b/src/main/java/graphql/incremental/DelayedIncrementalPartialResult.java @@ -1,6 +1,7 @@ package graphql.incremental; import graphql.ExperimentalApi; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.List; @@ -13,6 +14,7 @@ * {@link DelayedIncrementalPartialResult} */ @ExperimentalApi +@NullMarked public interface DelayedIncrementalPartialResult { /** * @return a list of defer and/or stream payloads. diff --git a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java index 3412d77298..54c95ed03d 100644 --- a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java +++ b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java @@ -2,6 +2,9 @@ import graphql.ExperimentalApi; import graphql.util.FpKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.Collections; import java.util.LinkedHashMap; @@ -9,10 +12,11 @@ import java.util.Map; @ExperimentalApi +@NullMarked public class DelayedIncrementalPartialResultImpl implements DelayedIncrementalPartialResult { - private final List incrementalItems; + private final @Nullable List incrementalItems; private final boolean hasNext; - private final Map extensions; + private final @Nullable Map extensions; private DelayedIncrementalPartialResultImpl(Builder builder) { this.incrementalItems = builder.incrementalItems; @@ -21,7 +25,7 @@ private DelayedIncrementalPartialResultImpl(Builder builder) { } @Override - public List getIncremental() { + public @Nullable List getIncremental() { return this.incrementalItems; } @@ -31,7 +35,7 @@ public boolean hasNext() { } @Override - public Map getExtensions() { + public @Nullable Map getExtensions() { return this.extensions; } @@ -62,6 +66,7 @@ public static Builder newIncrementalExecutionResult() { return new Builder(); } + @NullUnmarked public static class Builder { private boolean hasNext = false; private List incrementalItems = Collections.emptyList(); diff --git a/src/main/java/graphql/incremental/IncrementalExecutionResult.java b/src/main/java/graphql/incremental/IncrementalExecutionResult.java index 7d59fbe958..25e9f53a85 100644 --- a/src/main/java/graphql/incremental/IncrementalExecutionResult.java +++ b/src/main/java/graphql/incremental/IncrementalExecutionResult.java @@ -2,6 +2,7 @@ import graphql.ExecutionResult; import graphql.ExperimentalApi; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; @@ -83,6 +84,7 @@ * The execution behaviour should match what we get from running Apollo Server 4.9.5 with graphql-js v17.0.0-alpha.2 */ @ExperimentalApi +@NullMarked public interface IncrementalExecutionResult extends ExecutionResult { /** * Indicates whether there are pending incremental data. diff --git a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java index d067ec1327..6f4ca087d2 100644 --- a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java +++ b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java @@ -3,6 +3,8 @@ import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.ExperimentalApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; @@ -13,9 +15,10 @@ import java.util.function.Consumer; @ExperimentalApi +@NullMarked public class IncrementalExecutionResultImpl extends ExecutionResultImpl implements IncrementalExecutionResult { private final boolean hasNext; - private final List incremental; + private final @Nullable List incremental; private final Publisher incrementalItemPublisher; private IncrementalExecutionResultImpl(Builder builder) { @@ -79,6 +82,7 @@ public Map toSpecification() { return map; } + @NullUnmarked public static class Builder extends ExecutionResultImpl.Builder { private boolean hasNext = true; public List incremental; diff --git a/src/main/java/graphql/incremental/IncrementalPayload.java b/src/main/java/graphql/incremental/IncrementalPayload.java index 742d857c89..ff9fa9b1ef 100644 --- a/src/main/java/graphql/incremental/IncrementalPayload.java +++ b/src/main/java/graphql/incremental/IncrementalPayload.java @@ -4,6 +4,8 @@ import graphql.GraphQLError; import graphql.execution.ResultPath; import graphql.util.FpKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -16,13 +18,14 @@ * Represents a payload that can be resolved after the initial response. */ @ExperimentalApi +@NullMarked public abstract class IncrementalPayload { private final List path; - private final String label; - private final List errors; - private final transient Map extensions; + private final @Nullable String label; + private final @Nullable List errors; + private final transient @Nullable Map extensions; - protected IncrementalPayload(List path, String label, List errors, Map extensions) { + protected IncrementalPayload(List path, @Nullable String label, @Nullable List errors, @Nullable Map extensions) { this.path = path; this.errors = errors; this.label = label; @@ -90,7 +93,7 @@ public int hashCode() { return Objects.hash(path, label, errors, extensions); } - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -104,6 +107,7 @@ public boolean equals(Object obj) { Objects.equals(extensions, that.extensions); } + @NullUnmarked protected static abstract class Builder> { protected List path; protected String label; diff --git a/src/main/java/graphql/incremental/StreamPayload.java b/src/main/java/graphql/incremental/StreamPayload.java index c8b0f652e8..d86ac1a18f 100644 --- a/src/main/java/graphql/incremental/StreamPayload.java +++ b/src/main/java/graphql/incremental/StreamPayload.java @@ -2,6 +2,8 @@ import graphql.ExperimentalApi; import graphql.GraphQLError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; @@ -13,10 +15,11 @@ * Represents a stream payload */ @ExperimentalApi +@NullMarked public class StreamPayload extends IncrementalPayload { - private final List items; + private final @Nullable List items; - private StreamPayload(List items, List path, String label, List errors, Map extensions) { + private StreamPayload(@Nullable List items, List path, @Nullable String label, @Nullable List errors, @Nullable Map extensions) { super(path, label, errors, extensions); this.items = items; } @@ -47,7 +50,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; if (!super.equals(obj)) return false; @@ -62,6 +65,7 @@ public static StreamPayload.Builder newStreamedItem() { return new StreamPayload.Builder(); } + @NullUnmarked public static class Builder extends IncrementalPayload.Builder { private List items = null; diff --git a/src/main/java/graphql/introspection/GoodFaithIntrospection.java b/src/main/java/graphql/introspection/GoodFaithIntrospection.java index 2d40425cee..e9f5741807 100644 --- a/src/main/java/graphql/introspection/GoodFaithIntrospection.java +++ b/src/main/java/graphql/introspection/GoodFaithIntrospection.java @@ -4,6 +4,10 @@ import graphql.GraphQLContext; import graphql.GraphQLError; import graphql.PublicApi; +import graphql.execution.AbortExecutionException; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import graphql.execution.ExecutionContext; import graphql.language.SourceLocation; import graphql.validation.QueryComplexityLimits; import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/graphql/introspection/Introspection.java b/src/main/java/graphql/introspection/Introspection.java index 4dd3f9aa63..8e43501003 100644 --- a/src/main/java/graphql/introspection/Introspection.java +++ b/src/main/java/graphql/introspection/Introspection.java @@ -37,7 +37,8 @@ import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLUnionType; import graphql.schema.InputValueWithState; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.HashSet; @@ -75,6 +76,7 @@ * to disable introspection for that request. */ @PublicApi +@NullMarked public class Introspection { @@ -117,11 +119,12 @@ public static Optional isIntrospectionSensible(MergedSelectionS GraphQLContext graphQLContext = executionContext.getGraphQLContext(); for (String key : mergedSelectionSet.getKeys()) { - String fieldName = mergedSelectionSet.getSubField(key).getName(); + MergedField subField = Assert.assertNotNull(mergedSelectionSet.getSubField(key), "subField for key '%s' should not be null", key); + String fieldName = subField.getName(); if (fieldName.equals(SchemaMetaFieldDef.getName()) || fieldName.equals(TypeMetaFieldDef.getName())) { if (!isIntrospectionEnabled(graphQLContext)) { - return mkDisabledError(mergedSelectionSet.getSubField(key)); + return mkDisabledError(subField); } break; } @@ -129,7 +132,6 @@ public static Optional isIntrospectionSensible(MergedSelectionS return Optional.empty(); } - @NonNull private static Optional mkDisabledError(MergedField schemaField) { IntrospectionDisabledError error = new IntrospectionDisabledError(schemaField.getSingleField().getSourceLocation()); return Optional.of(ExecutionResult.newExecutionResult().addError(error).build()); @@ -158,7 +160,7 @@ private static void register(GraphQLFieldsContainer parentType, String fieldName * @param getter the function to call to get a value of T * @param for two */ - private static void register(GraphQLFieldsContainer parentType, String fieldName, Class targetClass, Function getter) { + private static void register(GraphQLFieldsContainer parentType, String fieldName, Class targetClass, Function getter) { IntrospectionDataFetcher dataFetcher = env -> { Object source = env.getSource(); if (targetClass.isInstance(source)) { @@ -223,7 +225,7 @@ public enum TypeKind { } else if (type instanceof GraphQLNonNull) { return TypeKind.NON_NULL; } else { - return Assert.assertShouldNeverHappen("Unknown kind of type: %s", type); + return Assert.assertShouldNeverHappen("Unknown kind of type: %s", String.valueOf(type)); } }; private static final IntrospectionDataFetcher nameDataFetcher = environment -> { @@ -348,9 +350,8 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, static { IntrospectionDataFetcher argsDataFetcher = environment -> { - Object type = environment.getSource(); - GraphQLFieldDefinition fieldDef = (GraphQLFieldDefinition) type; - Boolean includeDeprecated = environment.getArgument("includeDeprecated"); + GraphQLFieldDefinition fieldDef = Assert.assertNotNull(environment.getSource()); + boolean includeDeprecated = Boolean.TRUE.equals(environment.getArgument("includeDeprecated")); return ImmutableKit.filter(fieldDef.getArguments(), arg -> includeDeprecated || !arg.isDeprecated()); }; @@ -391,7 +392,7 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, Object type = environment.getSource(); if (type instanceof GraphQLFieldsContainer) { GraphQLFieldsContainer fieldsContainer = (GraphQLFieldsContainer) type; - Boolean includeDeprecated = environment.getArgument("includeDeprecated"); + boolean includeDeprecated = Boolean.TRUE.equals(environment.getArgument("includeDeprecated")); List fieldDefinitions = environment .getGraphQLSchema() .getCodeRegistry() @@ -432,7 +433,7 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, private static final IntrospectionDataFetcher enumValuesTypesFetcher = environment -> { Object type = environment.getSource(); if (type instanceof GraphQLEnumType) { - Boolean includeDeprecated = environment.getArgument("includeDeprecated"); + boolean includeDeprecated = Boolean.TRUE.equals(environment.getArgument("includeDeprecated")); List values = ((GraphQLEnumType) type).getValues(); if (includeDeprecated) { return values; @@ -446,7 +447,7 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, private static final IntrospectionDataFetcher inputFieldsFetcher = environment -> { Object type = environment.getSource(); if (type instanceof GraphQLInputObjectType) { - Boolean includeDeprecated = environment.getArgument("includeDeprecated"); + boolean includeDeprecated = Boolean.TRUE.equals(environment.getArgument("includeDeprecated")); List inputFields = environment .getGraphQLSchema() .getCodeRegistry() @@ -635,12 +636,12 @@ public enum DirectiveLocation { static { IntrospectionDataFetcher locationsDataFetcher = environment -> { - GraphQLDirective directive = environment.getSource(); + GraphQLDirective directive = Assert.assertNotNull(environment.getSource()); return new ArrayList<>(directive.validLocations()); }; IntrospectionDataFetcher argsDataFetcher = environment -> { - GraphQLDirective directive = environment.getSource(); - Boolean includeDeprecated = environment.getArgument("includeDeprecated"); + GraphQLDirective directive = Assert.assertNotNull(environment.getSource()); + boolean includeDeprecated = Boolean.TRUE.equals(environment.getArgument("includeDeprecated")); return ImmutableKit.filter(directive.getArguments(), arg -> includeDeprecated || !arg.isDeprecated()); }; @@ -709,7 +710,7 @@ public enum DirectiveLocation { public static final IntrospectionDataFetcher SchemaMetaFieldDefDataFetcher = IntrospectionDataFetchingEnvironment::getGraphQLSchema; public static final IntrospectionDataFetcher TypeMetaFieldDefDataFetcher = environment -> { - String name = environment.getArgument("name"); + String name = Assert.assertNotNull(environment.getArgument("name")); return environment.getGraphQLSchema().getType(name); }; @@ -728,7 +729,7 @@ public static GraphQLFieldDefinition buildSchemaField(GraphQLObjectType introspe @Internal public static GraphQLFieldDefinition buildTypeField(GraphQLObjectType introspectionSchemaType) { - GraphQLOutputType fieldType = introspectionSchemaType.getFieldDefinition("types").getType(); + GraphQLOutputType fieldType = Assert.assertNotNull(introspectionSchemaType.getFieldDefinition("types"), "types field should not be null").getType(); GraphQLObjectType underscoreType = unwrapAllAs(fieldType); return newFieldDefinition() .name("__type") @@ -793,8 +794,7 @@ public static GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLCo assertTrue(parentType instanceof GraphQLFieldsContainer, "should not happen : parent type must be an object or interface %s", parentType); GraphQLFieldsContainer fieldsContainer = (GraphQLFieldsContainer) parentType; fieldDefinition = schema.getCodeRegistry().getFieldVisibility().getFieldDefinition(fieldsContainer, fieldName); - assertTrue(fieldDefinition != null, "Unknown field '%s' for type %s", fieldName, fieldsContainer.getName()); - return fieldDefinition; + return Assert.assertNotNull(fieldDefinition, "Unknown field '%s' for type %s", fieldName, fieldsContainer.getName()); } /** @@ -821,7 +821,7 @@ public static GraphQLFieldDefinition getFieldDefinition(GraphQLSchema schema, Gr return fieldDefinition; } - private static GraphQLFieldDefinition getSystemFieldDef(GraphQLSchema schema, GraphQLCompositeType parentType, String fieldName) { + private static @Nullable GraphQLFieldDefinition getSystemFieldDef(GraphQLSchema schema, GraphQLCompositeType parentType, String fieldName) { if (schema.getQueryType() == parentType) { if (fieldName.equals(schema.getIntrospectionSchemaFieldDefinition().getName())) { return schema.getIntrospectionSchemaFieldDefinition(); diff --git a/src/main/java/graphql/introspection/IntrospectionQuery.java b/src/main/java/graphql/introspection/IntrospectionQuery.java index 694ccaab42..9696da0e29 100644 --- a/src/main/java/graphql/introspection/IntrospectionQuery.java +++ b/src/main/java/graphql/introspection/IntrospectionQuery.java @@ -1,8 +1,10 @@ package graphql.introspection; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; @PublicApi +@NullMarked public interface IntrospectionQuery { /** * This is the default introspection query provided by graphql-java diff --git a/src/main/java/graphql/introspection/IntrospectionQueryBuilder.java b/src/main/java/graphql/introspection/IntrospectionQueryBuilder.java index d4d37005b0..f3b39feebe 100644 --- a/src/main/java/graphql/introspection/IntrospectionQueryBuilder.java +++ b/src/main/java/graphql/introspection/IntrospectionQueryBuilder.java @@ -3,6 +3,8 @@ import com.google.common.collect.ImmutableList; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.language.AstPrinter; import graphql.language.BooleanValue; import graphql.language.Document; @@ -26,6 +28,7 @@ * by the options you specify */ @PublicApi +@NullMarked public class IntrospectionQueryBuilder { public static class Options { @@ -222,7 +225,7 @@ public Options typeRefFragmentDepth(int typeRefFragmentDepth) { } @SafeVarargs - private static List filter(T... args) { + private static List filter(@Nullable T... args) { return ImmutableKit.filterVarArgs(Objects::nonNull, args); } diff --git a/src/main/java/graphql/introspection/IntrospectionResultToSchema.java b/src/main/java/graphql/introspection/IntrospectionResultToSchema.java index 81ae6ce928..99a629aca2 100644 --- a/src/main/java/graphql/introspection/IntrospectionResultToSchema.java +++ b/src/main/java/graphql/introspection/IntrospectionResultToSchema.java @@ -2,6 +2,8 @@ import graphql.ExecutionResult; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.language.Argument; import graphql.language.Description; import graphql.language.Directive; @@ -45,6 +47,7 @@ @SuppressWarnings("unchecked") @PublicApi +@NullMarked public class IntrospectionResultToSchema { /** @@ -54,12 +57,12 @@ public class IntrospectionResultToSchema { * * @return a IDL Document of the schema */ - public Document createSchemaDefinition(ExecutionResult introspectionResult) { + public @Nullable Document createSchemaDefinition(ExecutionResult introspectionResult) { if (!introspectionResult.isDataPresent()) { return null; } - Map introspectionResultMap = introspectionResult.getData(); + Map introspectionResultMap = assertNotNull(introspectionResult.getData(), "data expected"); return createSchemaDefinition(introspectionResultMap); } @@ -71,14 +74,11 @@ public Document createSchemaDefinition(ExecutionResult introspectionResult) { * * @return a IDL Document of the schema */ - @SuppressWarnings("unchecked") public Document createSchemaDefinition(Map introspectionResult) { - Map schema = (Map) introspectionResult.get("__schema"); - assertNotNull(schema, "__schema expected"); + Map schema = assertNotNull((Map) introspectionResult.get("__schema"), "__schema expected"); - Map queryType = (Map) schema.get("queryType"); - assertNotNull(queryType, "queryType expected"); - TypeName query = TypeName.newTypeName().name((String) queryType.get("name")).build(); + Map queryType = assertNotNull((Map) schema.get("queryType"), "queryType expected"); + TypeName query = TypeName.newTypeName().name(assertNotNull((String) queryType.get("name"), "query name expected")).build(); boolean nonDefaultQueryName = !"Query".equals(query.getName()); SchemaDefinition.Builder schemaDefinition = SchemaDefinition.newSchemaDefinition(); @@ -88,7 +88,7 @@ public Document createSchemaDefinition(Map introspectionResult) Map mutationType = (Map) schema.get("mutationType"); boolean nonDefaultMutationName = false; if (mutationType != null) { - TypeName mutation = TypeName.newTypeName().name((String) mutationType.get("name")).build(); + TypeName mutation = TypeName.newTypeName().name(assertNotNull((String) mutationType.get("name"), "mutation name expected")).build(); nonDefaultMutationName = !"Mutation".equals(mutation.getName()); schemaDefinition.operationTypeDefinition(OperationTypeDefinition.newOperationTypeDefinition().name("mutation").typeName(mutation).build()); } @@ -96,7 +96,7 @@ public Document createSchemaDefinition(Map introspectionResult) Map subscriptionType = (Map) schema.get("subscriptionType"); boolean nonDefaultSubscriptionName = false; if (subscriptionType != null) { - TypeName subscription = TypeName.newTypeName().name(((String) subscriptionType.get("name"))).build(); + TypeName subscription = TypeName.newTypeName().name(assertNotNull((String) subscriptionType.get("name"), "subscription name expected")).build(); nonDefaultSubscriptionName = !"Subscription".equals(subscription.getName()); schemaDefinition.operationTypeDefinition(OperationTypeDefinition.newOperationTypeDefinition().name("subscription").typeName(subscription).build()); } @@ -106,7 +106,7 @@ public Document createSchemaDefinition(Map introspectionResult) document.definition(schemaDefinition.build()); } - List> types = (List>) schema.get("types"); + List> types = assertNotNull((List>) schema.get("types"), "types expected"); for (Map type : types) { TypeDefinition typeDefinition = createTypeDefinition(type); if (typeDefinition == null) { @@ -129,8 +129,8 @@ public Document createSchemaDefinition(Map introspectionResult) return document.build(); } - private DirectiveDefinition createDirective(Map input) { - String directiveName = (String) input.get("name"); + private @Nullable DirectiveDefinition createDirective(Map input) { + String directiveName = assertNotNull((String) input.get("name"), "directive name expected"); if (isBuiltInDirective(directiveName)) { return null; } @@ -140,12 +140,12 @@ private DirectiveDefinition createDirective(Map input) { .name(directiveName) .description(toDescription(input)); - List locations = (List) input.get("locations"); + List locations = assertNotNull((List) input.get("locations"), "locations expected"); List directiveLocations = createDirectiveLocations(locations); directiveDefBuilder.directiveLocations(directiveLocations); - List> args = (List>) input.get("args"); + List> args = assertNotNull((List>) input.get("args"), "args expected"); List inputValueDefinitions = createInputValueDefinitions(args); directiveDefBuilder.inputValueDefinitions(inputValueDefinitions); Optional.ofNullable((Boolean) input.get("isRepeatable")).ifPresent(value -> directiveDefBuilder.repeatable(value)); @@ -163,9 +163,9 @@ private List createDirectiveLocations(List locations) return result; } - private TypeDefinition createTypeDefinition(Map type) { - String kind = (String) type.get("kind"); - String name = (String) type.get("name"); + private @Nullable TypeDefinition createTypeDefinition(Map type) { + String kind = assertNotNull((String) type.get("kind"), "kind expected"); + String name = assertNotNull((String) type.get("name"), "name expected"); if (name.startsWith("__")) { return null; } @@ -187,8 +187,8 @@ private TypeDefinition createTypeDefinition(Map type) { } } - private TypeDefinition createScalar(Map input) { - String name = (String) input.get("name"); + private @Nullable TypeDefinition createScalar(Map input) { + String name = assertNotNull((String) input.get("name"), "name expected"); if (ScalarInfo.isGraphqlSpecifiedScalar(name)) { return null; } @@ -199,36 +199,34 @@ private TypeDefinition createScalar(Map input) { } - @SuppressWarnings("unchecked") UnionTypeDefinition createUnion(Map input) { - assertTrue(input.get("kind").equals("UNION"), "wrong input"); + assertTrue("UNION".equals(input.get("kind")), "wrong input"); UnionTypeDefinition.Builder unionTypeDefinition = UnionTypeDefinition.newUnionTypeDefinition(); - unionTypeDefinition.name((String) input.get("name")); + unionTypeDefinition.name(assertNotNull((String) input.get("name"), "name expected")); unionTypeDefinition.description(toDescription(input)); - List> possibleTypes = (List>) input.get("possibleTypes"); + List> possibleTypes = assertNotNull((List>) input.get("possibleTypes"), "possibleTypes expected"); for (Map possibleType : possibleTypes) { - TypeName typeName = TypeName.newTypeName().name((String) possibleType.get("name")).build(); + TypeName typeName = TypeName.newTypeName().name(assertNotNull((String) possibleType.get("name"), "possibleType name expected")).build(); unionTypeDefinition.memberType(typeName); } return unionTypeDefinition.build(); } - @SuppressWarnings("unchecked") EnumTypeDefinition createEnum(Map input) { - assertTrue(input.get("kind").equals("ENUM"), "wrong input"); + assertTrue("ENUM".equals(input.get("kind")), "wrong input"); - EnumTypeDefinition.Builder enumTypeDefinition = EnumTypeDefinition.newEnumTypeDefinition().name((String) input.get("name")); + EnumTypeDefinition.Builder enumTypeDefinition = EnumTypeDefinition.newEnumTypeDefinition().name(assertNotNull((String) input.get("name"), "name expected")); enumTypeDefinition.description(toDescription(input)); - List> enumValues = (List>) input.get("enumValues"); + List> enumValues = assertNotNull((List>) input.get("enumValues"), "enumValues expected"); for (Map enumValue : enumValues) { - EnumValueDefinition.Builder enumValueDefinition = EnumValueDefinition.newEnumValueDefinition().name((String) enumValue.get("name")); + EnumValueDefinition.Builder enumValueDefinition = EnumValueDefinition.newEnumValueDefinition().name(assertNotNull((String) enumValue.get("name"), "enumValue name expected")); enumValueDefinition.description(toDescription(enumValue)); createDeprecatedDirective(enumValue, enumValueDefinition); @@ -239,54 +237,50 @@ EnumTypeDefinition createEnum(Map input) { return enumTypeDefinition.build(); } - @SuppressWarnings("unchecked") InterfaceTypeDefinition createInterface(Map input) { - assertTrue(input.get("kind").equals("INTERFACE"), "wrong input"); + assertTrue("INTERFACE".equals(input.get("kind")), "wrong input"); - InterfaceTypeDefinition.Builder interfaceTypeDefinition = InterfaceTypeDefinition.newInterfaceTypeDefinition().name((String) input.get("name")); + InterfaceTypeDefinition.Builder interfaceTypeDefinition = InterfaceTypeDefinition.newInterfaceTypeDefinition().name(assertNotNull((String) input.get("name"), "name expected")); interfaceTypeDefinition.description(toDescription(input)); - if (input.containsKey("interfaces") && input.get("interfaces") != null) { + List> interfaces = (List>) input.get("interfaces"); + if (interfaces != null) { interfaceTypeDefinition.implementz( - map( - (List>) input.get("interfaces"), - this::createTypeIndirection - ) + map(interfaces, this::createTypeIndirection) ); } - List> fields = (List>) input.get("fields"); + List> fields = assertNotNull((List>) input.get("fields"), "fields expected"); interfaceTypeDefinition.definitions(createFields(fields)); return interfaceTypeDefinition.build(); } - @SuppressWarnings("unchecked") InputObjectTypeDefinition createInputObject(Map input) { - assertTrue(input.get("kind").equals("INPUT_OBJECT"), "wrong input"); + assertTrue("INPUT_OBJECT".equals(input.get("kind")), "wrong input"); InputObjectTypeDefinition.Builder inputObjectTypeDefinition = InputObjectTypeDefinition.newInputObjectDefinition() - .name((String) input.get("name")) + .name(assertNotNull((String) input.get("name"), "name expected")) .description(toDescription(input)); - List> fields = (List>) input.get("inputFields"); + List> fields = assertNotNull((List>) input.get("inputFields"), "inputFields expected"); List inputValueDefinitions = createInputValueDefinitions(fields); inputObjectTypeDefinition.inputValueDefinitions(inputValueDefinitions); return inputObjectTypeDefinition.build(); } - @SuppressWarnings("unchecked") ObjectTypeDefinition createObject(Map input) { - assertTrue(input.get("kind").equals("OBJECT"), "wrong input"); + assertTrue("OBJECT".equals(input.get("kind")), "wrong input"); - ObjectTypeDefinition.Builder objectTypeDefinition = ObjectTypeDefinition.newObjectTypeDefinition().name((String) input.get("name")); + ObjectTypeDefinition.Builder objectTypeDefinition = ObjectTypeDefinition.newObjectTypeDefinition().name(assertNotNull((String) input.get("name"), "name expected")); objectTypeDefinition.description(toDescription(input)); - if (input.containsKey("interfaces")) { + List> interfaces = (List>) input.get("interfaces"); + if (interfaces != null) { objectTypeDefinition.implementz( - map((List>) input.get("interfaces"), this::createTypeIndirection) + map(interfaces, this::createTypeIndirection) ); } - List> fields = (List>) input.get("fields"); + List> fields = assertNotNull((List>) input.get("fields"), "fields expected"); objectTypeDefinition.fieldDefinitions(createFields(fields)); @@ -296,13 +290,13 @@ ObjectTypeDefinition createObject(Map input) { private List createFields(List> fields) { List result = new ArrayList<>(fields.size()); for (Map field : fields) { - FieldDefinition.Builder fieldDefinition = FieldDefinition.newFieldDefinition().name((String) field.get("name")); + FieldDefinition.Builder fieldDefinition = FieldDefinition.newFieldDefinition().name(assertNotNull((String) field.get("name"), "field name expected")); fieldDefinition.description(toDescription(field)); - fieldDefinition.type(createTypeIndirection((Map) field.get("type"))); + fieldDefinition.type(createTypeIndirection(assertNotNull((Map) field.get("type"), "field type expected"))); createDeprecatedDirective(field, fieldDefinition); - List> args = (List>) field.get("args"); + List> args = assertNotNull((List>) field.get("args"), "field args expected"); List inputValueDefinitions = createInputValueDefinitions(args); fieldDefinition.inputValueDefinitions(inputValueDefinitions); result.add(fieldDefinition.build()); @@ -322,12 +316,11 @@ private void createDeprecatedDirective(Map field, NodeDirectives } } - @SuppressWarnings("unchecked") private List createInputValueDefinitions(List> args) { List result = new ArrayList<>(args.size()); for (Map arg : args) { - Type argType = createTypeIndirection((Map) arg.get("type")); - InputValueDefinition.Builder inputValueDefinition = InputValueDefinition.newInputValueDefinition().name((String) arg.get("name")).type(argType); + Type argType = createTypeIndirection(assertNotNull((Map) arg.get("type"), "arg type expected")); + InputValueDefinition.Builder inputValueDefinition = InputValueDefinition.newInputValueDefinition().name(assertNotNull((String) arg.get("name"), "arg name expected")).type(argType); inputValueDefinition.description(toDescription(arg)); String valueLiteral = (String) arg.get("defaultValue"); @@ -340,9 +333,8 @@ private List createInputValueDefinitions(List type) { - String kind = (String) type.get("kind"); + String kind = assertNotNull((String) type.get("kind"), "kind expected"); switch (kind) { case "INTERFACE": case "OBJECT": @@ -350,17 +342,17 @@ private Type createTypeIndirection(Map type) { case "ENUM": case "INPUT_OBJECT": case "SCALAR": - return TypeName.newTypeName().name((String) type.get("name")).build(); + return TypeName.newTypeName().name(assertNotNull((String) type.get("name"), "name expected")).build(); case "NON_NULL": - return NonNullType.newNonNullType().type(createTypeIndirection((Map) type.get("ofType"))).build(); + return NonNullType.newNonNullType().type(createTypeIndirection(assertNotNull((Map) type.get("ofType"), "ofType expected"))).build(); case "LIST": - return ListType.newListType().type(createTypeIndirection((Map) type.get("ofType"))).build(); + return ListType.newListType().type(createTypeIndirection(assertNotNull((Map) type.get("ofType"), "ofType expected"))).build(); default: return assertShouldNeverHappen("Unknown kind %s", kind); } } - private Description toDescription(Map input) { + private @Nullable Description toDescription(Map input) { String description = (String) input.get("description"); if (description == null) { return null; diff --git a/src/main/java/graphql/introspection/IntrospectionWithDirectivesSupport.java b/src/main/java/graphql/introspection/IntrospectionWithDirectivesSupport.java index 5d264e113c..8b954a8f07 100644 --- a/src/main/java/graphql/introspection/IntrospectionWithDirectivesSupport.java +++ b/src/main/java/graphql/introspection/IntrospectionWithDirectivesSupport.java @@ -24,7 +24,8 @@ import graphql.schema.SchemaTransformer; import graphql.util.TraversalControl; import graphql.util.TraverserContext; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Set; @@ -76,6 +77,7 @@ * */ @PublicApi +@NullMarked public class IntrospectionWithDirectivesSupport { private final DirectivePredicate directivePredicate; @@ -131,7 +133,7 @@ public GraphQLSchema apply(GraphQLSchema schema) { GraphQLTypeVisitorStub visitor = new GraphQLTypeVisitorStub() { @Override public TraversalControl visitGraphQLObjectType(GraphQLObjectType objectType, TraverserContext context) { - GraphQLCodeRegistry.Builder codeRegistry = context.getVarFromParents(GraphQLCodeRegistry.Builder.class); + GraphQLCodeRegistry.Builder codeRegistry = assertNotNull(context.getVarFromParents(GraphQLCodeRegistry.Builder.class), "codeRegistry should not be null"); // we need to change __XXX introspection types to have directive extensions if (INTROSPECTION_ELEMENTS.contains(objectType.getName())) { GraphQLObjectType newObjectType = addAppliedDirectives(objectType, codeRegistry, appliedDirectiveType, directiveArgumentType); @@ -197,13 +199,13 @@ private GraphQLObjectType addAppliedDirectives(GraphQLObjectType originalType, G return assertShouldNeverHappen("What directive containing element have we not considered? - %s", originalType); }; DataFetcher argsDF = env -> { - final GraphQLAppliedDirective directive = env.getSource(); + final GraphQLAppliedDirective directive = assertNotNull(env.getSource()); // we only show directive arguments that have values set on them return ImmutableKit.filter(directive.getArguments(), arg -> arg.getArgumentValue().isSet()); }; DataFetcher argValueDF = env -> { - final GraphQLAppliedDirectiveArgument argument = env.getSource(); + final GraphQLAppliedDirectiveArgument argument = assertNotNull(env.getSource()); InputValueWithState value = argument.getArgumentValue(); Node literal = ValuesResolver.valueToLiteral(value, argument.getType(), env.getGraphQlContext(), env.getLocale()); return AstPrinter.printAst(literal); @@ -224,7 +226,7 @@ private GraphQLObjectType addAppliedDirectives(GraphQLObjectType originalType, G return objectType; } - private List filterDirectives(GraphQLSchema schema, boolean isDefinedDirective, GraphQLDirectiveContainer container, List directives) { + private List filterDirectives(GraphQLSchema schema, boolean isDefinedDirective, @Nullable GraphQLDirectiveContainer container, List directives) { return ImmutableKit.filter(directives, directive -> { DirectivePredicateEnvironment env = buildDirectivePredicateEnv(schema, isDefinedDirective, container, directive.getName()); @@ -232,7 +234,7 @@ private List filterDirectives(GraphQLSchema schema, boolean is }); } - private List filterAppliedDirectives(GraphQLSchema schema, boolean isDefinedDirective, GraphQLDirectiveContainer container, List directives) { + private List filterAppliedDirectives(GraphQLSchema schema, boolean isDefinedDirective, @Nullable GraphQLDirectiveContainer container, List directives) { return ImmutableKit.filter(directives, directive -> { DirectivePredicateEnvironment env = buildDirectivePredicateEnv(schema, isDefinedDirective, container, directive.getName()); @@ -240,11 +242,10 @@ private List filterAppliedDirectives(GraphQLSchema sche }); } - @NonNull - private DirectivePredicateEnvironment buildDirectivePredicateEnv(GraphQLSchema schema, boolean isDefinedDirective, GraphQLDirectiveContainer container, String directiveName) { + private DirectivePredicateEnvironment buildDirectivePredicateEnv(GraphQLSchema schema, boolean isDefinedDirective, @Nullable GraphQLDirectiveContainer container, String directiveName) { return new DirectivePredicateEnvironment() { @Override - public GraphQLDirectiveContainer getDirectiveContainer() { + public @Nullable GraphQLDirectiveContainer getDirectiveContainer() { return container; } @@ -269,6 +270,7 @@ public GraphQLSchema getSchema() { * The parameter environment on a call to {@link DirectivePredicate} */ @PublicApi + @NullMarked public interface DirectivePredicateEnvironment { /** @@ -277,7 +279,7 @@ public interface DirectivePredicateEnvironment { * * @return the schema element that contained this directive. */ - GraphQLDirectiveContainer getDirectiveContainer(); + @Nullable GraphQLDirectiveContainer getDirectiveContainer(); /** * @return the directive to be included diff --git a/src/main/java/graphql/language/AstSignature.java b/src/main/java/graphql/language/AstSignature.java index 3736655b55..9b468a51ed 100644 --- a/src/main/java/graphql/language/AstSignature.java +++ b/src/main/java/graphql/language/AstSignature.java @@ -1068,7 +1068,7 @@ private boolean isThisOperation(OperationDefinition operationDefinition, @Nullab private Document transformDoc(Document document, NodeVisitorStub visitor) { AstTransformer astTransformer = new AstTransformer(); - Node newDoc = astTransformer.transform(document, visitor); + Node newDoc = assertNotNull(astTransformer.transform(document, visitor)); return (Document) newDoc; } diff --git a/src/main/java/graphql/language/AstSorter.java b/src/main/java/graphql/language/AstSorter.java index 48a4b7efec..1b8936ee0f 100644 --- a/src/main/java/graphql/language/AstSorter.java +++ b/src/main/java/graphql/language/AstSorter.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.function.Function; +import static graphql.Assert.assertNotNull; import static graphql.util.TreeTransformerUtil.changeNode; import static java.util.Comparator.naturalOrder; import static java.util.Comparator.nullsLast; @@ -225,7 +226,7 @@ public TraversalControl visitDirectiveDefinition(DirectiveDefinition node, Trave }; AstTransformer astTransformer = new AstTransformer(); - Node newDoc = astTransformer.transform(nodeToBeSorted, visitor); + Node newDoc = assertNotNull(astTransformer.transform(nodeToBeSorted, visitor)); //noinspection unchecked return (T) newDoc; } diff --git a/src/main/java/graphql/language/AstTransformer.java b/src/main/java/graphql/language/AstTransformer.java index b2d48acd1a..7c6eaa7e32 100644 --- a/src/main/java/graphql/language/AstTransformer.java +++ b/src/main/java/graphql/language/AstTransformer.java @@ -28,9 +28,9 @@ public class AstTransformer { * Transforms the input tree using the Visitor Pattern. * @param root the root node of the input tree. * @param nodeVisitor the visitor which will transform the input tree. - * @return the transformed tree. + * @return the transformed tree, or null if the root node was deleted. */ - public Node transform(Node root, NodeVisitor nodeVisitor) { + public @Nullable Node transform(Node root, NodeVisitor nodeVisitor) { assertNotNull(root); assertNotNull(nodeVisitor); @@ -47,15 +47,17 @@ public Node transform(Node root, NodeVisitor nodeVisitor) { * object to your visitor by adding it to this map such that such that the key * is the class of the object, and the value is the object itself. The object * can be retrieved within the visitor by calling context.getVarFromParents(). - * @return the transformed tree. + * @return the transformed tree, or null if the root node was deleted. */ - public Node transform(Node root, NodeVisitor nodeVisitor, @Nullable Map, Object> rootVars) { + public @Nullable Node transform(Node root, NodeVisitor nodeVisitor, @Nullable Map, Object> rootVars) { assertNotNull(root); assertNotNull(nodeVisitor); TraverserVisitor traverserVisitor = getNodeTraverserVisitor(nodeVisitor); TreeTransformer treeTransformer = new TreeTransformer<>(AST_NODE_ADAPTER); - return treeTransformer.transform(root, traverserVisitor, rootVars); + return rootVars == null + ? treeTransformer.transform(root, traverserVisitor) + : treeTransformer.transform(root, traverserVisitor, rootVars); } public Node transformParallel(Node root, NodeVisitor nodeVisitor) { @@ -69,7 +71,7 @@ public Node transformParallel(Node root, NodeVisitor nodeVisitor, ForkJoinPool f TraverserVisitor traverserVisitor = new TraverserVisitorStub() { @Override public TraversalControl enter(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } }; @@ -82,7 +84,7 @@ private TraverserVisitor getNodeTraverserVisitor(NodeVisitor nodeVisitor) return new TraverserVisitor() { @Override public TraversalControl enter(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } @Override diff --git a/src/main/java/graphql/language/NodeTraverser.java b/src/main/java/graphql/language/NodeTraverser.java index a0f6ba8063..325de90f65 100644 --- a/src/main/java/graphql/language/NodeTraverser.java +++ b/src/main/java/graphql/language/NodeTraverser.java @@ -15,6 +15,8 @@ import java.util.Map; import java.util.function.Function; +import static graphql.Assert.assertNotNull; + /** * Lets you traverse a {@link Node} tree. */ @@ -61,12 +63,12 @@ public Object depthFirst(NodeVisitor nodeVisitor, Collection roo @Override public TraversalControl enter(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } @Override public TraversalControl leave(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } }; return doTraverse(roots, nodeTraverserVisitor); @@ -97,7 +99,7 @@ public Object preOrder(NodeVisitor nodeVisitor, Collection roots @Override public TraversalControl enter(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } @Override @@ -139,7 +141,7 @@ public TraversalControl enter(TraverserContext context) { @Override public TraversalControl leave(TraverserContext context) { - return context.thisNode().accept(context, nodeVisitor); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, nodeVisitor); } }; diff --git a/src/main/java/graphql/language/ScalarTypeDefinition.java b/src/main/java/graphql/language/ScalarTypeDefinition.java index 3374b69dab..f781187aa0 100644 --- a/src/main/java/graphql/language/ScalarTypeDefinition.java +++ b/src/main/java/graphql/language/ScalarTypeDefinition.java @@ -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; @@ -20,6 +23,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class ScalarTypeDefinition extends AbstractDescribedNode implements TypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -30,8 +34,8 @@ public class ScalarTypeDefinition extends AbstractDescribedNode directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -94,7 +98,7 @@ public ScalarTypeDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -109,7 +113,7 @@ public boolean isEqualTo(Node o) { @Override public ScalarTypeDefinition deepCopy() { - return new ScalarTypeDefinition(name, deepCopy(directives.getDirectives()), description, getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); + return new ScalarTypeDefinition(name, assertNotNull(deepCopy(directives.getDirectives()), "directives deepCopy should not return null"), description, getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @Override @@ -135,6 +139,7 @@ public ScalarTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/ScalarTypeExtensionDefinition.java b/src/main/java/graphql/language/ScalarTypeExtensionDefinition.java index 0feb48451b..5e1578769d 100644 --- a/src/main/java/graphql/language/ScalarTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/ScalarTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,13 +17,14 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class ScalarTypeExtensionDefinition extends ScalarTypeDefinition implements SDLExtensionDefinition { @Internal protected ScalarTypeExtensionDefinition(String name, List directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(name, directives, description, sourceLocation, comments, ignoredChars, additionalData); @@ -28,7 +32,7 @@ protected ScalarTypeExtensionDefinition(String name, @Override public ScalarTypeExtensionDefinition deepCopy() { - return new ScalarTypeExtensionDefinition(getName(), deepCopy(getDirectives()), getDescription(), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); + return new ScalarTypeExtensionDefinition(getName(), assertNotNull(deepCopy(getDirectives()), "directives deepCopy should not return null"), getDescription(), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @Override @@ -57,6 +61,7 @@ public ScalarTypeExtensionDefinition transformExtension(Consumer builde return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/SchemaDefinition.java b/src/main/java/graphql/language/SchemaDefinition.java index 931ef33c41..bc0bc8a14f 100644 --- a/src/main/java/graphql/language/SchemaDefinition.java +++ b/src/main/java/graphql/language/SchemaDefinition.java @@ -8,6 +8,9 @@ import graphql.util.FpKit; 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; @@ -19,6 +22,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class SchemaDefinition extends AbstractDescribedNode implements SDLDefinition, DirectivesContainer { private final NodeUtil.DirectivesHolder directives; @@ -31,11 +35,11 @@ public class SchemaDefinition extends AbstractDescribedNode im @Internal protected SchemaDefinition(List directives, List operationTypeDefinitions, - SourceLocation sourceLocation, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData, - Description description) { + @Nullable Description description) { super(sourceLocation, comments, ignoredChars, additionalData, description); this.directives = NodeUtil.DirectivesHolder.of(directives); this.operationTypeDefinitions = ImmutableList.copyOf(operationTypeDefinitions); @@ -65,10 +69,6 @@ public List getOperationTypeDefinitions() { return operationTypeDefinitions; } - public Description getDescription() { - return description; - } - @Override public List getChildren() { return FpKit.concat(directives.getDirectives(), operationTypeDefinitions); @@ -91,7 +91,7 @@ public SchemaDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -103,7 +103,7 @@ public boolean isEqualTo(Node o) { @Override public SchemaDefinition deepCopy() { - return new SchemaDefinition(deepCopy(directives.getDirectives()), deepCopy(operationTypeDefinitions), getSourceLocation(), getComments(), + return new SchemaDefinition(assertNotNull(deepCopy(directives.getDirectives()), "directives deepCopy should not return null"), assertNotNull(deepCopy(operationTypeDefinitions), "operationTypeDefinitions deepCopy should not return null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData(), description); } @@ -130,6 +130,7 @@ public static Builder newSchemaDefinition() { return new Builder(); } + @NullUnmarked public static final class Builder implements NodeBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/SchemaExtensionDefinition.java b/src/main/java/graphql/language/SchemaExtensionDefinition.java index 43927f5b92..5a926ef8ac 100644 --- a/src/main/java/graphql/language/SchemaExtensionDefinition.java +++ b/src/main/java/graphql/language/SchemaExtensionDefinition.java @@ -3,6 +3,9 @@ import com.google.common.collect.ImmutableList; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -13,11 +16,12 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class SchemaExtensionDefinition extends SchemaDefinition implements SDLExtensionDefinition { protected SchemaExtensionDefinition(List directives, List operationTypeDefinitions, - SourceLocation sourceLocation, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -34,7 +38,7 @@ public SchemaExtensionDefinition withNewChildren(NodeChildrenContainer newChildr @Override public SchemaExtensionDefinition deepCopy() { - return new SchemaExtensionDefinition(deepCopy(getDirectives()), deepCopy(getOperationTypeDefinitions()), getSourceLocation(), getComments(), + return new SchemaExtensionDefinition(assertNotNull(deepCopy(getDirectives()), "directives deepCopy should not return null"), assertNotNull(deepCopy(getOperationTypeDefinitions()), "operationTypeDefinitions deepCopy should not return null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @@ -56,6 +60,7 @@ public static Builder newSchemaExtensionDefinition() { return new Builder(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/Selection.java b/src/main/java/graphql/language/Selection.java index 7160ccfafe..db2dea5d01 100644 --- a/src/main/java/graphql/language/Selection.java +++ b/src/main/java/graphql/language/Selection.java @@ -2,7 +2,9 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; @PublicApi +@NullMarked public interface Selection> extends Node { } diff --git a/src/main/java/graphql/language/SelectionSet.java b/src/main/java/graphql/language/SelectionSet.java index 8e85bdcdef..87c9242d5e 100644 --- a/src/main/java/graphql/language/SelectionSet.java +++ b/src/main/java/graphql/language/SelectionSet.java @@ -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.Collection; import java.util.LinkedHashMap; @@ -20,6 +23,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class SelectionSet extends AbstractNode { private final ImmutableList selections; @@ -27,7 +31,7 @@ public class SelectionSet extends AbstractNode { public static final String CHILD_SELECTIONS = "selections"; @Internal - protected SelectionSet(Collection selections, SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { + protected SelectionSet(Collection selections, @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(sourceLocation, comments, ignoredChars, additionalData); this.selections = ImmutableList.copyOf(selections); } @@ -79,7 +83,7 @@ public SelectionSet withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -92,7 +96,7 @@ public boolean isEqualTo(Node o) { @Override public SelectionSet deepCopy() { - return new SelectionSet(deepCopy(selections), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); + return new SelectionSet(assertNotNull(deepCopy(selections), "selections deepCopy should not return null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @Override @@ -121,6 +125,7 @@ public SelectionSet transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeBuilder { private ImmutableList selections = emptyList(); diff --git a/src/main/java/graphql/language/SelectionSetContainer.java b/src/main/java/graphql/language/SelectionSetContainer.java index d3df7af419..b0e24d3a5d 100644 --- a/src/main/java/graphql/language/SelectionSetContainer.java +++ b/src/main/java/graphql/language/SelectionSetContainer.java @@ -2,8 +2,11 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @PublicApi +@NullMarked public interface SelectionSetContainer extends Node { - SelectionSet getSelectionSet(); + @Nullable SelectionSet getSelectionSet(); } diff --git a/src/main/java/graphql/language/TypeKind.java b/src/main/java/graphql/language/TypeKind.java index 2af70b18f2..8d96e20c55 100644 --- a/src/main/java/graphql/language/TypeKind.java +++ b/src/main/java/graphql/language/TypeKind.java @@ -2,11 +2,13 @@ import graphql.Assert; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * And enumeration of the the kind of things that can be in a graphql type system */ @PublicApi +@NullMarked public enum TypeKind { Operation, Object, Interface, Union, Enum, Scalar, InputObject; diff --git a/src/main/java/graphql/language/TypeName.java b/src/main/java/graphql/language/TypeName.java index add06add41..fcddef8d4c 100644 --- a/src/main/java/graphql/language/TypeName.java +++ b/src/main/java/graphql/language/TypeName.java @@ -6,6 +6,9 @@ import graphql.PublicApi; 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; @@ -20,12 +23,13 @@ import static graphql.language.NodeUtil.assertNewChildrenAreEmpty; @PublicApi +@NullMarked public class TypeName extends AbstractNode implements Type, NamedNode { private final String name; @Internal - protected TypeName(String name, SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { + protected TypeName(String name, @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(sourceLocation, comments, ignoredChars, additionalData); this.name = name; } @@ -61,7 +65,7 @@ public TypeName withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -106,6 +110,7 @@ public TypeName transform(Consumer builderConsumer) { } + @NullUnmarked public static final class Builder implements NodeBuilder { private String name; private SourceLocation sourceLocation; diff --git a/src/main/java/graphql/language/UnionTypeDefinition.java b/src/main/java/graphql/language/UnionTypeDefinition.java index 9af502db89..9486b4a7cb 100644 --- a/src/main/java/graphql/language/UnionTypeDefinition.java +++ b/src/main/java/graphql/language/UnionTypeDefinition.java @@ -8,6 +8,9 @@ import graphql.util.FpKit; 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; @@ -21,6 +24,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class UnionTypeDefinition extends AbstractDescribedNode implements TypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -34,8 +38,8 @@ public class UnionTypeDefinition extends AbstractDescribedNode directives, List memberTypes, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(sourceLocation, comments, ignoredChars, additionalData, description); @@ -115,7 +119,7 @@ public UnionTypeDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -131,8 +135,8 @@ public boolean isEqualTo(Node o) { @Override public UnionTypeDefinition deepCopy() { return new UnionTypeDefinition(name, - deepCopy(directives.getDirectives()), - deepCopy(memberTypes), + assertNotNull(deepCopy(directives.getDirectives()), "directives deepCopy should not return null"), + assertNotNull(deepCopy(memberTypes), "memberTypes deepCopy should not return null"), description, getSourceLocation(), getComments(), @@ -164,6 +168,7 @@ public UnionTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/UnionTypeExtensionDefinition.java b/src/main/java/graphql/language/UnionTypeExtensionDefinition.java index 81ea751792..3a63ecb4c7 100644 --- a/src/main/java/graphql/language/UnionTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/UnionTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,14 +17,15 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class UnionTypeExtensionDefinition extends UnionTypeDefinition implements SDLExtensionDefinition { @Internal protected UnionTypeExtensionDefinition(String name, List directives, List memberTypes, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -38,8 +42,8 @@ protected UnionTypeExtensionDefinition(String name, @Override public UnionTypeExtensionDefinition deepCopy() { return new UnionTypeExtensionDefinition(getName(), - deepCopy(getDirectives()), - deepCopy(getMemberTypes()), + assertNotNull(deepCopy(getDirectives()), "directives deepCopy should not return null"), + assertNotNull(deepCopy(getMemberTypes()), "memberTypes deepCopy should not return null"), getDescription(), getSourceLocation(), getComments(), @@ -73,6 +77,7 @@ public UnionTypeExtensionDefinition transformExtension(Consumer builder return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/VariableDefinition.java b/src/main/java/graphql/language/VariableDefinition.java index ce913e17d1..1bf8d99319 100644 --- a/src/main/java/graphql/language/VariableDefinition.java +++ b/src/main/java/graphql/language/VariableDefinition.java @@ -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.ArrayList; import java.util.LinkedHashMap; @@ -21,11 +24,12 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class VariableDefinition extends AbstractDescribedNode implements DirectivesContainer, NamedNode { private final String name; private final Type type; - private final Value defaultValue; + private final @Nullable Value defaultValue; private final NodeUtil.DirectivesHolder directives; public static final String CHILD_TYPE = "type"; @@ -35,10 +39,10 @@ public class VariableDefinition extends AbstractDescribedNode directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -58,7 +62,7 @@ protected VariableDefinition(String name, */ public VariableDefinition(String name, Type type, - Value defaultValue) { + @Nullable Value defaultValue) { this(name, type, defaultValue, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } @@ -73,7 +77,7 @@ public VariableDefinition(String name, this(name, type, null, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } - public Value getDefaultValue() { + public @Nullable Value getDefaultValue() { return defaultValue; } @@ -135,7 +139,7 @@ public VariableDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -152,9 +156,9 @@ public boolean isEqualTo(Node o) { @Override public VariableDefinition deepCopy() { return new VariableDefinition(name, - deepCopy(type), + assertNotNull(deepCopy(type), "type deepCopy should not return null"), deepCopy(defaultValue), - deepCopy(directives.getDirectives()), + assertNotNull(deepCopy(directives.getDirectives()), "directives deepCopy should not return null"), description, getSourceLocation(), getComments(), @@ -200,6 +204,7 @@ public VariableDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private String name; diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedField.java b/src/main/java/graphql/normalized/ExecutableNormalizedField.java index 69d88ba1be..56f57e8929 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedField.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedField.java @@ -20,6 +20,8 @@ import graphql.util.FpKit; import graphql.util.MutableRef; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -50,8 +52,9 @@ */ @PublicApi @Mutable +@NullMarked public class ExecutableNormalizedField { - private final String alias; + private final @Nullable String alias; private final ImmutableMap normalizedArguments; private final LinkedHashMap resolvedArguments; private final ImmutableList astArguments; @@ -59,7 +62,7 @@ public class ExecutableNormalizedField { // Mutable List on purpose: it is modified after creation private final LinkedHashSet objectTypeNames; private final ArrayList children; - private ExecutableNormalizedField parent; + private @Nullable ExecutableNormalizedField parent; private final String fieldName; private final int level; @@ -136,13 +139,13 @@ private ExecutableNormalizedField(Builder builder) { * * @return true if the field is conditional */ - public boolean isConditional(@NonNull GraphQLSchema schema) { + public boolean isConditional(GraphQLSchema schema) { if (parent == null) { return false; } for (GraphQLInterfaceType commonParentOutputInterface : parent.getInterfacesCommonToAllOutputTypes(schema)) { - List implementations = schema.getImplementations(commonParentOutputInterface); + List implementations = assertNotNull(schema.getImplementations(commonParentOutputInterface), "implementations should not be null"); // __typename if (fieldName.equals(Introspection.TypeNameMetaFieldDef.getName()) && implementations.size() == objectTypeNames.size()) { return false; @@ -231,7 +234,7 @@ private GraphQLFieldDefinition getOneFieldDefinition(GraphQLSchema schema) { return assertNotNull(fieldVisibility.getFieldDefinition(type, fieldName), "No field %s found for type %s", fieldName, objectTypeName); } - private static GraphQLFieldDefinition resolveIntrospectionField(GraphQLSchema schema, Set objectTypeNames, String fieldName) { + private static @Nullable GraphQLFieldDefinition resolveIntrospectionField(GraphQLSchema schema, Set objectTypeNames, String fieldName) { if (fieldName.equals(schema.getIntrospectionTypenameFieldDefinition().getName())) { return schema.getIntrospectionTypenameFieldDefinition(); } else if (objectTypeNames.size() == 1 && objectTypeNames.iterator().next().equals(schema.getQueryType().getName())) { @@ -320,7 +323,7 @@ public String getResultKey() { * @see #getResultKey() * @see #getName() */ - public String getAlias() { + public @Nullable String getAlias() { return alias; } @@ -338,7 +341,7 @@ public ImmutableList getAstArguments() { * * @return an argument value */ - public NormalizedInputValue getNormalizedArgument(String name) { + public @Nullable NormalizedInputValue getNormalizedArgument(String name) { return normalizedArguments.get(name); } @@ -476,7 +479,7 @@ public int getLevel() { /** * @return the parent of this {@link ExecutableNormalizedField} or null if it's a top level field */ - public ExecutableNormalizedField getParent() { + public @Nullable ExecutableNormalizedField getParent() { return parent; } @@ -606,6 +609,7 @@ public ExecutableNormalizedField transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static class Builder { private LinkedHashSet objectTypeNames = new LinkedHashSet<>(); private String fieldName; diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperation.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperation.java index 2200d02777..41ba065060 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperation.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperation.java @@ -12,6 +12,8 @@ import graphql.language.OperationDefinition; import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLFieldsContainer; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Map; @@ -25,10 +27,11 @@ * An operation consists of a list of {@link ExecutableNormalizedField}s in a parent child hierarchy */ @PublicApi +@NullMarked public class ExecutableNormalizedOperation { private final OperationDefinition.Operation operation; + private final @Nullable String operationName; private final Map> operationDirectives; - private final String operationName; private final List topLevelFields; private final ImmutableListMultimap fieldToNormalizedField; private final Map normalizedFieldToMergedField; @@ -39,7 +42,7 @@ public class ExecutableNormalizedOperation { public ExecutableNormalizedOperation( OperationDefinition.Operation operation, - String operationName, + @Nullable String operationName, Map> operationDirectives, List topLevelFields, ImmutableListMultimap fieldToNormalizedField, @@ -70,7 +73,7 @@ public OperationDefinition.Operation getOperation() { /** * @return the operation name, which can be null */ - public String getOperationName() { + public @Nullable String getOperationName() { return operationName; } @@ -152,7 +155,7 @@ public Map getNormalizedFieldToMergedFie * * @return a {@link MergedField} or null if its not present */ - public MergedField getMergedField(ExecutableNormalizedField executableNormalizedField) { + public @Nullable MergedField getMergedField(ExecutableNormalizedField executableNormalizedField) { return normalizedFieldToMergedField.get(executableNormalizedField); } @@ -171,7 +174,7 @@ public Map getNormalizedFieldToQuery * * @return the fields query directives or null */ - public QueryDirectives getQueryDirectives(ExecutableNormalizedField executableNormalizedField) { + public @Nullable QueryDirectives getQueryDirectives(ExecutableNormalizedField executableNormalizedField) { return normalizedFieldToQueryDirectives.get(executableNormalizedField); } diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java index 8050f5154c..1c25cdadfe 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java @@ -45,6 +45,7 @@ import graphql.schema.GraphQLUnionType; import graphql.schema.GraphQLUnmodifiedType; import graphql.schema.impl.SchemaUtil; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -68,6 +69,7 @@ import static graphql.util.FpKit.filterSet; import static graphql.util.FpKit.groupingBy; import static graphql.util.FpKit.intersection; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toCollection; @@ -78,6 +80,7 @@ * during a given graphql operation. */ @PublicApi +@NullMarked public class ExecutableNormalizedOperationFactory { public static class Options { @@ -487,8 +490,9 @@ private ExecutableNormalizedOperation createNormalizedQueryImpl() { buildEnfsRecursively(null, null, 0); for (PossibleMerger possibleMerger : possibleMergerList) { - List childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey); - ENFMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema, options.deferSupport); + ExecutableNormalizedField mergerParent = assertNotNull(possibleMerger.parent, "possibleMerger.parent should not be null"); + List childrenWithSameResultKey = mergerParent.getChildrenWithSameResultKey(possibleMerger.resultKey); + ENFMerger.merge(mergerParent, childrenWithSameResultKey, graphQLSchema, options.deferSupport); } Map> operationDirectives = directivesResolver.resolveDirectivesByName(operationDefinition, @@ -546,7 +550,7 @@ private void buildEnfsRecursively(@Nullable ExecutableNormalizedField executable return; } collectedFields = new ArrayList<>(); - for (CollectedField fieldAndAstParent : fieldAndAstParents) { + for (CollectedField fieldAndAstParent : assertNotNull(fieldAndAstParents, "fieldAndAstParents should not be null")) { if (fieldAndAstParent.field.getSelectionSet() == null) { continue; } @@ -633,7 +637,7 @@ private void createNFs(ImmutableList.Builder nfListBu Map> fieldsByName, ImmutableListMultimap.Builder normalizedFieldToAstFields, int level, - ExecutableNormalizedField parent) { + @Nullable ExecutableNormalizedField parent) { for (String resultKey : fieldsByName.keySet()) { List fieldsWithSameResultKey = fieldsByName.get(resultKey); List commonParentsGroups = groupByCommonParents(fieldsWithSameResultKey); @@ -660,7 +664,7 @@ private void createNFs(ImmutableList.Builder nfListBu // new single ENF private ExecutableNormalizedField createNF(CollectedFieldGroup collectedFieldGroup, int level, - ExecutableNormalizedField parent) { + @Nullable ExecutableNormalizedField parent) { this.fieldCount++; if (this.fieldCount > this.options.getMaxFieldsCount()) { @@ -706,12 +710,12 @@ private List groupByCommonParentsNoDeferSupport(Collection< Set allRelevantObjects = objectTypes.build(); Map> groupByAstParent = groupingBy(fields, fieldAndType -> fieldAndType.astTypeCondition); if (groupByAstParent.size() == 1) { - return singletonList(new CollectedFieldGroup(ImmutableSet.copyOf(fields), allRelevantObjects, null)); + return singletonList(new CollectedFieldGroup(ImmutableSet.copyOf(fields), allRelevantObjects, emptySet())); } ImmutableList.Builder result = ImmutableList.builder(); for (GraphQLObjectType objectType : allRelevantObjects) { Set relevantFields = filterSet(fields, field -> field.objectTypes.contains(objectType)); - result.add(new CollectedFieldGroup(relevantFields, singleton(objectType), null)); + result.add(new CollectedFieldGroup(relevantFields, singleton(objectType), emptySet())); } return result.build(); } @@ -782,7 +786,7 @@ private void collectFromSelectionSet(SelectionSet selectionSet, List result, GraphQLCompositeType astTypeCondition, Set possibleObjects, - NormalizedDeferredExecution deferredExecution + @Nullable NormalizedDeferredExecution deferredExecution ) { for (Selection selection : selectionSet.getSelections()) { if (selection instanceof Field) { @@ -835,7 +839,7 @@ private void collectInlineFragment(List result, GraphQLCompositeType newAstTypeCondition = astTypeCondition; if (inlineFragment.getTypeCondition() != null) { - newAstTypeCondition = (GraphQLCompositeType) this.graphQLSchema.getType(inlineFragment.getTypeCondition().getName()); + newAstTypeCondition = (GraphQLCompositeType) assertNotNull(this.graphQLSchema.getType(inlineFragment.getTypeCondition().getName()), "type should not be null"); newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition); } @@ -866,7 +870,7 @@ private void collectField(List result, Field field, Set possibleObjectTypes, GraphQLCompositeType astTypeCondition, - NormalizedDeferredExecution deferredExecution + @Nullable NormalizedDeferredExecution deferredExecution ) { if (!conditionalNodes.shouldInclude(field, this.coercedVariableValues.toMap(), @@ -920,10 +924,10 @@ private ImmutableSet resolvePossibleObjects(GraphQLCompositeT } private static class PossibleMerger { - ExecutableNormalizedField parent; + @Nullable ExecutableNormalizedField parent; String resultKey; - public PossibleMerger(ExecutableNormalizedField parent, String resultKey) { + public PossibleMerger(@Nullable ExecutableNormalizedField parent, String resultKey) { this.parent = parent; this.resultKey = resultKey; } @@ -933,9 +937,9 @@ private static class CollectedField { Field field; Set objectTypes; GraphQLCompositeType astTypeCondition; - NormalizedDeferredExecution deferredExecution; + @Nullable NormalizedDeferredExecution deferredExecution; - public CollectedField(Field field, Set objectTypes, GraphQLCompositeType astTypeCondition, NormalizedDeferredExecution deferredExecution) { + public CollectedField(Field field, Set objectTypes, GraphQLCompositeType astTypeCondition, @Nullable NormalizedDeferredExecution deferredExecution) { this.field = field; this.objectTypes = objectTypes; this.astTypeCondition = astTypeCondition; diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationToAstCompiler.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationToAstCompiler.java index f71e1a30e8..95cb59f6de 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationToAstCompiler.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationToAstCompiler.java @@ -25,7 +25,7 @@ import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLUnmodifiedType; import graphql.util.LinkedHashMapFactory; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -58,6 +58,7 @@ * has the same schema as the one provided. */ @PublicApi +@NullMarked public class ExecutableNormalizedOperationToAstCompiler { /** @@ -96,10 +97,10 @@ public Map getVariables() { * * @return a {@link CompilerResult} object */ - public static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind, + public static CompilerResult compileToDocument(GraphQLSchema schema, + OperationDefinition.Operation operationKind, @Nullable String operationName, - @NonNull List topLevelFields, + List topLevelFields, @Nullable VariablePredicate variablePredicate) { return compileToDocument(schema, operationKind, operationName, topLevelFields, LinkedHashMapFactory.of(), variablePredicate); } @@ -119,11 +120,11 @@ public static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, * * @return a {@link CompilerResult} object */ - public static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind, + public static CompilerResult compileToDocument(GraphQLSchema schema, + OperationDefinition.Operation operationKind, @Nullable String operationName, - @NonNull List topLevelFields, - @NonNull Map normalizedFieldToQueryDirectives, + List topLevelFields, + Map normalizedFieldToQueryDirectives, @Nullable VariablePredicate variablePredicate) { return compileToDocument(schema, operationKind, operationName, topLevelFields, normalizedFieldToQueryDirectives, variablePredicate, false); } @@ -146,10 +147,10 @@ public static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, * @see ExecutableNormalizedOperationToAstCompiler#compileToDocument(GraphQLSchema, OperationDefinition.Operation, String, List, VariablePredicate) */ @ExperimentalApi - public static CompilerResult compileToDocumentWithDeferSupport(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind, + public static CompilerResult compileToDocumentWithDeferSupport(GraphQLSchema schema, + OperationDefinition.Operation operationKind, @Nullable String operationName, - @NonNull List topLevelFields, + List topLevelFields, @Nullable VariablePredicate variablePredicate ) { return compileToDocumentWithDeferSupport(schema, operationKind, operationName, topLevelFields, LinkedHashMapFactory.of(), variablePredicate); @@ -173,24 +174,24 @@ public static CompilerResult compileToDocumentWithDeferSupport(@NonNull GraphQLS * @see ExecutableNormalizedOperationToAstCompiler#compileToDocument(GraphQLSchema, OperationDefinition.Operation, String, List, Map, VariablePredicate) */ @ExperimentalApi - public static CompilerResult compileToDocumentWithDeferSupport(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind, + public static CompilerResult compileToDocumentWithDeferSupport(GraphQLSchema schema, + OperationDefinition.Operation operationKind, @Nullable String operationName, - @NonNull List topLevelFields, - @NonNull Map normalizedFieldToQueryDirectives, + List topLevelFields, + Map normalizedFieldToQueryDirectives, @Nullable VariablePredicate variablePredicate ) { return compileToDocument(schema, operationKind, operationName, topLevelFields, normalizedFieldToQueryDirectives, variablePredicate, true); } - private static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind, + private static CompilerResult compileToDocument(GraphQLSchema schema, + OperationDefinition.Operation operationKind, @Nullable String operationName, - @NonNull List topLevelFields, - @NonNull Map normalizedFieldToQueryDirectives, + List topLevelFields, + Map normalizedFieldToQueryDirectives, @Nullable VariablePredicate variablePredicate, boolean deferSupport) { - GraphQLObjectType operationType = getOperationType(schema, operationKind); + GraphQLObjectType operationType = Assert.assertNotNull(getOperationType(schema, operationKind), "operationType should not be null"); VariableAccumulator variableAccumulator = new VariableAccumulator(variablePredicate); List> selections = subselectionsForNormalizedField(schema, operationType.getName(), topLevelFields, normalizedFieldToQueryDirectives, variableAccumulator, deferSupport); @@ -212,9 +213,9 @@ private static CompilerResult compileToDocument(@NonNull GraphQLSchema schema, } private static List> subselectionsForNormalizedField(GraphQLSchema schema, - @NonNull String parentOutputType, + String parentOutputType, List executableNormalizedFields, - @NonNull Map normalizedFieldToQueryDirectives, + Map normalizedFieldToQueryDirectives, VariableAccumulator variableAccumulator, boolean deferSupport) { if (deferSupport) { @@ -225,9 +226,9 @@ private static List> subselectionsForNormalizedField(GraphQLSchema } private static List> subselectionsForNormalizedFieldNoDeferSupport(GraphQLSchema schema, - @NonNull String parentOutputType, + String parentOutputType, List executableNormalizedFields, - @NonNull Map normalizedFieldToQueryDirectives, + Map normalizedFieldToQueryDirectives, VariableAccumulator variableAccumulator) { ImmutableList.Builder> selections = ImmutableList.builder(); @@ -261,9 +262,9 @@ private static List> subselectionsForNormalizedFieldNoDeferSupport( private static List> subselectionsForNormalizedFieldWithDeferSupport(GraphQLSchema schema, - @NonNull String parentOutputType, + String parentOutputType, List executableNormalizedFields, - @NonNull Map normalizedFieldToQueryDirectives, + Map normalizedFieldToQueryDirectives, VariableAccumulator variableAccumulator) { ImmutableList.Builder> selections = ImmutableList.builder(); @@ -335,7 +336,7 @@ private static List> subselectionsForNormalizedFieldWithDeferSuppor */ private static Map selectionForNormalizedField(GraphQLSchema schema, ExecutableNormalizedField executableNormalizedField, - @NonNull Map normalizedFieldToQueryDirectives, + Map normalizedFieldToQueryDirectives, VariableAccumulator variableAccumulator, boolean deferSupport) { Map groupedFields = new LinkedHashMap<>(); @@ -353,7 +354,7 @@ private static Map selectionForNormalizedField(GraphQLSchema sche private static Field selectionForNormalizedField(GraphQLSchema schema, String objectTypeName, ExecutableNormalizedField executableNormalizedField, - @NonNull Map normalizedFieldToQueryDirectives, + Map normalizedFieldToQueryDirectives, VariableAccumulator variableAccumulator, boolean deferSupport) { final List> subSelections; @@ -390,7 +391,7 @@ private static Field selectionForNormalizedField(GraphQLSchema schema, .build(); } - private static @NonNull List buildDirectives(ExecutableNormalizedField executableNormalizedField, QueryDirectives queryDirectives, VariableAccumulator variableAccumulator) { + private static List buildDirectives(ExecutableNormalizedField executableNormalizedField, @Nullable QueryDirectives queryDirectives, VariableAccumulator variableAccumulator) { if (queryDirectives == null || queryDirectives.getImmediateAppliedDirectivesByField().isEmpty()) { return emptyList(); } @@ -418,17 +419,16 @@ private static SelectionSet selectionSet(List fields) { } - @NonNull private static GraphQLFieldDefinition getFieldDefinition(GraphQLSchema schema, String parentType, ExecutableNormalizedField nf) { - return Introspection.getFieldDef(schema, (GraphQLCompositeType) schema.getType(parentType), nf.getName()); + return Introspection.getFieldDef(schema, (GraphQLCompositeType) Assert.assertNotNull(schema.getType(parentType), "type '%s' should not be null", parentType), nf.getName()); } @Nullable - private static GraphQLObjectType getOperationType(@NonNull GraphQLSchema schema, - OperationDefinition.@NonNull Operation operationKind) { + private static GraphQLObjectType getOperationType(GraphQLSchema schema, + OperationDefinition.Operation operationKind) { switch (operationKind) { case QUERY: return schema.getQueryType(); @@ -445,10 +445,10 @@ private static GraphQLObjectType getOperationType(@NonNull GraphQLSchema schema, * Represents important execution details that can be associated with a fragment. */ private static class ExecutionFragmentDetails { - private final String typeName; - private final NormalizedDeferredExecution deferredExecution; + private final @Nullable String typeName; + private final @Nullable NormalizedDeferredExecution deferredExecution; - public ExecutionFragmentDetails(String typeName, NormalizedDeferredExecution deferredExecution) { + public ExecutionFragmentDetails(@Nullable String typeName, @Nullable NormalizedDeferredExecution deferredExecution) { this.typeName = typeName; this.deferredExecution = deferredExecution; } diff --git a/src/main/java/graphql/normalized/NormalizedInputValue.java b/src/main/java/graphql/normalized/NormalizedInputValue.java index c6f881fac0..fbedc49954 100644 --- a/src/main/java/graphql/normalized/NormalizedInputValue.java +++ b/src/main/java/graphql/normalized/NormalizedInputValue.java @@ -3,6 +3,8 @@ import graphql.Assert; import graphql.PublicApi; import graphql.language.Value; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Objects; @@ -15,11 +17,12 @@ * An argument value with type information. */ @PublicApi +@NullMarked public class NormalizedInputValue { private final String typeName; - private final Object value; + private final @Nullable Object value; - public NormalizedInputValue(String typeName, Object value) { + public NormalizedInputValue(String typeName, @Nullable Object value) { this.typeName = assertValidTypeName(typeName); this.value = value; } @@ -61,7 +64,7 @@ public String getUnwrappedTypeName() { * * @return the value */ - public Object getValue() { + public @Nullable Object getValue() { return value; } @@ -115,7 +118,7 @@ private String unwrapOne(String typeName) { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/src/main/java/graphql/parser/InvalidSyntaxException.java b/src/main/java/graphql/parser/InvalidSyntaxException.java index 939dfd2e2d..180672a20d 100644 --- a/src/main/java/graphql/parser/InvalidSyntaxException.java +++ b/src/main/java/graphql/parser/InvalidSyntaxException.java @@ -6,6 +6,8 @@ import graphql.InvalidSyntaxError; import graphql.PublicApi; import graphql.language.SourceLocation; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Collections; import java.util.List; @@ -14,15 +16,16 @@ * This exception is thrown by the {@link Parser} if the graphql syntax is not valid */ @PublicApi +@NullMarked public class InvalidSyntaxException extends GraphQLException { - private final String message; - private final String sourcePreview; - private final String offendingToken; - private final SourceLocation location; + private final @Nullable String message; + private final @Nullable String sourcePreview; + private final @Nullable String offendingToken; + private final @Nullable SourceLocation location; @Internal - protected InvalidSyntaxException(String msg, SourceLocation location, String offendingToken, String sourcePreview, Exception cause) { + protected InvalidSyntaxException(@Nullable String msg, @Nullable SourceLocation location, @Nullable String offendingToken, @Nullable String sourcePreview, @Nullable Exception cause) { super(cause); this.message = msg; this.sourcePreview = sourcePreview; @@ -31,24 +34,24 @@ protected InvalidSyntaxException(String msg, SourceLocation location, String off } public InvalidSyntaxError toInvalidSyntaxError() { - List sourceLocations = location == null ? null : Collections.singletonList(location); + @Nullable List sourceLocations = location == null ? null : Collections.singletonList(location); return new InvalidSyntaxError(sourceLocations, message, sourcePreview, offendingToken); } @Override - public String getMessage() { + public @Nullable String getMessage() { return message; } - public SourceLocation getLocation() { + public @Nullable SourceLocation getLocation() { return location; } - public String getSourcePreview() { + public @Nullable String getSourcePreview() { return sourcePreview; } - public String getOffendingToken() { + public @Nullable String getOffendingToken() { return offendingToken; } diff --git a/src/main/java/graphql/parser/MultiSourceReader.java b/src/main/java/graphql/parser/MultiSourceReader.java index fad54b2fa0..9b0b2d02f2 100644 --- a/src/main/java/graphql/parser/MultiSourceReader.java +++ b/src/main/java/graphql/parser/MultiSourceReader.java @@ -3,6 +3,9 @@ import graphql.Assert; import graphql.PublicApi; import graphql.util.LockKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.io.LineNumberReader; @@ -21,6 +24,7 @@ * place at some point in time. */ @PublicApi +@NullMarked public class MultiSourceReader extends Reader { // In Java version 16+, LineNumberReader.read considers end-of-stream to be a line terminator @@ -99,10 +103,10 @@ private int calcLineNumber() { } public static class SourceAndLine { - private String sourceName = null; + private @Nullable String sourceName = null; private int line = 0; - public String getSourceName() { + public @Nullable String getSourceName() { return sourceName; } @@ -191,7 +195,7 @@ public int getLineNumber() { /** * @return The name of the current source */ - public String getSourceName() { + public @Nullable String getSourceName() { return readerLock.callLocked(() -> { if (sourceParts.isEmpty()) { return null; @@ -241,6 +245,7 @@ public void close() throws IOException { } } + @NullUnmarked private static class SourcePart { String sourceName; LineNumberReader lineReader; @@ -270,6 +275,7 @@ public static Builder newMultiSourceReader() { return new Builder(); } + @NullUnmarked public static class Builder { List sourceParts = new ArrayList<>(); boolean trackData = true; diff --git a/src/main/java/graphql/parser/Parser.java b/src/main/java/graphql/parser/Parser.java index c2015f274f..59f6e368df 100644 --- a/src/main/java/graphql/parser/Parser.java +++ b/src/main/java/graphql/parser/Parser.java @@ -26,7 +26,8 @@ import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.tree.ParseTreeListener; import org.antlr.v4.runtime.tree.TerminalNode; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.io.Reader; @@ -54,6 +55,7 @@ * @see graphql.language.IgnoredChar */ @PublicApi +@NullMarked public class Parser { @Internal @@ -299,7 +301,6 @@ private static MultiSourceReader setupMultiSourceReader(ParserEnvironment enviro return multiSourceReader; } - @NonNull private static SafeTokenReader setupSafeTokenReader(ParserEnvironment environment, ParserOptions parserOptions, MultiSourceReader multiSourceReader) { int maxCharacters = parserOptions.getMaxCharacters(); Consumer onTooManyCharacters = it -> { @@ -308,7 +309,6 @@ private static SafeTokenReader setupSafeTokenReader(ParserEnvironment environmen return new SafeTokenReader(multiSourceReader, maxCharacters, onTooManyCharacters); } - @NonNull private static CodePointCharStream setupCharStream(SafeTokenReader safeTokenReader) { CodePointCharStream charStream; try { @@ -319,7 +319,6 @@ private static CodePointCharStream setupCharStream(SafeTokenReader safeTokenRead return charStream; } - @NonNull private static GraphqlLexer setupGraphqlLexer(ParserEnvironment environment, MultiSourceReader multiSourceReader, CodePointCharStream charStream) { GraphqlLexer lexer = new GraphqlLexer(charStream); lexer.removeErrorListeners(); @@ -330,7 +329,8 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int String preview = AntlrHelper.createPreview(multiSourceReader, line); String msgKey; List args; - if (antlerMsg == null || environment.getParserOptions().isRedactTokenParserErrorMessages()) { + ParserOptions opts = environment.getParserOptions(); + if (antlerMsg == null || (opts != null && opts.isRedactTokenParserErrorMessages())) { msgKey = "InvalidSyntax.noMessage"; args = ImmutableList.of(sourceLocation.getLine(), sourceLocation.getColumn()); } else { @@ -344,7 +344,6 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int return lexer; } - @NonNull private SafeTokenSource getSafeTokenSource(ParserEnvironment environment, ParserOptions parserOptions, MultiSourceReader multiSourceReader, GraphqlLexer lexer) { int maxTokens = parserOptions.getMaxTokens(); int maxWhitespaceTokens = parserOptions.getMaxWhitespaceTokens(); @@ -393,7 +392,7 @@ public void visitTerminal(TerminalNode node) { final Token token = node.getSymbol(); parsingListener.onToken(new ParsingListener.Token() { @Override - public String getText() { + public @Nullable String getText() { return token == null ? null : token.getText(); } @@ -423,10 +422,10 @@ public int getCharPositionInLine() { parser.addParseListener(listener); } - private void throwIfTokenProblems(ParserEnvironment environment, Token token, int maxLimit, MultiSourceReader multiSourceReader, Class targetException) throws ParseCancelledException { + private void throwIfTokenProblems(ParserEnvironment environment, @Nullable Token token, int maxLimit, MultiSourceReader multiSourceReader, Class targetException) throws ParseCancelledException { String tokenType = "grammar"; - SourceLocation sourceLocation = null; - String offendingToken = null; + @Nullable SourceLocation sourceLocation = null; + @Nullable String offendingToken = null; if (token != null) { int channel = token.getChannel(); tokenType = channel == CHANNEL_WHITESPACE ? "whitespace" : (channel == CHANNEL_COMMENTS ? "comments" : "grammar"); diff --git a/src/main/java/graphql/parser/ParserEnvironment.java b/src/main/java/graphql/parser/ParserEnvironment.java index 7f0ac696ba..7fa26996d0 100644 --- a/src/main/java/graphql/parser/ParserEnvironment.java +++ b/src/main/java/graphql/parser/ParserEnvironment.java @@ -2,6 +2,9 @@ import graphql.PublicApi; import graphql.i18n.I18n; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.io.Reader; import java.io.StringReader; @@ -13,6 +16,7 @@ * This is the arguments that can be passed to a {@link Parser} */ @PublicApi +@NullMarked public interface ParserEnvironment { /** @@ -23,7 +27,7 @@ public interface ParserEnvironment { /** * @return the parsing options */ - ParserOptions getParserOptions(); + @Nullable ParserOptions getParserOptions(); /** * @return the locale to produce parsing error messages in @@ -42,6 +46,7 @@ static Builder newParserEnvironment() { return new Builder(); } + @NullUnmarked class Builder { Reader reader; ParserOptions parserOptions = ParserOptions.getDefaultParserOptions(); diff --git a/src/main/java/graphql/parser/ParserOptions.java b/src/main/java/graphql/parser/ParserOptions.java index 2e04294427..03001b04f2 100644 --- a/src/main/java/graphql/parser/ParserOptions.java +++ b/src/main/java/graphql/parser/ParserOptions.java @@ -1,6 +1,8 @@ package graphql.parser; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import java.util.function.Consumer; @@ -10,6 +12,7 @@ * Options that control how the {@link Parser} behaves. */ @PublicApi +@NullMarked public class ParserOptions { /** * A graphql hacking vector is to send nonsensical queries with large tokens that contain a repeated characters @@ -323,6 +326,7 @@ public static Builder newParserOptions() { return new Builder(); } + @NullUnmarked public static class Builder { private boolean captureIgnoredChars = false; diff --git a/src/main/java/graphql/schema/AsyncDataFetcher.java b/src/main/java/graphql/schema/AsyncDataFetcher.java index a003bc4c7e..5a70ea808d 100644 --- a/src/main/java/graphql/schema/AsyncDataFetcher.java +++ b/src/main/java/graphql/schema/AsyncDataFetcher.java @@ -1,6 +1,7 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -12,6 +13,7 @@ * A modifier type that indicates the underlying data fetcher is run asynchronously */ @PublicApi +@NullMarked public class AsyncDataFetcher implements DataFetcher> { /** diff --git a/src/main/java/graphql/schema/CoercingParseLiteralException.java b/src/main/java/graphql/schema/CoercingParseLiteralException.java index 73f562a62a..551252d313 100644 --- a/src/main/java/graphql/schema/CoercingParseLiteralException.java +++ b/src/main/java/graphql/schema/CoercingParseLiteralException.java @@ -4,8 +4,12 @@ import graphql.GraphqlErrorException; import graphql.PublicApi; import graphql.language.SourceLocation; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; @PublicApi +@NullMarked public class CoercingParseLiteralException extends GraphqlErrorException { public CoercingParseLiteralException() { @@ -20,7 +24,7 @@ public CoercingParseLiteralException(String message, Throwable cause) { this(newCoercingParseLiteralException().message(message).cause(cause)); } - public CoercingParseLiteralException(String message, Throwable cause, SourceLocation sourceLocation) { + public CoercingParseLiteralException(String message, @Nullable Throwable cause, @Nullable SourceLocation sourceLocation) { this(newCoercingParseLiteralException().message(message).cause(cause).sourceLocation(sourceLocation)); } @@ -41,6 +45,7 @@ public static Builder newCoercingParseLiteralException() { return new Builder(); } + @NullUnmarked public static class Builder extends BuilderBase { public CoercingParseLiteralException build() { return new CoercingParseLiteralException(this); diff --git a/src/main/java/graphql/schema/CoercingParseValueException.java b/src/main/java/graphql/schema/CoercingParseValueException.java index 3bc93518d3..6f051db3c4 100644 --- a/src/main/java/graphql/schema/CoercingParseValueException.java +++ b/src/main/java/graphql/schema/CoercingParseValueException.java @@ -4,8 +4,12 @@ import graphql.GraphqlErrorException; import graphql.PublicApi; import graphql.language.SourceLocation; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; @PublicApi +@NullMarked public class CoercingParseValueException extends GraphqlErrorException { public CoercingParseValueException() { @@ -24,7 +28,7 @@ public CoercingParseValueException(Throwable cause) { this(newCoercingParseValueException().cause(cause)); } - public CoercingParseValueException(String message, Throwable cause, SourceLocation sourceLocation) { + public CoercingParseValueException(String message, @Nullable Throwable cause, @Nullable SourceLocation sourceLocation) { this(newCoercingParseValueException().message(message).cause(cause).sourceLocation(sourceLocation)); } @@ -41,6 +45,7 @@ public static Builder newCoercingParseValueException() { return new Builder(); } + @NullUnmarked public static class Builder extends BuilderBase { public CoercingParseValueException build() { return new CoercingParseValueException(this); diff --git a/src/main/java/graphql/schema/CoercingSerializeException.java b/src/main/java/graphql/schema/CoercingSerializeException.java index 8de575af17..cfb4e8544d 100644 --- a/src/main/java/graphql/schema/CoercingSerializeException.java +++ b/src/main/java/graphql/schema/CoercingSerializeException.java @@ -4,8 +4,11 @@ import graphql.ErrorType; import graphql.GraphqlErrorException; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; @PublicApi +@NullMarked public class CoercingSerializeException extends GraphqlErrorException { public CoercingSerializeException() { @@ -37,6 +40,7 @@ public static Builder newCoercingSerializeException() { return new Builder(); } + @NullUnmarked public static class Builder extends BuilderBase { public CoercingSerializeException build() { return new CoercingSerializeException(this); diff --git a/src/main/java/graphql/schema/DataFetcherFactories.java b/src/main/java/graphql/schema/DataFetcherFactories.java index 2345535d56..1116aab494 100644 --- a/src/main/java/graphql/schema/DataFetcherFactories.java +++ b/src/main/java/graphql/schema/DataFetcherFactories.java @@ -1,6 +1,7 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; @@ -9,6 +10,7 @@ * A helper for {@link graphql.schema.DataFetcherFactory} */ @PublicApi +@NullMarked public class DataFetcherFactories { /** diff --git a/src/main/java/graphql/schema/DataFetcherFactoryEnvironment.java b/src/main/java/graphql/schema/DataFetcherFactoryEnvironment.java index 7ff5aefb9a..3805adaebe 100644 --- a/src/main/java/graphql/schema/DataFetcherFactoryEnvironment.java +++ b/src/main/java/graphql/schema/DataFetcherFactoryEnvironment.java @@ -1,6 +1,8 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; /** * This is passed to a {@link graphql.schema.DataFetcherFactory} when it is invoked to @@ -10,6 +12,7 @@ * of a {@link GraphQLFieldDefinition} */ @PublicApi +@NullMarked @Deprecated(since = "2024-11-26") public class DataFetcherFactoryEnvironment { private final GraphQLFieldDefinition fieldDefinition; @@ -29,6 +32,7 @@ public static Builder newDataFetchingFactoryEnvironment() { return new Builder(); } + @NullUnmarked public static class Builder { GraphQLFieldDefinition fieldDefinition; diff --git a/src/main/java/graphql/schema/DataFetchingFieldSelectionSet.java b/src/main/java/graphql/schema/DataFetchingFieldSelectionSet.java index 62e7ad8333..17deabfd4d 100644 --- a/src/main/java/graphql/schema/DataFetchingFieldSelectionSet.java +++ b/src/main/java/graphql/schema/DataFetchingFieldSelectionSet.java @@ -1,6 +1,7 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; import java.util.Map; @@ -94,6 +95,7 @@ * by result key, that is by the field alias or by the field name. */ @PublicApi +@NullMarked public interface DataFetchingFieldSelectionSet { /** diff --git a/src/main/java/graphql/schema/DefaultGraphqlTypeComparatorRegistry.java b/src/main/java/graphql/schema/DefaultGraphqlTypeComparatorRegistry.java index 29e7243c8f..93b5ba995c 100644 --- a/src/main/java/graphql/schema/DefaultGraphqlTypeComparatorRegistry.java +++ b/src/main/java/graphql/schema/DefaultGraphqlTypeComparatorRegistry.java @@ -2,6 +2,8 @@ import com.google.common.collect.ImmutableMap; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import java.util.Comparator; import java.util.HashMap; @@ -17,6 +19,7 @@ * Associates a {@code Comparator} with a {@code GraphqlTypeComparatorEnvironment} to control the scope in which the {@code Comparator} can be applied. */ @PublicApi +@NullMarked public class DefaultGraphqlTypeComparatorRegistry implements GraphqlTypeComparatorRegistry { // This sensible order was taken from the original SchemaPrinter code. It ordered the types in this manner @@ -114,6 +117,7 @@ public static Builder newComparators() { return new Builder(); } + @NullUnmarked public static class Builder { private final Map> registry = new HashMap<>(); diff --git a/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java b/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java index 91bc2fe351..18f69e01c8 100644 --- a/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java +++ b/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java @@ -12,7 +12,7 @@ import graphql.language.OperationDefinition; import org.dataloader.DataLoader; import org.dataloader.DataLoaderRegistry; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.List; @@ -27,6 +27,7 @@ */ @SuppressWarnings("TypeParameterUnusedInFormals") @PublicApi +@NullMarked public class DelegatingDataFetchingEnvironment implements DataFetchingEnvironment { protected final DataFetchingEnvironment delegateEnvironment; @@ -41,7 +42,7 @@ public DelegatingDataFetchingEnvironment(DataFetchingEnvironment delegateEnviron } @Override - public T getSource() { + public @Nullable T getSource() { return delegateEnvironment.getSource(); } @@ -56,7 +57,7 @@ public boolean containsArgument(String name) { } @Override - public T getArgument(String name) { + public @Nullable T getArgument(String name) { return delegateEnvironment.getArgument(name); } @@ -67,12 +68,12 @@ public T getArgumentOrDefault(String name, T defaultValue) { @Deprecated(since = "2022-04-17") @Override - public T getContext() { + public @Nullable T getContext() { return delegateEnvironment.getContext(); } @Override - public @NonNull GraphQLContext getGraphQlContext() { + public GraphQLContext getGraphQlContext() { return delegateEnvironment.getGraphQlContext(); } @@ -82,7 +83,7 @@ public T getContext() { } @Override - public T getRoot() { + public @Nullable T getRoot() { return delegateEnvironment.getRoot(); } diff --git a/src/main/java/graphql/schema/FieldCoordinates.java b/src/main/java/graphql/schema/FieldCoordinates.java index a8a048a72f..c407516b59 100644 --- a/src/main/java/graphql/schema/FieldCoordinates.java +++ b/src/main/java/graphql/schema/FieldCoordinates.java @@ -2,6 +2,8 @@ import graphql.AssertException; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Objects; @@ -13,19 +15,20 @@ * like {@link graphql.schema.DataFetcher} need to be specified using those coordinates. */ @PublicApi +@NullMarked public class FieldCoordinates { private final boolean systemCoordinates; - private final String typeName; + private final @Nullable String typeName; private final String fieldName; - private FieldCoordinates(String typeName, String fieldName, boolean systemCoordinates) { + private FieldCoordinates(@Nullable String typeName, String fieldName, boolean systemCoordinates) { this.typeName = typeName; this.fieldName = fieldName; this.systemCoordinates = systemCoordinates; } - public String getTypeName() { + public @Nullable String getTypeName() { return typeName; } diff --git a/src/main/java/graphql/schema/GraphQLAppliedDirectiveArgument.java b/src/main/java/graphql/schema/GraphQLAppliedDirectiveArgument.java index 4446894ec9..9fa9679d10 100644 --- a/src/main/java/graphql/schema/GraphQLAppliedDirectiveArgument.java +++ b/src/main/java/graphql/schema/GraphQLAppliedDirectiveArgument.java @@ -8,7 +8,8 @@ import graphql.language.Value; import graphql.util.TraversalControl; import graphql.util.TraverserContext; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -26,14 +27,15 @@ * You can think of them as 'instances' of {@link GraphQLArgument}, when applied to a directive on a schema element */ @PublicApi +@NullMarked public class GraphQLAppliedDirectiveArgument implements GraphQLNamedSchemaElement, GraphQLInputSchemaElement { private final String name; private final InputValueWithState value; private final GraphQLInputType originalType; - private GraphQLInputType replacedType; + private @Nullable GraphQLInputType replacedType; - private final Argument definition; + private final @Nullable Argument definition; public static final String CHILD_TYPE = "type"; @@ -41,7 +43,7 @@ public class GraphQLAppliedDirectiveArgument implements GraphQLNamedSchemaElemen private GraphQLAppliedDirectiveArgument(String name, InputValueWithState value, GraphQLInputType type, - Argument definition + @Nullable Argument definition ) { assertValidName(name); this.name = name; @@ -70,7 +72,7 @@ public boolean hasSetValue() { /** * @return an input value with state for an applied directive argument */ - public @NonNull InputValueWithState getArgumentValue() { + public InputValueWithState getArgumentValue() { return value; } @@ -89,7 +91,7 @@ public boolean hasSetValue() { * * @return a value of type T which is the java value of the argument */ - public T getValue() { + public @Nullable T getValue() { return getInputValueImpl(getType(), value, GraphQLContext.getDefault(), Locale.getDefault()); } @@ -102,7 +104,7 @@ public T getValue() { return null; } - public Argument getDefinition() { + public @Nullable Argument getDefinition() { return definition; } @@ -186,6 +188,7 @@ public String toString() { '}'; } + @NullUnmarked public static class Builder extends GraphqlTypeBuilder { private InputValueWithState value = InputValueWithState.NOT_SET; @@ -218,7 +221,7 @@ public Builder definition(Argument definition) { * * @return this builder */ - public Builder valueLiteral(@NonNull Value value) { + public Builder valueLiteral(Value value) { this.value = InputValueWithState.newLiteralValue(value); return this; } @@ -233,7 +236,7 @@ public Builder valueProgrammatic(@Nullable Object value) { return this; } - public Builder inputValueWithState(@NonNull InputValueWithState value) { + public Builder inputValueWithState(InputValueWithState value) { this.value = Assert.assertNotNull(value); return this; } diff --git a/src/main/java/graphql/schema/GraphQLArgument.java b/src/main/java/graphql/schema/GraphQLArgument.java index 924cdf1198..9795bf6f1f 100644 --- a/src/main/java/graphql/schema/GraphQLArgument.java +++ b/src/main/java/graphql/schema/GraphQLArgument.java @@ -8,7 +8,8 @@ import graphql.language.Value; import graphql.util.TraversalControl; import graphql.util.TraverserContext; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -46,18 +47,19 @@ * as opposed to its schema definition itself. */ @PublicApi +@NullMarked public class GraphQLArgument implements GraphQLNamedSchemaElement, GraphQLInputValueDefinition { private final String name; - private final String description; - private final String deprecationReason; + private final @Nullable String description; + private final @Nullable String deprecationReason; private final GraphQLInputType originalType; - private GraphQLInputType replacedType; + private @Nullable GraphQLInputType replacedType; private final InputValueWithState defaultValue; private final InputValueWithState value; - private final InputValueDefinition definition; + private final @Nullable InputValueDefinition definition; private final DirectivesUtil.DirectivesHolder directivesHolder; @@ -65,14 +67,14 @@ public class GraphQLArgument implements GraphQLNamedSchemaElement, GraphQLInputV private GraphQLArgument(String name, - String description, + @Nullable String description, GraphQLInputType type, InputValueWithState defaultValue, InputValueWithState value, - InputValueDefinition definition, + @Nullable InputValueDefinition definition, List directives, List appliedDirectives, - String deprecationReason) { + @Nullable String deprecationReason) { assertValidName(name); assertNotNull(type, "type can't be null"); this.name = name; @@ -104,7 +106,7 @@ public GraphQLInputType getType() { * * @return a {@link InputValueWithState} that represents the arguments default value */ - public @NonNull InputValueWithState getArgumentDefaultValue() { + public InputValueWithState getArgumentDefaultValue() { return defaultValue; } @@ -125,7 +127,7 @@ public boolean hasSetValue() { * @deprecated use {@link GraphQLAppliedDirectiveArgument} instead */ @Deprecated(since = "2022-02-24") - public @NonNull InputValueWithState getArgumentValue() { + public InputValueWithState getArgumentValue() { return value; } @@ -148,7 +150,7 @@ public boolean hasSetValue() { * @deprecated use {@link GraphQLAppliedDirectiveArgument} instead */ @Deprecated(since = "2022-02-24") - public static T getArgumentValue(GraphQLArgument argument) { + public static @Nullable T getArgumentValue(GraphQLArgument argument) { return getInputValueImpl(argument.getType(), argument.getArgumentValue(), GraphQLContext.getDefault(), Locale.getDefault()); } @@ -168,15 +170,15 @@ public static T getArgumentValue(GraphQLArgument argument) { * * @return a value of type T which is the java value of the argument default */ - public static T getArgumentDefaultValue(GraphQLArgument argument) { + public static @Nullable T getArgumentDefaultValue(GraphQLArgument argument) { return getInputValueImpl(argument.getType(), argument.getArgumentDefaultValue(), GraphQLContext.getDefault(), Locale.getDefault()); } - public String getDescription() { + public @Nullable String getDescription() { return description; } - public String getDeprecationReason() { + public @Nullable String getDeprecationReason() { return deprecationReason; } @@ -184,7 +186,7 @@ public boolean isDeprecated() { return deprecationReason != null; } - public InputValueDefinition getDefinition() { + public @Nullable InputValueDefinition getDefinition() { return definition; } @@ -324,6 +326,7 @@ public GraphQLAppliedDirectiveArgument toAppliedArgument() { .build(); } + @NullUnmarked public static class Builder extends GraphqlDirectivesContainerTypeBuilder { private GraphQLInputType type; @@ -382,7 +385,7 @@ public Builder defaultValue(Object defaultValue) { * * @return this builder */ - public Builder defaultValueLiteral(@NonNull Value defaultValue) { + public Builder defaultValueLiteral(Value defaultValue) { this.defaultValue = InputValueWithState.newLiteralValue(defaultValue); return this; } @@ -432,7 +435,7 @@ public Builder value(@Nullable Object value) { * @deprecated use {@link GraphQLAppliedDirectiveArgument} methods instead */ @Deprecated(since = "2022-02-24") - public Builder valueLiteral(@NonNull Value value) { + public Builder valueLiteral(Value value) { this.value = InputValueWithState.newLiteralValue(value); return this; } diff --git a/src/main/java/graphql/schema/GraphQLCompositeType.java b/src/main/java/graphql/schema/GraphQLCompositeType.java index a8cf3c4ed6..20df40dcce 100644 --- a/src/main/java/graphql/schema/GraphQLCompositeType.java +++ b/src/main/java/graphql/schema/GraphQLCompositeType.java @@ -2,7 +2,9 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; @PublicApi +@NullMarked public interface GraphQLCompositeType extends GraphQLNamedOutputType { } diff --git a/src/main/java/graphql/schema/GraphQLDirective.java b/src/main/java/graphql/schema/GraphQLDirective.java index 402902303e..80aaacd274 100644 --- a/src/main/java/graphql/schema/GraphQLDirective.java +++ b/src/main/java/graphql/schema/GraphQLDirective.java @@ -6,6 +6,9 @@ import graphql.language.DirectiveDefinition; 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.Collections; import java.util.EnumSet; @@ -33,24 +36,25 @@ * as opposed to its schema definition itself. */ @PublicApi +@NullMarked public class GraphQLDirective implements GraphQLNamedSchemaElement { private final String name; private final boolean repeatable; - private final String description; + private final @Nullable String description; private final EnumSet locations; private final ImmutableList arguments; - private final DirectiveDefinition definition; + private final @Nullable DirectiveDefinition definition; public static final String CHILD_ARGUMENTS = "arguments"; private GraphQLDirective(String name, - String description, + @Nullable String description, boolean repeatable, EnumSet locations, List arguments, - DirectiveDefinition definition) { + @Nullable DirectiveDefinition definition) { assertValidName(name); assertNotNull(arguments, "arguments can't be null"); assertNotEmpty(locations, "locations can't be empty"); @@ -79,7 +83,7 @@ public List getArguments() { return arguments; } - public GraphQLArgument getArgument(String name) { + public @Nullable GraphQLArgument getArgument(String name) { for (GraphQLArgument argument : arguments) { if (argument.getName().equals(name)) { return argument; @@ -92,11 +96,11 @@ public EnumSet validLocations() { return EnumSet.copyOf(locations); } - public String getDescription() { + public @Nullable String getDescription() { return description; } - public DirectiveDefinition getDefinition() { + public @Nullable DirectiveDefinition getDefinition() { return definition; } @@ -192,6 +196,7 @@ public static Builder newDirective(GraphQLDirective existing) { return new Builder(existing); } + @NullUnmarked public static class Builder extends GraphqlTypeBuilder { private EnumSet locations = EnumSet.noneOf(DirectiveLocation.class); diff --git a/src/main/java/graphql/schema/GraphQLDirectiveContainer.java b/src/main/java/graphql/schema/GraphQLDirectiveContainer.java index 4bee55aa36..979a2d4580 100644 --- a/src/main/java/graphql/schema/GraphQLDirectiveContainer.java +++ b/src/main/java/graphql/schema/GraphQLDirectiveContainer.java @@ -1,6 +1,8 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Map; @@ -23,6 +25,7 @@ * @see graphql.language.DirectiveDefinition#isRepeatable() */ @PublicApi +@NullMarked public interface GraphQLDirectiveContainer extends GraphQLNamedSchemaElement { String CHILD_DIRECTIVES = "directives"; @@ -52,7 +55,7 @@ public interface GraphQLDirectiveContainer extends GraphQLNamedSchemaElement { * * @return the directive or null if there is not one with that name */ - GraphQLAppliedDirective getAppliedDirective(String directiveName); + @Nullable GraphQLAppliedDirective getAppliedDirective(String directiveName); /** @@ -135,7 +138,7 @@ default boolean hasAppliedDirective(String directiveName) { * @deprecated - use the {@link GraphQLAppliedDirective} methods instead */ @Deprecated(since = "2022-02-24") - GraphQLDirective getDirective(String directiveName); + @Nullable GraphQLDirective getDirective(String directiveName); /** * Returns all of the directives with the provided name, including repeatable and non repeatable directives. diff --git a/src/main/java/graphql/schema/GraphQLEnumValueDefinition.java b/src/main/java/graphql/schema/GraphQLEnumValueDefinition.java index b06aed763c..962fb08f45 100644 --- a/src/main/java/graphql/schema/GraphQLEnumValueDefinition.java +++ b/src/main/java/graphql/schema/GraphQLEnumValueDefinition.java @@ -12,7 +12,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import static graphql.Assert.assertNotNull; import static graphql.Assert.assertValidName; @@ -25,23 +27,24 @@ * @see graphql.schema.GraphQLEnumType */ @PublicApi +@NullMarked public class GraphQLEnumValueDefinition implements GraphQLNamedSchemaElement, GraphQLDirectiveContainer { private final String name; - private final String description; + private final @Nullable String description; private final Object value; - private final String deprecationReason; + private final @Nullable String deprecationReason; private final DirectivesUtil.DirectivesHolder directivesHolder; - private final EnumValueDefinition definition; + private final @Nullable EnumValueDefinition definition; @Internal private GraphQLEnumValueDefinition(String name, - String description, + @Nullable String description, Object value, - String deprecationReason, + @Nullable String deprecationReason, List directives, List appliedDirectives, - EnumValueDefinition definition) { + @Nullable EnumValueDefinition definition) { assertValidName(name); assertNotNull(directives, "directives cannot be null"); @@ -58,7 +61,7 @@ public String getName() { return name; } - public String getDescription() { + public @Nullable String getDescription() { return description; } @@ -70,7 +73,7 @@ public boolean isDeprecated() { return deprecationReason != null; } - public String getDeprecationReason() { + public @Nullable String getDeprecationReason() { return deprecationReason; } @@ -94,7 +97,7 @@ public GraphQLDirective getDirective(String directiveName) { return directivesHolder.getDirective(directiveName); } - public EnumValueDefinition getDefinition() { + public @Nullable EnumValueDefinition getDefinition() { return definition; } diff --git a/src/main/java/graphql/schema/GraphQLFieldDefinition.java b/src/main/java/graphql/schema/GraphQLFieldDefinition.java index 779421c2c1..86cdc07d96 100644 --- a/src/main/java/graphql/schema/GraphQLFieldDefinition.java +++ b/src/main/java/graphql/schema/GraphQLFieldDefinition.java @@ -15,7 +15,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; @@ -34,32 +36,33 @@ * See https://graphql.org/learn/queries/#fields for more details on the concept. */ @PublicApi +@NullMarked public class GraphQLFieldDefinition implements GraphQLNamedSchemaElement, GraphQLDirectiveContainer { private final String name; - private final String description; + private final @Nullable String description; private final GraphQLOutputType originalType; - private final DataFetcherFactory dataFetcherFactory; - private final String deprecationReason; + private final @Nullable DataFetcherFactory dataFetcherFactory; + private final @Nullable String deprecationReason; private final ImmutableList arguments; private final DirectivesUtil.DirectivesHolder directivesHolder; - private final FieldDefinition definition; + private final @Nullable FieldDefinition definition; - private GraphQLOutputType replacedType; + private @Nullable GraphQLOutputType replacedType; public static final String CHILD_ARGUMENTS = "arguments"; public static final String CHILD_TYPE = "type"; @Internal private GraphQLFieldDefinition(String name, - String description, + @Nullable String description, GraphQLOutputType type, - DataFetcherFactory dataFetcherFactory, + @Nullable DataFetcherFactory dataFetcherFactory, List arguments, - String deprecationReason, + @Nullable String deprecationReason, List directives, List appliedDirectives, - FieldDefinition definition) { + @Nullable FieldDefinition definition) { assertValidName(name); assertNotNull(type, "type can't be null"); assertNotNull(arguments, "arguments can't be null"); @@ -90,7 +93,7 @@ public GraphQLOutputType getType() { // to be removed in a future version when all code is in the code registry @Internal @Deprecated(since = "2018-12-03") - DataFetcher getDataFetcher() { + @Nullable DataFetcher getDataFetcher() { if (dataFetcherFactory == null) { return null; } @@ -99,7 +102,7 @@ DataFetcher getDataFetcher() { .build()); } - public GraphQLArgument getArgument(String name) { + public @Nullable GraphQLArgument getArgument(String name) { for (GraphQLArgument argument : arguments) { if (argument.getName().equals(name)) { return argument; @@ -147,15 +150,15 @@ public List getArguments() { return arguments; } - public String getDescription() { + public @Nullable String getDescription() { return description; } - public FieldDefinition getDefinition() { + public @Nullable FieldDefinition getDefinition() { return definition; } - public String getDeprecationReason() { + public @Nullable String getDeprecationReason() { return deprecationReason; } diff --git a/src/main/java/graphql/schema/GraphQLFieldsContainer.java b/src/main/java/graphql/schema/GraphQLFieldsContainer.java index 449fb78f22..f2b54f7ce0 100644 --- a/src/main/java/graphql/schema/GraphQLFieldsContainer.java +++ b/src/main/java/graphql/schema/GraphQLFieldsContainer.java @@ -1,6 +1,8 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; @@ -12,13 +14,14 @@ * @see graphql.schema.GraphQLInterfaceType */ @PublicApi +@NullMarked public interface GraphQLFieldsContainer extends GraphQLCompositeType { - GraphQLFieldDefinition getFieldDefinition(String name); + @Nullable GraphQLFieldDefinition getFieldDefinition(String name); List getFieldDefinitions(); - default GraphQLFieldDefinition getField(String name) { + default @Nullable GraphQLFieldDefinition getField(String name) { return getFieldDefinition(name); } diff --git a/src/main/java/graphql/schema/GraphQLImplementingType.java b/src/main/java/graphql/schema/GraphQLImplementingType.java index f8d394567f..87f6c95ebc 100644 --- a/src/main/java/graphql/schema/GraphQLImplementingType.java +++ b/src/main/java/graphql/schema/GraphQLImplementingType.java @@ -1,6 +1,7 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; @@ -8,6 +9,7 @@ * A GraphQLType which can implement interfaces */ @PublicApi +@NullMarked public interface GraphQLImplementingType extends GraphQLFieldsContainer { /** * @return This returns GraphQLInterface or GraphQLTypeReference instances, if the type diff --git a/src/main/java/graphql/schema/GraphQLInputFieldsContainer.java b/src/main/java/graphql/schema/GraphQLInputFieldsContainer.java index bac018bdf9..ffd44c0e5a 100644 --- a/src/main/java/graphql/schema/GraphQLInputFieldsContainer.java +++ b/src/main/java/graphql/schema/GraphQLInputFieldsContainer.java @@ -1,6 +1,8 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; @@ -10,9 +12,10 @@ * @see graphql.schema.GraphQLInputType */ @PublicApi +@NullMarked public interface GraphQLInputFieldsContainer extends GraphQLNamedType { - GraphQLInputObjectField getFieldDefinition(String name); + @Nullable GraphQLInputObjectField getFieldDefinition(String name); List getFieldDefinitions(); } \ No newline at end of file diff --git a/src/main/java/graphql/schema/GraphQLInputObjectField.java b/src/main/java/graphql/schema/GraphQLInputObjectField.java index 23323393b8..2b83d3b836 100644 --- a/src/main/java/graphql/schema/GraphQLInputObjectField.java +++ b/src/main/java/graphql/schema/GraphQLInputObjectField.java @@ -8,7 +8,8 @@ import graphql.language.Value; import graphql.util.TraversalControl; import graphql.util.TraverserContext; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -30,31 +31,32 @@ * See https://graphql.org/learn/schema/#input-types for more details on the concept. */ @PublicApi +@NullMarked public class GraphQLInputObjectField implements GraphQLNamedSchemaElement, GraphQLInputValueDefinition { private final String name; - private final String description; + private final @Nullable String description; private final GraphQLInputType originalType; private final InputValueWithState defaultValue; - private final String deprecationReason; - private final InputValueDefinition definition; + private final @Nullable String deprecationReason; + private final @Nullable InputValueDefinition definition; private final DirectivesUtil.DirectivesHolder directivesHolder; - private GraphQLInputType replacedType; + private @Nullable GraphQLInputType replacedType; public static final String CHILD_TYPE = "type"; private GraphQLInputObjectField( String name, - String description, + @Nullable String description, GraphQLInputType type, InputValueWithState defaultValue, List directives, List appliedDirectives, - InputValueDefinition definition, - String deprecationReason) { + @Nullable InputValueDefinition definition, + @Nullable String deprecationReason) { assertValidName(name); assertNotNull(type, "type can't be null"); assertNotNull(directives, "directives cannot be null"); @@ -88,7 +90,7 @@ public GraphQLInputType getType() { * * @return a input value with captured state */ - public @NonNull InputValueWithState getInputFieldDefaultValue() { + public InputValueWithState getInputFieldDefaultValue() { return defaultValue; } @@ -108,7 +110,7 @@ public GraphQLInputType getType() { * * @return a value of type T which is the java value of the input field default */ - public static T getInputFieldDefaultValue(GraphQLInputObjectField inputObjectField) { + public static @Nullable T getInputFieldDefaultValue(GraphQLInputObjectField inputObjectField) { return getInputValueImpl(inputObjectField.getType(), inputObjectField.getInputFieldDefaultValue(), GraphQLContext.getDefault(), Locale.getDefault()); } @@ -117,11 +119,11 @@ public boolean hasSetDefaultValue() { return defaultValue.isSet(); } - public String getDescription() { + public @Nullable String getDescription() { return description; } - public String getDeprecationReason() { + public @Nullable String getDeprecationReason() { return deprecationReason; } @@ -129,7 +131,7 @@ public boolean isDeprecated() { return deprecationReason != null; } - public InputValueDefinition getDefinition() { + public @Nullable InputValueDefinition getDefinition() { return definition; } @@ -251,7 +253,7 @@ public String toString() { '}'; } - private static Object inputTypeToStringAvoidingCircularReference(GraphQLInputType graphQLInputType) { + private static @Nullable Object inputTypeToStringAvoidingCircularReference(@Nullable GraphQLInputType graphQLInputType) { return (graphQLInputType instanceof GraphQLInputObjectType) ? String.format("[%s]", GraphQLInputObjectType.class.getSimpleName()) : graphQLInputType; diff --git a/src/main/java/graphql/schema/GraphQLInputObjectType.java b/src/main/java/graphql/schema/GraphQLInputObjectType.java index 95ac38adef..6abd924a04 100644 --- a/src/main/java/graphql/schema/GraphQLInputObjectType.java +++ b/src/main/java/graphql/schema/GraphQLInputObjectType.java @@ -19,7 +19,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; @@ -35,13 +37,14 @@ * See https://graphql.org/learn/schema/#input-types for more details on the concept */ @PublicApi +@NullMarked public class GraphQLInputObjectType implements GraphQLNamedInputType, GraphQLUnmodifiedType, GraphQLNullableType, GraphQLInputFieldsContainer, GraphQLDirectiveContainer { private final String name; private final boolean isOneOf; - private final String description; + private final @Nullable String description; private final ImmutableMap fieldMap; - private final InputObjectTypeDefinition definition; + private final @Nullable InputObjectTypeDefinition definition; private final ImmutableList extensionDefinitions; private final DirectivesUtil.DirectivesHolder directives; @@ -49,11 +52,11 @@ public class GraphQLInputObjectType implements GraphQLNamedInputType, GraphQLUnm @Internal private GraphQLInputObjectType(String name, - String description, + @Nullable String description, List fields, List directives, List appliedDirectives, - InputObjectTypeDefinition definition, + @Nullable InputObjectTypeDefinition definition, List extensionDefinitions) { assertValidName(name); assertNotNull(fields, "fields can't be null"); @@ -100,7 +103,7 @@ public boolean isOneOf() { return isOneOf; } - public String getDescription() { + public @Nullable String getDescription() { return description; } @@ -108,7 +111,7 @@ public List getFields() { return getFieldDefinitions(); } - public GraphQLInputObjectField getField(String name) { + public @Nullable GraphQLInputObjectField getField(String name) { return fieldMap.get(name); } @@ -148,7 +151,7 @@ public GraphQLAppliedDirective getAppliedDirective(String directiveName) { } @Override - public GraphQLInputObjectField getFieldDefinition(String name) { + public @Nullable GraphQLInputObjectField getFieldDefinition(String name) { return fieldMap.get(name); } @@ -158,7 +161,7 @@ public List getFieldDefinitions() { return values instanceof ImmutableList ? (ImmutableList) values : ImmutableList.copyOf(values); } - public InputObjectTypeDefinition getDefinition() { + public @Nullable InputObjectTypeDefinition getDefinition() { return definition; } diff --git a/src/main/java/graphql/schema/GraphQLInputSchemaElement.java b/src/main/java/graphql/schema/GraphQLInputSchemaElement.java index 86b5b620f1..e0b30cd17a 100644 --- a/src/main/java/graphql/schema/GraphQLInputSchemaElement.java +++ b/src/main/java/graphql/schema/GraphQLInputSchemaElement.java @@ -1,10 +1,12 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * A schema element that is concerned with input. */ @PublicApi +@NullMarked public interface GraphQLInputSchemaElement extends GraphQLSchemaElement { } diff --git a/src/main/java/graphql/schema/GraphQLInputType.java b/src/main/java/graphql/schema/GraphQLInputType.java index 46fcf91301..19a3f19d19 100644 --- a/src/main/java/graphql/schema/GraphQLInputType.java +++ b/src/main/java/graphql/schema/GraphQLInputType.java @@ -2,11 +2,13 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * Input types represent those set of types that are allowed to be accepted as graphql mutation input, as opposed * to {@link graphql.schema.GraphQLOutputType}s which can only be used as graphql response output. */ @PublicApi +@NullMarked public interface GraphQLInputType extends GraphQLType, GraphQLInputSchemaElement { } diff --git a/src/main/java/graphql/schema/GraphQLInputValueDefinition.java b/src/main/java/graphql/schema/GraphQLInputValueDefinition.java index bce4e31fa5..bd51e0550e 100644 --- a/src/main/java/graphql/schema/GraphQLInputValueDefinition.java +++ b/src/main/java/graphql/schema/GraphQLInputValueDefinition.java @@ -1,6 +1,7 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * Named schema elements that contain input type information. @@ -11,6 +12,7 @@ * @see graphql.schema.GraphQLArgument */ @PublicApi +@NullMarked public interface GraphQLInputValueDefinition extends GraphQLDirectiveContainer, GraphQLInputSchemaElement { T getType(); diff --git a/src/main/java/graphql/schema/GraphQLInterfaceType.java b/src/main/java/graphql/schema/GraphQLInterfaceType.java index eba21cc9aa..91695d7704 100644 --- a/src/main/java/graphql/schema/GraphQLInterfaceType.java +++ b/src/main/java/graphql/schema/GraphQLInterfaceType.java @@ -17,7 +17,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; @@ -38,31 +40,32 @@ * See https://graphql.org/learn/schema/#interfaces for more details on the concept. */ @PublicApi +@NullMarked public class GraphQLInterfaceType implements GraphQLNamedType, GraphQLCompositeType, GraphQLUnmodifiedType, GraphQLNullableType, GraphQLDirectiveContainer, GraphQLImplementingType { private final String name; - private final String description; + private final @Nullable String description; private final Map fieldDefinitionsByName; - private final TypeResolver typeResolver; - private final InterfaceTypeDefinition definition; + private final @Nullable TypeResolver typeResolver; + private final @Nullable InterfaceTypeDefinition definition; private final ImmutableList extensionDefinitions; private final DirectivesUtil.DirectivesHolder directivesHolder; private final ImmutableList originalInterfaces; private final Comparator interfaceComparator; - private ImmutableList replacedInterfaces; + private @Nullable ImmutableList replacedInterfaces; public static final String CHILD_FIELD_DEFINITIONS = "fieldDefinitions"; public static final String CHILD_INTERFACES = "interfaces"; @Internal private GraphQLInterfaceType(String name, - String description, + @Nullable String description, List fieldDefinitions, - TypeResolver typeResolver, + @Nullable TypeResolver typeResolver, List directives, List appliedDirectives, - InterfaceTypeDefinition definition, + @Nullable InterfaceTypeDefinition definition, List extensionDefinitions, List interfaces, Comparator interfaceComparator) { @@ -87,7 +90,7 @@ private Map buildDefinitionMap(Listhttps://graphql.org/learn/schema/#lists-and-non-null for more details on the concept */ @PublicApi +@NullMarked public class GraphQLNonNull implements GraphQLType, GraphQLInputType, GraphQLOutputType, GraphQLModifiedType { /** @@ -34,7 +37,7 @@ public static GraphQLNonNull nonNull(GraphQLType wrappedType) { } private final GraphQLType originalWrappedType; - private GraphQLType replacedWrappedType; + private @Nullable GraphQLType replacedWrappedType; public static final String CHILD_WRAPPED_TYPE = "wrappedType"; @@ -104,7 +107,7 @@ public SchemaElementChildrenContainer getChildrenWithTypeReferences() { @Override public GraphQLSchemaElement withNewChildren(SchemaElementChildrenContainer newChildren) { - return nonNull(newChildren.getChildOrNull(CHILD_WRAPPED_TYPE)); + return nonNull(assertNotNull(newChildren.getChildOrNull(CHILD_WRAPPED_TYPE), "wrapped type child should not be null")); } @Override diff --git a/src/main/java/graphql/schema/GraphQLNullableType.java b/src/main/java/graphql/schema/GraphQLNullableType.java index 584f9e0288..0f2c980e13 100644 --- a/src/main/java/graphql/schema/GraphQLNullableType.java +++ b/src/main/java/graphql/schema/GraphQLNullableType.java @@ -2,7 +2,9 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; @PublicApi +@NullMarked public interface GraphQLNullableType extends GraphQLType { } diff --git a/src/main/java/graphql/schema/GraphQLObjectType.java b/src/main/java/graphql/schema/GraphQLObjectType.java index 199c9cc009..515b2c4a95 100644 --- a/src/main/java/graphql/schema/GraphQLObjectType.java +++ b/src/main/java/graphql/schema/GraphQLObjectType.java @@ -17,7 +17,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.function.UnaryOperator; import static graphql.Assert.assertNotNull; @@ -38,30 +40,31 @@ * See https://graphql.org/learn/schema/#object-types-and-fields for more details on the concept. */ @PublicApi +@NullMarked public class GraphQLObjectType implements GraphQLNamedOutputType, GraphQLCompositeType, GraphQLUnmodifiedType, GraphQLNullableType, GraphQLDirectiveContainer, GraphQLImplementingType { private final String name; - private final String description; + private final @Nullable String description; private final Comparator interfaceComparator; private final Map fieldDefinitionsByName; private final ImmutableList originalInterfaces; private final DirectivesUtil.DirectivesHolder directivesHolder; - private final ObjectTypeDefinition definition; + private final @Nullable ObjectTypeDefinition definition; private final ImmutableList extensionDefinitions; - private ImmutableList replacedInterfaces; + private @Nullable ImmutableList replacedInterfaces; public static final String CHILD_INTERFACES = "interfaces"; public static final String CHILD_FIELD_DEFINITIONS = "fieldDefinitions"; @Internal private GraphQLObjectType(String name, - String description, + @Nullable String description, List fieldDefinitions, List interfaces, List directives, List appliedDirectives, - ObjectTypeDefinition definition, + @Nullable ObjectTypeDefinition definition, List extensionDefinitions, Comparator interfaceComparator) { assertValidName(name); @@ -122,7 +125,7 @@ public GraphQLAppliedDirective getAppliedDirective(String directiveName) { } @Override - public GraphQLFieldDefinition getFieldDefinition(String name) { + public @Nullable GraphQLFieldDefinition getFieldDefinition(String name) { return fieldDefinitionsByName.get(name); } @@ -139,7 +142,7 @@ public List getInterfaces() { return originalInterfaces; } - public String getDescription() { + public @Nullable String getDescription() { return description; } @@ -149,7 +152,7 @@ public String getName() { return name; } - public ObjectTypeDefinition getDefinition() { + public @Nullable ObjectTypeDefinition getDefinition() { return definition; } diff --git a/src/main/java/graphql/schema/GraphQLOutputType.java b/src/main/java/graphql/schema/GraphQLOutputType.java index 22e5c99392..8789c40609 100644 --- a/src/main/java/graphql/schema/GraphQLOutputType.java +++ b/src/main/java/graphql/schema/GraphQLOutputType.java @@ -2,11 +2,13 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * Output types represent those set of types that are allowed to be sent back as a graphql response, as opposed * to {@link graphql.schema.GraphQLInputType}s which can only be used as graphql mutation input. */ @PublicApi +@NullMarked public interface GraphQLOutputType extends GraphQLType { } diff --git a/src/main/java/graphql/schema/GraphQLSchema.java b/src/main/java/graphql/schema/GraphQLSchema.java index 1ba9ed962c..8a8e020bd4 100644 --- a/src/main/java/graphql/schema/GraphQLSchema.java +++ b/src/main/java/graphql/schema/GraphQLSchema.java @@ -448,7 +448,7 @@ public boolean containsType(String typeName) { } return Assert.assertShouldNeverHappen("The system field name %s is unknown", fieldName); } - String typeName = fieldCoordinates.getTypeName(); + String typeName = Assert.assertNotNull(fieldCoordinates.getTypeName(), "non-system coordinates must have a type name"); GraphQLType graphQLType = getType(typeName); if (graphQLType != null) { assertTrue(graphQLType instanceof GraphQLFieldsContainer, diff --git a/src/main/java/graphql/schema/GraphQLSchemaElement.java b/src/main/java/graphql/schema/GraphQLSchemaElement.java index df6a15cc0e..c8d92e6346 100644 --- a/src/main/java/graphql/schema/GraphQLSchemaElement.java +++ b/src/main/java/graphql/schema/GraphQLSchemaElement.java @@ -4,6 +4,7 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; import java.util.List; @@ -14,6 +15,7 @@ * this interface. */ @PublicApi +@NullMarked public interface GraphQLSchemaElement { default List getChildren() { diff --git a/src/main/java/graphql/schema/GraphQLTypeReference.java b/src/main/java/graphql/schema/GraphQLTypeReference.java index 08fb3df47a..94009b9a59 100644 --- a/src/main/java/graphql/schema/GraphQLTypeReference.java +++ b/src/main/java/graphql/schema/GraphQLTypeReference.java @@ -5,6 +5,8 @@ import graphql.language.Node; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import static graphql.Assert.assertValidName; @@ -13,6 +15,7 @@ * object when the schema is built. */ @PublicApi +@NullMarked public class GraphQLTypeReference implements GraphQLNamedOutputType, GraphQLNamedInputType { /** @@ -41,12 +44,12 @@ public String getName() { } @Override - public String getDescription() { + public @Nullable String getDescription() { return null; } @Override - public Node getDefinition() { + public @Nullable Node getDefinition() { return null; } diff --git a/src/main/java/graphql/schema/GraphQLTypeVisitor.java b/src/main/java/graphql/schema/GraphQLTypeVisitor.java index 7853fa8dac..afb3e6068c 100644 --- a/src/main/java/graphql/schema/GraphQLTypeVisitor.java +++ b/src/main/java/graphql/schema/GraphQLTypeVisitor.java @@ -4,6 +4,7 @@ import graphql.util.TraversalControl; import graphql.util.TraverserContext; import graphql.util.TreeTransformerUtil; +import org.jspecify.annotations.NullMarked; /** * GraphQLTypeVisitor can be used to visit all the elements of a schema @@ -12,6 +13,7 @@ * @see GraphQLTypeVisitorStub */ @PublicApi +@NullMarked public interface GraphQLTypeVisitor { /** * This method will be called when a directive is applied to a schema element. diff --git a/src/main/java/graphql/schema/GraphQLTypeVisitorStub.java b/src/main/java/graphql/schema/GraphQLTypeVisitorStub.java index 79e9c0909c..d8a8ff995e 100644 --- a/src/main/java/graphql/schema/GraphQLTypeVisitorStub.java +++ b/src/main/java/graphql/schema/GraphQLTypeVisitorStub.java @@ -3,6 +3,7 @@ import graphql.PublicApi; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; import static graphql.util.TraversalControl.CONTINUE; @@ -11,6 +12,7 @@ * Overwrite only required methods and/or {@link #visitGraphQLType(GraphQLSchemaElement, TraverserContext)} as default fallback. */ @PublicApi +@NullMarked public class GraphQLTypeVisitorStub implements GraphQLTypeVisitor { @Override diff --git a/src/main/java/graphql/schema/GraphQLUnmodifiedType.java b/src/main/java/graphql/schema/GraphQLUnmodifiedType.java index 8fc8c0a979..a211dc083a 100644 --- a/src/main/java/graphql/schema/GraphQLUnmodifiedType.java +++ b/src/main/java/graphql/schema/GraphQLUnmodifiedType.java @@ -2,7 +2,9 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; @PublicApi +@NullMarked public interface GraphQLUnmodifiedType extends GraphQLNamedType { } diff --git a/src/main/java/graphql/schema/GraphqlElementParentTree.java b/src/main/java/graphql/schema/GraphqlElementParentTree.java index acf5080e0c..f15484e16d 100644 --- a/src/main/java/graphql/schema/GraphqlElementParentTree.java +++ b/src/main/java/graphql/schema/GraphqlElementParentTree.java @@ -2,6 +2,8 @@ import graphql.Internal; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayDeque; import java.util.ArrayList; @@ -18,10 +20,11 @@ * which can be on a GraphqlFieldDefinition, which can be on a GraphqlObjectType. */ @PublicApi +@NullMarked public class GraphqlElementParentTree { private final GraphQLSchemaElement element; - private final GraphqlElementParentTree parent; + private final @Nullable GraphqlElementParentTree parent; @Internal public GraphqlElementParentTree(Deque nodeStack) { diff --git a/src/main/java/graphql/schema/GraphqlTypeComparatorEnvironment.java b/src/main/java/graphql/schema/GraphqlTypeComparatorEnvironment.java index de7ed388a9..0e48129c27 100644 --- a/src/main/java/graphql/schema/GraphqlTypeComparatorEnvironment.java +++ b/src/main/java/graphql/schema/GraphqlTypeComparatorEnvironment.java @@ -1,6 +1,9 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.Objects; import java.util.function.Consumer; @@ -12,13 +15,14 @@ * Otherwise, supplying only the {@code elementType} results in the {@code Comparator} being reused across all matching {@code GraphQLType}s regardless of parent. */ @PublicApi +@NullMarked public class GraphqlTypeComparatorEnvironment { - private Class parentType; + private @Nullable Class parentType; - private Class elementType; + private @Nullable Class elementType; - private GraphqlTypeComparatorEnvironment(Class parentType, Class elementType) { + private GraphqlTypeComparatorEnvironment(@Nullable Class parentType, @Nullable Class elementType) { this.parentType = parentType; this.elementType = elementType; } @@ -26,14 +30,14 @@ private GraphqlTypeComparatorEnvironment(Class p /** * @return The parent type or {@code null} if not supplied. */ - public Class getParentType() { + public @Nullable Class getParentType() { return parentType; } /** * @return The valid element type or {@code null} if not supplied. */ - public Class getElementType() { + public @Nullable Class getElementType() { return elementType; } @@ -61,7 +65,7 @@ public boolean equals(Object o) { } GraphqlTypeComparatorEnvironment that = (GraphqlTypeComparatorEnvironment) o; - return Objects.equals(parentType, that.parentType) && elementType.equals(that.elementType); + return Objects.equals(parentType, that.parentType) && Objects.equals(elementType, that.elementType); } @Override @@ -80,6 +84,7 @@ public static Builder newEnvironment(GraphqlTypeComparatorEnvironment existing) return new Builder(existing); } + @NullUnmarked public static class Builder { private Class parentType; diff --git a/src/main/java/graphql/schema/GraphqlTypeComparatorRegistry.java b/src/main/java/graphql/schema/GraphqlTypeComparatorRegistry.java index af9f28914b..1a5dd86aea 100644 --- a/src/main/java/graphql/schema/GraphqlTypeComparatorRegistry.java +++ b/src/main/java/graphql/schema/GraphqlTypeComparatorRegistry.java @@ -1,10 +1,12 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.Comparator; @PublicApi +@NullMarked public interface GraphqlTypeComparatorRegistry { /** diff --git a/src/main/java/graphql/schema/InputValueWithState.java b/src/main/java/graphql/schema/InputValueWithState.java index 47a5236ec9..63ec3c1e85 100644 --- a/src/main/java/graphql/schema/InputValueWithState.java +++ b/src/main/java/graphql/schema/InputValueWithState.java @@ -2,7 +2,7 @@ import graphql.PublicApi; import graphql.language.Value; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import static graphql.Assert.assertNotNull; @@ -11,6 +11,7 @@ * Used by @{@link GraphQLArgument} and {@link GraphQLInputObjectField} to represent different value states. */ @PublicApi +@NullMarked public class InputValueWithState { private enum State { /** @@ -35,16 +36,16 @@ private enum State { } private final State state; - private final Object value; + private final @Nullable Object value; - private InputValueWithState(State state, Object value) { + private InputValueWithState(State state, @Nullable Object value) { this.state = state; this.value = value; } public static final InputValueWithState NOT_SET = new InputValueWithState(State.NOT_SET, null); - public static InputValueWithState newLiteralValue(@NonNull Value value) { + public static InputValueWithState newLiteralValue(Value value) { assertNotNull(value, "value literal can't be null"); return new InputValueWithState(State.LITERAL, value); } diff --git a/src/main/java/graphql/schema/SchemaElementChildrenContainer.java b/src/main/java/graphql/schema/SchemaElementChildrenContainer.java index 428913b012..27c398e3f0 100644 --- a/src/main/java/graphql/schema/SchemaElementChildrenContainer.java +++ b/src/main/java/graphql/schema/SchemaElementChildrenContainer.java @@ -1,6 +1,9 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -13,6 +16,7 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class SchemaElementChildrenContainer { private final Map> children = new LinkedHashMap<>(); @@ -25,7 +29,7 @@ public List getChildren(String key) { return (List) children.getOrDefault(key, emptyList()); } - public T getChildOrNull(String key) { + public @Nullable T getChildOrNull(String key) { List result = children.getOrDefault(key, new ArrayList<>()); if (result.size() > 1) { throw new IllegalStateException("children " + key + " is not a single value"); @@ -65,6 +69,7 @@ public boolean isEmpty() { return this.children.isEmpty(); } + @NullUnmarked public static class Builder { private final Map> children = new LinkedHashMap<>(); diff --git a/src/main/java/graphql/schema/SchemaTransformer.java b/src/main/java/graphql/schema/SchemaTransformer.java index d0b98912bb..853d9bc55b 100644 --- a/src/main/java/graphql/schema/SchemaTransformer.java +++ b/src/main/java/graphql/schema/SchemaTransformer.java @@ -4,6 +4,9 @@ import com.google.common.collect.Multimap; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import graphql.introspection.Introspection; import graphql.schema.idl.ScalarInfo; import graphql.util.Breadcrumb; @@ -76,6 +79,7 @@ * */ @PublicApi +@NullMarked public class SchemaTransformer { /** @@ -152,7 +156,7 @@ public static GraphQLSchema transformSchemaWithDeletes(GraphQLSchema schema, Gra * * @see GraphQLTypeVisitor#deleteNode(TraverserContext) */ - public static GraphQLSchema transformSchemaWithDeletes(GraphQLSchema schema, GraphQLTypeVisitor visitor, Consumer postTransformation) { + public static GraphQLSchema transformSchemaWithDeletes(GraphQLSchema schema, GraphQLTypeVisitor visitor, @Nullable Consumer postTransformation) { SchemaTransformer schemaTransformer = new SchemaTransformer(); return (GraphQLSchema) schemaTransformer.transformImpl(schema, null, visitor, postTransformation, true); } @@ -185,10 +189,10 @@ public T transform(final T schemaElement, Graph return (T) transformImpl(null, schemaElement, visitor, null, false); } - private Object transformImpl(final GraphQLSchema schema, - GraphQLSchemaElement schemaElement, + private Object transformImpl(final @Nullable GraphQLSchema schema, + @Nullable GraphQLSchemaElement schemaElement, GraphQLTypeVisitor visitor, - Consumer postTransformation, + @Nullable Consumer postTransformation, boolean ensureAllTypesAreVisited) { DummyRoot dummyRoot; GraphQLCodeRegistry.Builder codeRegistry = null; @@ -216,10 +220,10 @@ private Object transformImpl(final GraphQLSchema schema, } if (schema != null) { - + GraphQLCodeRegistry.Builder registry = assertNotNull(codeRegistry, "codeRegistry must not be null when schema is set"); GraphQLSchema graphQLSchema = schema; - if (schemaChanged || codeRegistry.hasChanged()) { - graphQLSchema = dummyRoot.rebuildSchema(codeRegistry, allChangedNamedTypes); + if (schemaChanged || registry.hasChanged()) { + graphQLSchema = dummyRoot.rebuildSchema(registry, allChangedNamedTypes); if (postTransformation != null) { graphQLSchema = graphQLSchema.transform(postTransformation); } @@ -230,7 +234,7 @@ private Object transformImpl(final GraphQLSchema schema, } } - private void replaceTypeReferences(DummyRoot dummyRoot, GraphQLSchema schema, GraphQLCodeRegistry.Builder codeRegistry, Map changedTypes) { + private void replaceTypeReferences(DummyRoot dummyRoot, @Nullable GraphQLSchema schema, GraphQLCodeRegistry.@Nullable Builder codeRegistry, Map changedTypes) { GraphQLTypeVisitor typeRefVisitor = new GraphQLTypeVisitorStub() { @Override public TraversalControl visitGraphQLTypeReference(GraphQLTypeReference typeRef, TraverserContext context) { @@ -249,8 +253,8 @@ private boolean traverseAndTransform(DummyRoot dummyRoot, Map typesWhereNameIsChanged, Set allChangedNamedTypes, Map typeReferences, - GraphQLTypeVisitor visitor, GraphQLCodeRegistry.Builder codeRegistry, - GraphQLSchema schema) { + GraphQLTypeVisitor visitor, GraphQLCodeRegistry.@Nullable Builder codeRegistry, + @Nullable GraphQLSchema schema) { List> zippers = new LinkedList<>(); Map> zipperByNodeAfterTraversing = new LinkedHashMap<>(); Map> zipperByOriginalNode = new LinkedHashMap<>(); @@ -263,7 +267,7 @@ private boolean traverseAndTransform(DummyRoot dummyRoot, TraverserVisitor nodeTraverserVisitor = new TraverserVisitor<>() { @Override public TraversalControl enter(TraverserContext context) { - GraphQLSchemaElement currentSchemaElement = context.thisNode(); + GraphQLSchemaElement currentSchemaElement = assertNotNull(context.thisNode(), "thisNode should not be null"); if (currentSchemaElement == dummyRoot) { return TraversalControl.CONTINUE; } @@ -283,7 +287,7 @@ public TraversalControl enter(TraverserContext context) { nodeZipper = zippers.get(zippers.size() - 1); if (context.originalThisNode() instanceof GraphQLNamedType && context.isChanged()) { GraphQLNamedType originalNamedType = (GraphQLNamedType) context.originalThisNode(); - GraphQLNamedType changedNamedType = (GraphQLNamedType) context.thisNode(); + GraphQLNamedType changedNamedType = (GraphQLNamedType) assertNotNull(context.thisNode(), "thisNode should not be null"); if (!originalNamedType.getName().equals(changedNamedType.getName())) { typesWhereNameIsChanged.put(originalNamedType.getName(), changedNamedType); } @@ -318,8 +322,8 @@ public TraversalControl leave(TraverserContext context) { @Override public TraversalControl backRef(TraverserContext context) { - NodeZipper zipper = zipperByOriginalNode.get(context.thisNode()); - breadcrumbsByZipper.get(zipper).add(context.getBreadcrumbs()); + NodeZipper zipper = assertNotNull(zipperByOriginalNode.get(context.thisNode()), "zipper must exist for backRef node"); + assertNotNull(breadcrumbsByZipper.get(zipper), "breadcrumbs must exist for zipper").add(context.getBreadcrumbs()); if (zipper.getModificationType() == DELETE) { return CONTINUE; } @@ -347,6 +351,7 @@ public TraversalControl backRef(TraverserContext context) return zipUpToDummyRoot(zippers, stronglyConnectedTopologicallySorted, breadcrumbsByZipper, zipperByNodeAfterTraversing, allChangedNamedTypes); } + @NullUnmarked private static class RelevantZippersAndBreadcrumbs { final Multimap> zipperByParent = LinkedHashMultimap.create(); final Set> relevantZippers; @@ -437,7 +442,7 @@ private boolean zipUpToDummyRoot(List> zippers, } // we need to change all elements inside the current SCC for (GraphQLSchemaElement element : unchangedSccElements) { - NodeZipper currentZipper = nodeToZipper.get(element); + NodeZipper currentZipper = assertNotNull(nodeToZipper.get(element), "zipper must exist for element"); NodeZipper newZipper = currentZipper.withNewNode(element.copy()); nodeToZipper.put(element, newZipper); relevantZippers.updateZipper(currentZipper, newZipper); @@ -582,6 +587,7 @@ private NodeZipper moveUp( return new NodeZipper<>(newNode, newBreadcrumbs, SCHEMA_ELEMENT_ADAPTER); } + @NullUnmarked private static class ZipperWithOneParent { public NodeZipper zipper; public Breadcrumb parent; @@ -593,6 +599,7 @@ public ZipperWithOneParent(NodeZipper zipper, Breadcrumb context) { - return context.thisNode().accept(context, delegate); + return assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, delegate); } @Override @@ -133,7 +136,7 @@ private static class TraverserDelegateListVisitor implements TraverserVisitor context) { for (GraphQLTypeVisitor graphQLTypeVisitor : typeVisitors) { - TraversalControl control = context.thisNode().accept(context, graphQLTypeVisitor); + TraversalControl control = assertNotNull(context.thisNode(), "thisNode should not be null").accept(context, graphQLTypeVisitor); if (control != CONTINUE) { return control; } diff --git a/src/main/java/graphql/schema/SelectedField.java b/src/main/java/graphql/schema/SelectedField.java index e9b3dd89ce..b369dbd1fd 100644 --- a/src/main/java/graphql/schema/SelectedField.java +++ b/src/main/java/graphql/schema/SelectedField.java @@ -1,6 +1,8 @@ package graphql.schema; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Map; @@ -11,6 +13,7 @@ * interface returned via {@link DataFetchingEnvironment#getSelectionSet()} */ @PublicApi +@NullMarked public interface SelectedField { /** * @return the simple name of the selected field @@ -71,7 +74,7 @@ public interface SelectedField { /** * @return the alias of the selected field or null if not alias was used */ - String getAlias(); + @Nullable String getAlias(); /** * The result key is either the field query alias OR the field name in that preference order @@ -86,7 +89,7 @@ public interface SelectedField { * * @return the fields selected parent or null if there is not one */ - SelectedField getParentField(); + @Nullable SelectedField getParentField(); /** * @return a sub selection set (if it has any) diff --git a/src/main/java/graphql/schema/StaticDataFetcher.java b/src/main/java/graphql/schema/StaticDataFetcher.java index b9b49d9d6d..d74672b4ac 100644 --- a/src/main/java/graphql/schema/StaticDataFetcher.java +++ b/src/main/java/graphql/schema/StaticDataFetcher.java @@ -3,22 +3,25 @@ import graphql.PublicApi; import graphql.TrivialDataFetcher; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A {@link graphql.schema.DataFetcher} that always returns the same value */ @PublicApi +@NullMarked public class StaticDataFetcher implements DataFetcher, TrivialDataFetcher { - private final Object value; + private final @Nullable Object value; - public StaticDataFetcher(Object value) { + public StaticDataFetcher(@Nullable Object value) { this.value = value; } @Override - public Object get(DataFetchingEnvironment environment) { + public @Nullable Object get(DataFetchingEnvironment environment) { return value; } diff --git a/src/main/java/graphql/schema/diff/DiffCategory.java b/src/main/java/graphql/schema/diff/DiffCategory.java index 68592568cd..2b455ac09b 100644 --- a/src/main/java/graphql/schema/diff/DiffCategory.java +++ b/src/main/java/graphql/schema/diff/DiffCategory.java @@ -1,11 +1,13 @@ package graphql.schema.diff; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * A classification of difference events. */ @PublicApi +@NullMarked public enum DiffCategory { /** * The new API is missing something compared to the old API diff --git a/src/main/java/graphql/schema/diff/DiffEvent.java b/src/main/java/graphql/schema/diff/DiffEvent.java index 1b58988eae..666debee52 100644 --- a/src/main/java/graphql/schema/diff/DiffEvent.java +++ b/src/main/java/graphql/schema/diff/DiffEvent.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.language.TypeKind; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; import java.util.ArrayList; import java.util.Arrays; @@ -13,6 +15,7 @@ * This represents the events that the {@link SchemaDiff} outputs. */ @PublicApi +@NullMarked public class DiffEvent { private final DiffLevel level; @@ -86,6 +89,7 @@ public static Builder apiBreakage() { } + @NullUnmarked public static class Builder { DiffCategory category; diff --git a/src/main/java/graphql/schema/diff/DiffLevel.java b/src/main/java/graphql/schema/diff/DiffLevel.java index 00132a9834..56b700854c 100644 --- a/src/main/java/graphql/schema/diff/DiffLevel.java +++ b/src/main/java/graphql/schema/diff/DiffLevel.java @@ -1,11 +1,13 @@ package graphql.schema.diff; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * This is the level of difference between graphql APIs */ @PublicApi +@NullMarked public enum DiffLevel { /** * A simple info object coming out of the difference engine diff --git a/src/main/java/graphql/schema/diff/DiffSet.java b/src/main/java/graphql/schema/diff/DiffSet.java index 590d50bbb4..6a013d21ee 100644 --- a/src/main/java/graphql/schema/diff/DiffSet.java +++ b/src/main/java/graphql/schema/diff/DiffSet.java @@ -5,6 +5,9 @@ import graphql.GraphQL; import graphql.PublicApi; import graphql.introspection.IntrospectionQuery; +import org.jspecify.annotations.NullMarked; + +import static graphql.Assert.assertNotNull; import graphql.schema.GraphQLSchema; import java.util.Map; @@ -15,6 +18,7 @@ * {@link graphql.introspection.IntrospectionQuery}. */ @PublicApi +@NullMarked @Deprecated(since = "2023-10-04") public class DiffSet { @@ -70,6 +74,6 @@ private static Map introspect(GraphQLSchema schema) { GraphQL gql = GraphQL.newGraphQL(schema).build(); ExecutionResult result = gql.execute(IntrospectionQuery.INTROSPECTION_QUERY); Assert.assertTrue(result.getErrors().isEmpty(), "The schema has errors during Introspection"); - return result.getData(); + return assertNotNull(result.getData(), "Introspection result data should not be null"); } } diff --git a/src/main/java/graphql/schema/diff/SchemaDiffSet.java b/src/main/java/graphql/schema/diff/SchemaDiffSet.java index dd28a65f4b..99d97ea2e3 100644 --- a/src/main/java/graphql/schema/diff/SchemaDiffSet.java +++ b/src/main/java/graphql/schema/diff/SchemaDiffSet.java @@ -5,6 +5,7 @@ import graphql.GraphQL; import graphql.PublicApi; import graphql.introspection.IntrospectionQuery; +import org.jspecify.annotations.NullMarked; import graphql.introspection.IntrospectionResultToSchema; import graphql.language.Document; import graphql.parser.Parser; @@ -17,6 +18,7 @@ * Interface used to define 2 schemas that can be diffed by the {@link SchemaDiff} operation. */ @PublicApi +@NullMarked public class SchemaDiffSet { private final Document oldSchemaDoc; @@ -129,6 +131,6 @@ private static Map introspect(GraphQLSchema schema) { GraphQL gql = GraphQL.newGraphQL(schema).build(); ExecutionResult result = gql.execute(IntrospectionQuery.INTROSPECTION_QUERY); Assert.assertTrue(result.getErrors().isEmpty(), "The schema has errors during Introspection"); - return result.getData(); + return Assert.assertNotNull(result.getData(), "Introspection result data should not be null"); } } diff --git a/src/main/java/graphql/schema/diff/reporting/CapturingReporter.java b/src/main/java/graphql/schema/diff/reporting/CapturingReporter.java index b838a68975..e19a281767 100644 --- a/src/main/java/graphql/schema/diff/reporting/CapturingReporter.java +++ b/src/main/java/graphql/schema/diff/reporting/CapturingReporter.java @@ -2,6 +2,7 @@ import graphql.PublicApi; import graphql.schema.diff.DiffEvent; +import org.jspecify.annotations.NullMarked; import graphql.schema.diff.DiffLevel; import java.util.ArrayList; @@ -11,6 +12,7 @@ * A reporter that captures all the difference events as they occur */ @PublicApi +@NullMarked public class CapturingReporter implements DifferenceReporter { private final List events = new ArrayList<>(); private final List infos = new ArrayList<>(); diff --git a/src/main/java/graphql/schema/diff/reporting/ChainedReporter.java b/src/main/java/graphql/schema/diff/reporting/ChainedReporter.java index 06c0e10555..ed7e34d4c8 100644 --- a/src/main/java/graphql/schema/diff/reporting/ChainedReporter.java +++ b/src/main/java/graphql/schema/diff/reporting/ChainedReporter.java @@ -2,6 +2,7 @@ import graphql.PublicApi; import graphql.schema.diff.DiffEvent; +import org.jspecify.annotations.NullMarked; import java.util.Arrays; import java.util.List; @@ -10,6 +11,7 @@ * A reporter that chains together one or more difference reporters */ @PublicApi +@NullMarked public class ChainedReporter implements DifferenceReporter { private final List reporters; diff --git a/src/main/java/graphql/schema/diff/reporting/PrintStreamReporter.java b/src/main/java/graphql/schema/diff/reporting/PrintStreamReporter.java index acb10cdaf4..15af4d6d40 100644 --- a/src/main/java/graphql/schema/diff/reporting/PrintStreamReporter.java +++ b/src/main/java/graphql/schema/diff/reporting/PrintStreamReporter.java @@ -2,6 +2,7 @@ import graphql.PublicApi; import graphql.schema.diff.DiffEvent; +import org.jspecify.annotations.NullMarked; import graphql.schema.diff.DiffLevel; import java.io.PrintStream; @@ -12,6 +13,7 @@ * A reporter that prints its output to a PrintStream */ @PublicApi +@NullMarked public class PrintStreamReporter implements DifferenceReporter { int breakageCount = 0; diff --git a/src/main/java/graphql/schema/idl/CombinedWiringFactory.java b/src/main/java/graphql/schema/idl/CombinedWiringFactory.java index 1ff2eb4845..09aada1e95 100644 --- a/src/main/java/graphql/schema/idl/CombinedWiringFactory.java +++ b/src/main/java/graphql/schema/idl/CombinedWiringFactory.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.schema.DataFetcher; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.schema.DataFetcherFactory; import graphql.schema.GraphQLScalarType; import graphql.schema.TypeResolver; @@ -17,6 +19,7 @@ * whether it handles a type and delegates to the first one to answer yes. */ @PublicApi +@NullMarked public class CombinedWiringFactory implements WiringFactory { private final List factories; @@ -146,7 +149,7 @@ public SchemaDirectiveWiring getSchemaDirectiveWiring(SchemaDirectiveWiringEnvir } @Override - public DataFetcher getDefaultDataFetcher(FieldWiringEnvironment environment) { + public @Nullable DataFetcher getDefaultDataFetcher(FieldWiringEnvironment environment) { for (WiringFactory factory : factories) { if (factory.getDefaultDataFetcher(environment) != null) { return factory.getDefaultDataFetcher(environment); diff --git a/src/main/java/graphql/schema/idl/MapEnumValuesProvider.java b/src/main/java/graphql/schema/idl/MapEnumValuesProvider.java index 7bcd19d97d..d04b491b91 100644 --- a/src/main/java/graphql/schema/idl/MapEnumValuesProvider.java +++ b/src/main/java/graphql/schema/idl/MapEnumValuesProvider.java @@ -2,10 +2,13 @@ import graphql.Assert; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Map; @PublicApi +@NullMarked public class MapEnumValuesProvider implements EnumValuesProvider { @@ -17,7 +20,7 @@ public MapEnumValuesProvider(Map values) { } @Override - public Object getValue(String name) { + public @Nullable Object getValue(String name) { return values.get(name); } } diff --git a/src/main/java/graphql/schema/idl/NaturalEnumValuesProvider.java b/src/main/java/graphql/schema/idl/NaturalEnumValuesProvider.java index 15a99e35c0..c5913870db 100644 --- a/src/main/java/graphql/schema/idl/NaturalEnumValuesProvider.java +++ b/src/main/java/graphql/schema/idl/NaturalEnumValuesProvider.java @@ -2,11 +2,13 @@ import graphql.Assert; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * Simple EnumValuesProvided which maps the GraphQL Enum name to the Java Enum instance. */ @PublicApi +@NullMarked public class NaturalEnumValuesProvider> implements EnumValuesProvider { diff --git a/src/main/java/graphql/schema/idl/RuntimeWiring.java b/src/main/java/graphql/schema/idl/RuntimeWiring.java index b985d648f5..263a816ff6 100644 --- a/src/main/java/graphql/schema/idl/RuntimeWiring.java +++ b/src/main/java/graphql/schema/idl/RuntimeWiring.java @@ -16,7 +16,9 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.UnaryOperator; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import static graphql.Assert.assertNotNull; import static graphql.schema.visibility.DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY; @@ -27,6 +29,7 @@ * to wire together a functional {@link GraphQLSchema} */ @PublicApi +@NullMarked public class RuntimeWiring { private final Map> dataFetchers; @@ -143,7 +146,7 @@ public Map getDataFetchersForType(String typeName) { return dataFetchers.computeIfAbsent(typeName, k -> new LinkedHashMap<>()); } - public DataFetcher getDefaultDataFetcherForType(String typeName) { + public @Nullable DataFetcher getDefaultDataFetcherForType(String typeName) { return defaultDataFetchers.get(typeName); } diff --git a/src/main/java/graphql/schema/idl/SchemaDirectiveWiring.java b/src/main/java/graphql/schema/idl/SchemaDirectiveWiring.java index 1b136ae9db..36a6b90640 100644 --- a/src/main/java/graphql/schema/idl/SchemaDirectiveWiring.java +++ b/src/main/java/graphql/schema/idl/SchemaDirectiveWiring.java @@ -2,6 +2,7 @@ import graphql.PublicApi; import graphql.schema.GraphQLArgument; +import org.jspecify.annotations.NullMarked; import graphql.schema.GraphQLEnumType; import graphql.schema.GraphQLEnumValueDefinition; import graphql.schema.GraphQLFieldDefinition; @@ -27,6 +28,7 @@ * */ @PublicApi +@NullMarked public interface SchemaDirectiveWiring { /** diff --git a/src/main/java/graphql/schema/idl/SchemaDirectiveWiringEnvironment.java b/src/main/java/graphql/schema/idl/SchemaDirectiveWiringEnvironment.java index 538582ed32..aa33b13f06 100644 --- a/src/main/java/graphql/schema/idl/SchemaDirectiveWiringEnvironment.java +++ b/src/main/java/graphql/schema/idl/SchemaDirectiveWiringEnvironment.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.language.NamedNode; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.language.NodeParentTree; import graphql.schema.DataFetcher; import graphql.schema.GraphQLAppliedDirective; @@ -21,6 +23,7 @@ * @param the type of the object in play */ @PublicApi +@NullMarked public interface SchemaDirectiveWiringEnvironment { /** @@ -43,7 +46,7 @@ public interface SchemaDirectiveWiringEnvironment */ @PublicApi +@NullMarked public class SchemaGenerator { private final SchemaTypeChecker typeChecker = new SchemaTypeChecker(); diff --git a/src/main/java/graphql/schema/idl/SchemaPrinter.java b/src/main/java/graphql/schema/idl/SchemaPrinter.java index b199926588..db9e23f327 100644 --- a/src/main/java/graphql/schema/idl/SchemaPrinter.java +++ b/src/main/java/graphql/schema/idl/SchemaPrinter.java @@ -6,6 +6,8 @@ import graphql.GraphQLContext; import graphql.PublicApi; import graphql.execution.ValuesResolver; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.language.AstPrinter; import graphql.language.Comment; import graphql.language.Description; @@ -77,6 +79,7 @@ * This can print an in memory GraphQL schema back to a logical schema definition */ @PublicApi +@NullMarked public class SchemaPrinter { /** * This predicate excludes all directives which are specified by the GraphQL Specification. @@ -630,7 +633,7 @@ private SchemaElementPrinter interfacePrinter() { } if (shouldPrintAsAst(type.getDefinition())) { - printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + printAsAst(out, Assert.assertNotNull(type.getDefinition()), type.getExtensionDefinitions()); } else { printComments(out, type, ""); if (type.getInterfaces().isEmpty()) { @@ -666,7 +669,7 @@ private SchemaElementPrinter unionPrinter() { Comparator comparator = getComparator(GraphQLUnionType.class, GraphQLOutputType.class); if (shouldPrintAsAst(type.getDefinition())) { - printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + printAsAst(out, Assert.assertNotNull(type.getDefinition()), type.getExtensionDefinitions()); } else { printComments(out, type, ""); out.format("union %s%s = ", type.getName(), directivesString(GraphQLUnionType.class, type)); @@ -704,7 +707,7 @@ private SchemaElementPrinter objectPrinter() { return; } if (shouldPrintAsAst(type.getDefinition())) { - printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + printAsAst(out, Assert.assertNotNull(type.getDefinition()), type.getExtensionDefinitions()); } else { printComments(out, type, ""); if (type.getInterfaces().isEmpty()) { @@ -737,7 +740,7 @@ private SchemaElementPrinter inputObjectPrinter() { return; } if (shouldPrintAsAst(type.getDefinition())) { - printAsAst(out, type.getDefinition(), type.getExtensionDefinitions()); + printAsAst(out, Assert.assertNotNull(type.getDefinition()), type.getExtensionDefinitions()); } else { printComments(out, type, ""); @@ -777,7 +780,7 @@ private SchemaElementPrinter inputObjectPrinter() { * * @return true if we should print using AST nodes */ - private boolean shouldPrintAsAst(TypeDefinition definition) { + private boolean shouldPrintAsAst(@Nullable TypeDefinition definition) { return options.isUseAstDefinitions() && definition != null; } @@ -788,7 +791,7 @@ private boolean shouldPrintAsAst(TypeDefinition definition) { * * @return true if we should print using AST nodes */ - private boolean shouldPrintAsAst(SchemaDefinition definition) { + private boolean shouldPrintAsAst(@Nullable SchemaDefinition definition) { return options.isUseAstDefinitions() && definition != null; } @@ -819,8 +822,8 @@ private void printAsAst(PrintWriter out, TypeDefinition definition, List extensions) { - out.printf("%s\n", AstPrinter.printAst(definition)); + private void printAsAst(PrintWriter out, @Nullable SchemaDefinition definition, List extensions) { + out.printf("%s\n", AstPrinter.printAst(Assert.assertNotNull(definition, "definition should not be null - guard with shouldPrintAsAst"))); if (extensions != null) { for (SchemaExtensionDefinition extension : extensions) { out.printf("\n%s\n", AstPrinter.printAst(extension)); @@ -896,7 +899,7 @@ String argsString(List arguments) { return argsString(null, arguments); } - String argsString(Class parent, List arguments) { + String argsString(@Nullable Class parent, List arguments) { boolean hasAstDefinitionComments = arguments.stream().anyMatch(this::hasAstDefinitionComments); boolean hasDescriptions = arguments.stream().anyMatch(this::hasDescription); String halfPrefix = hasAstDefinitionComments || hasDescriptions ? " " : ""; @@ -1062,7 +1065,7 @@ private List addOrUpdateDeprecatedDirectiveIfNeeded(Gra return directives; } - private GraphQLAppliedDirective createDeprecatedDirective(String reason) { + private GraphQLAppliedDirective createDeprecatedDirective(@Nullable String reason) { GraphQLAppliedDirectiveArgument arg = GraphQLAppliedDirectiveArgument.newArgument() .name("reason") .valueProgrammatic(reason) @@ -1074,7 +1077,7 @@ private GraphQLAppliedDirective createDeprecatedDirective(String reason) { .build(); } - private List updateDeprecatedDirective(List directives, String reason) { + private List updateDeprecatedDirective(List directives, @Nullable String reason) { GraphQLAppliedDirectiveArgument newArg = GraphQLAppliedDirectiveArgument.newArgument() .name("reason") .valueProgrammatic(reason) @@ -1084,7 +1087,8 @@ private List updateDeprecatedDirective(List { if (isDeprecatedDirective(d)) { // Don't include reason is deliberately replaced with NOT_SET, for example in Anonymizer - if (d.getArgument("reason").getArgumentValue() != InputValueWithState.NOT_SET) { + GraphQLAppliedDirectiveArgument reasonArg = d.getArgument("reason"); + if (reasonArg != null && reasonArg.getArgumentValue() != InputValueWithState.NOT_SET) { return d.transform(builder -> builder.argument(newArg)); } } @@ -1092,7 +1096,7 @@ private List updateDeprecatedDirective(List lines = Arrays.asList(descriptionText.split("\n")); if (options.isDescriptionsAsHashComments()) { printMultiLineHashDescription(out, prefix, lines); @@ -1226,7 +1230,7 @@ private void printComments(PrintWriter out, Object graphQLType, String prefix) { if (options.isIncludeAstDefinitionComments()) { String commentsText = getAstDefinitionComments(graphQLType); - if (!isNullOrEmpty(commentsText)) { + if (commentsText != null && !commentsText.isEmpty()) { List lines = Arrays.asList(commentsText.split("\n")); if (!lines.isEmpty()) { printMultiLineHashDescription(out, prefix, lines); @@ -1259,7 +1263,7 @@ private boolean hasAstDefinitionComments(Object commentHolder) { return !isNullOrEmpty(comments); } - private String getAstDefinitionComments(Object commentHolder) { + private @Nullable String getAstDefinitionComments(Object commentHolder) { if (commentHolder instanceof GraphQLObjectType) { GraphQLObjectType type = (GraphQLObjectType) commentHolder; return comments(ofNullable(type.getDefinition()).map(ObjectTypeDefinition::getComments).orElse(null)); @@ -1301,7 +1305,7 @@ private String getAstDefinitionComments(Object commentHolder) { } } - private String comments(List comments) { + private @Nullable String comments(@Nullable List comments) { if (comments == null || comments.isEmpty()) { return null; } @@ -1314,7 +1318,7 @@ private boolean hasDescription(Object descriptionHolder) { return !isNullOrEmpty(description); } - private String getDescription(Object descriptionHolder) { + private @Nullable String getDescription(Object descriptionHolder) { if (descriptionHolder instanceof GraphQLObjectType) { GraphQLObjectType type = (GraphQLObjectType) descriptionHolder; return description(type.getDescription(), ofNullable(type.getDefinition()).map(ObjectTypeDefinition::getDescription).orElse(null)); @@ -1356,7 +1360,7 @@ private String getDescription(Object descriptionHolder) { } } - String description(String runtimeDescription, Description descriptionAst) { + @Nullable String description(@Nullable String runtimeDescription, @Nullable Description descriptionAst) { // // 95% of the time if the schema was built from SchemaGenerator then the runtime description is the only description // So the other code here is a really defensive way to get the description @@ -1370,7 +1374,7 @@ String description(String runtimeDescription, Description descriptionAst) { return descriptionText; } - private Comparator getComparator(Class parentType, Class elementType) { + private Comparator getComparator(@Nullable Class parentType, @Nullable Class elementType) { GraphqlTypeComparatorEnvironment environment = GraphqlTypeComparatorEnvironment.newEnvironment() .parentType(parentType) .elementType(elementType) @@ -1385,7 +1389,7 @@ private static String trimNewLineChars(String s) { return s; } - private static boolean isNullOrEmpty(String s) { + private static boolean isNullOrEmpty(@Nullable String s) { return s == null || s.isEmpty(); } } diff --git a/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java b/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java index e4eb79799e..3c13305924 100644 --- a/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java +++ b/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java @@ -2,6 +2,9 @@ import graphql.PublicApi; import graphql.schema.DataFetcher; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; import graphql.schema.idl.errors.StrictModeWiringException; @@ -20,6 +23,7 @@ * This is used by {@link RuntimeWiring} to wire together a functional {@link GraphQLSchema} */ @PublicApi +@NullMarked public class TypeRuntimeWiring { private final static AtomicBoolean DEFAULT_STRICT_MODE = new AtomicBoolean(true); @@ -43,12 +47,12 @@ public static boolean getStrictModeJvmWide() { } private final String typeName; - private final DataFetcher defaultDataFetcher; + private final @Nullable DataFetcher defaultDataFetcher; private final Map fieldDataFetchers; - private final TypeResolver typeResolver; - private final EnumValuesProvider enumValuesProvider; + private final @Nullable TypeResolver typeResolver; + private final @Nullable EnumValuesProvider enumValuesProvider; - private TypeRuntimeWiring(String typeName, DataFetcher defaultDataFetcher, Map fieldDataFetchers, TypeResolver typeResolver, EnumValuesProvider enumValuesProvider) { + private TypeRuntimeWiring(String typeName, @Nullable DataFetcher defaultDataFetcher, Map fieldDataFetchers, @Nullable TypeResolver typeResolver, @Nullable EnumValuesProvider enumValuesProvider) { this.typeName = typeName; this.defaultDataFetcher = defaultDataFetcher; this.fieldDataFetchers = fieldDataFetchers; @@ -88,18 +92,19 @@ public Map getFieldDataFetchers() { return fieldDataFetchers; } - public DataFetcher getDefaultDataFetcher() { + public @Nullable DataFetcher getDefaultDataFetcher() { return defaultDataFetcher; } - public TypeResolver getTypeResolver() { + public @Nullable TypeResolver getTypeResolver() { return typeResolver; } - public EnumValuesProvider getEnumValuesProvider() { + public @Nullable EnumValuesProvider getEnumValuesProvider() { return enumValuesProvider; } + @NullUnmarked public static class Builder { private final Map fieldDataFetchers = new LinkedHashMap<>(); private String typeName; diff --git a/src/main/java/graphql/schema/idl/errors/SchemaProblem.java b/src/main/java/graphql/schema/idl/errors/SchemaProblem.java index 817a54d5b5..c18b2d8f71 100644 --- a/src/main/java/graphql/schema/idl/errors/SchemaProblem.java +++ b/src/main/java/graphql/schema/idl/errors/SchemaProblem.java @@ -4,6 +4,7 @@ import graphql.GraphQLException; import graphql.PublicApi; import graphql.schema.idl.SchemaParser; +import org.jspecify.annotations.NullMarked; import java.util.ArrayList; import java.util.List; @@ -14,6 +15,7 @@ * exception as a list of {@link GraphQLError}s */ @PublicApi +@NullMarked public class SchemaProblem extends GraphQLException { private final List errors; diff --git a/src/main/java/graphql/schema/idl/errors/StrictModeWiringException.java b/src/main/java/graphql/schema/idl/errors/StrictModeWiringException.java index 4c92bffd6d..2f94660c32 100644 --- a/src/main/java/graphql/schema/idl/errors/StrictModeWiringException.java +++ b/src/main/java/graphql/schema/idl/errors/StrictModeWiringException.java @@ -2,6 +2,7 @@ import graphql.GraphQLException; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeRuntimeWiring; @@ -10,6 +11,7 @@ * something gets redefined. */ @PublicApi +@NullMarked public class StrictModeWiringException extends GraphQLException { public StrictModeWiringException(String msg) { super(msg); diff --git a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java index 7995d6fab1..b926584098 100644 --- a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java +++ b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java @@ -3,6 +3,7 @@ import com.google.common.collect.ImmutableList; import graphql.PublicApi; import graphql.introspection.Introspection; +import org.jspecify.annotations.NullMarked; import graphql.schema.GraphQLEnumType; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLFieldsContainer; @@ -36,6 +37,7 @@ * Transforms a schema by applying a visibility predicate to every field. */ @PublicApi +@NullMarked public class FieldVisibilitySchemaTransformation { private final VisibleFieldPredicate visibleFieldPredicate; diff --git a/src/main/java/graphql/schema/transform/VisibleFieldPredicateEnvironment.java b/src/main/java/graphql/schema/transform/VisibleFieldPredicateEnvironment.java index 54ee176019..3aabe56419 100644 --- a/src/main/java/graphql/schema/transform/VisibleFieldPredicateEnvironment.java +++ b/src/main/java/graphql/schema/transform/VisibleFieldPredicateEnvironment.java @@ -2,12 +2,14 @@ import graphql.PublicApi; import graphql.schema.GraphQLNamedSchemaElement; +import org.jspecify.annotations.NullMarked; import graphql.schema.GraphQLSchemaElement; /** * Container to pass additional context about a schema element (ie., field) to {@link VisibleFieldPredicate}. */ @PublicApi +@NullMarked public interface VisibleFieldPredicateEnvironment { GraphQLNamedSchemaElement getSchemaElement(); diff --git a/src/main/java/graphql/schema/usage/SchemaUsage.java b/src/main/java/graphql/schema/usage/SchemaUsage.java index 32ac6c42c6..df9dcdd1c3 100644 --- a/src/main/java/graphql/schema/usage/SchemaUsage.java +++ b/src/main/java/graphql/schema/usage/SchemaUsage.java @@ -3,6 +3,7 @@ import com.google.common.collect.ImmutableMap; import graphql.Internal; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import graphql.introspection.Introspection; import graphql.schema.GraphQLDirective; import graphql.schema.GraphQLInterfaceType; @@ -36,6 +37,7 @@ * is to find the types and directives in a schema that could in theory be removed because they are not useful. */ @PublicApi +@NullMarked public class SchemaUsage { private final Map fieldReferenceCounts; private final Map inputFieldReferenceCounts; diff --git a/src/main/java/graphql/schema/usage/SchemaUsageSupport.java b/src/main/java/graphql/schema/usage/SchemaUsageSupport.java index f51bb0429d..72d05beae6 100644 --- a/src/main/java/graphql/schema/usage/SchemaUsageSupport.java +++ b/src/main/java/graphql/schema/usage/SchemaUsageSupport.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.schema.GraphQLAppliedDirective; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.schema.GraphQLAppliedDirectiveArgument; import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLDirective; @@ -31,6 +33,7 @@ import static graphql.util.TraversalControl.CONTINUE; @PublicApi +@NullMarked public class SchemaUsageSupport { /** @@ -84,9 +87,9 @@ public TraversalControl visitGraphQLArgument(GraphQLArgument node, TraverserCont GraphQLSchemaElement parentElement = context.getParentNode(); if (parentElement instanceof GraphQLFieldDefinition) { - parentElement = context.getParentContext().getParentNode(); + parentElement = assertNotNull(context.getParentContext(), "parentContext should not be null").getParentNode(); } - recordBackReference(inputType, parentElement); + recordBackReference(inputType, assertNotNull(parentElement, "parentElement should not be null")); return CONTINUE; } @@ -97,9 +100,9 @@ public TraversalControl visitGraphQLAppliedDirectiveArgument(GraphQLAppliedDirec GraphQLSchemaElement parentElement = context.getParentNode(); if (parentElement instanceof GraphQLAppliedDirective) { - parentElement = context.getParentContext().getParentNode(); + parentElement = assertNotNull(context.getParentContext(), "parentContext should not be null").getParentNode(); } - recordBackReference(inputType, parentElement); + recordBackReference(inputType, assertNotNull(parentElement, "parentElement should not be null")); return CONTINUE; } @@ -109,7 +112,7 @@ public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition node, builder.fieldReferenceCounts.compute(fieldType.getName(), incCount()); builder.outputFieldReferenceCounts.compute(fieldType.getName(), incCount()); - recordBackReference(fieldType, context.getParentNode()); + recordBackReference(fieldType, assertNotNull(context.getParentNode(), "parentNode should not be null")); return CONTINUE; } @@ -120,42 +123,46 @@ public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField nod builder.fieldReferenceCounts.compute(fieldType.getName(), incCount()); builder.inputFieldReferenceCounts.compute(fieldType.getName(), incCount()); - recordBackReference(fieldType, context.getParentNode()); + recordBackReference(fieldType, assertNotNull(context.getParentNode(), "parentNode should not be null")); return CONTINUE; } @Override public TraversalControl visitGraphQLDirective(GraphQLDirective directive, TraverserContext context) { - GraphQLSchemaElement parentElement = visitDirectiveLike(context, directive.getName()); - recordBackReference(directive, parentElement); + @Nullable GraphQLSchemaElement parentElement = visitDirectiveLike(context, directive.getName()); + if (parentElement != null) { + recordBackReference(directive, parentElement); + } return CONTINUE; } @Override public TraversalControl visitGraphQLAppliedDirective(GraphQLAppliedDirective appliedDirective, TraverserContext context) { - GraphQLSchemaElement parentElement = visitDirectiveLike(context, appliedDirective.getName()); - recordBackReference(appliedDirective, parentElement); + @Nullable GraphQLSchemaElement parentElement = visitDirectiveLike(context, appliedDirective.getName()); + if (parentElement != null) { + recordBackReference(appliedDirective, parentElement); + } return CONTINUE; } - private GraphQLSchemaElement visitDirectiveLike(TraverserContext context, String directiveName) { - GraphQLSchemaElement parentElement = context.getParentNode(); + private @Nullable GraphQLSchemaElement visitDirectiveLike(TraverserContext context, String directiveName) { + @Nullable GraphQLSchemaElement parentElement = context.getParentNode(); if (parentElement != null) { // a null parent is a directive definition // we record a count if the directive is applied to something - not just defined builder.directiveReferenceCount.compute(directiveName, incCount()); } if (parentElement instanceof GraphQLArgument) { - context = context.getParentContext(); + context = assertNotNull(context.getParentContext(), "parentContext should not be null"); parentElement = context.getParentNode(); } if (parentElement instanceof GraphQLFieldDefinition) { - context = context.getParentContext(); + context = assertNotNull(context.getParentContext(), "parentContext should not be null"); parentElement = context.getParentNode(); } if (parentElement instanceof GraphQLInputObjectField) { - context = context.getParentContext(); + context = assertNotNull(context.getParentContext(), "parentContext should not be null"); parentElement = context.getParentNode(); } return parentElement; diff --git a/src/main/java/graphql/schema/validation/SchemaValidationErrorClassification.java b/src/main/java/graphql/schema/validation/SchemaValidationErrorClassification.java index e8a7e6b08a..a31450ca8c 100644 --- a/src/main/java/graphql/schema/validation/SchemaValidationErrorClassification.java +++ b/src/main/java/graphql/schema/validation/SchemaValidationErrorClassification.java @@ -2,6 +2,7 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** @@ -9,6 +10,7 @@ * and all the error classifications implement this interface. */ @PublicApi +@NullMarked public interface SchemaValidationErrorClassification { } diff --git a/src/main/java/graphql/schema/visibility/BlockedFields.java b/src/main/java/graphql/schema/visibility/BlockedFields.java index 937d029d83..94336b96a3 100644 --- a/src/main/java/graphql/schema/visibility/BlockedFields.java +++ b/src/main/java/graphql/schema/visibility/BlockedFields.java @@ -2,6 +2,9 @@ import graphql.Internal; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import graphql.collect.ImmutableKit; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLFieldsContainer; @@ -21,6 +24,7 @@ * Remember that graphql type and fields names MUST be inside the name space "[_A-Za-z][_0-9A-Za-z]*" */ @PublicApi +@NullMarked public class BlockedFields implements GraphqlFieldVisibility { private final List patterns; @@ -40,7 +44,7 @@ public List getFieldDefinitions(GraphQLFieldsContainer f } @Override - public GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { + public @Nullable GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { GraphQLFieldDefinition fieldDefinition = fieldsContainer.getFieldDefinition(fieldName); if (fieldDefinition != null) { if (block(mkFQN(fieldsContainer.getName(), fieldDefinition.getName()))) { @@ -57,7 +61,7 @@ public List getFieldDefinitions(GraphQLInputFieldsConta } @Override - public GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { + public @Nullable GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { GraphQLInputObjectField fieldDefinition = fieldsContainer.getFieldDefinition(fieldName); if (fieldDefinition != null) { if (block(mkFQN(fieldsContainer.getName(), fieldDefinition.getName()))) { @@ -84,6 +88,7 @@ public static Builder newBlock() { return new Builder(); } + @NullUnmarked public static class Builder { private final List patterns = new ArrayList<>(); diff --git a/src/main/java/graphql/schema/visibility/DefaultGraphqlFieldVisibility.java b/src/main/java/graphql/schema/visibility/DefaultGraphqlFieldVisibility.java index d918d818ff..645c3e4174 100644 --- a/src/main/java/graphql/schema/visibility/DefaultGraphqlFieldVisibility.java +++ b/src/main/java/graphql/schema/visibility/DefaultGraphqlFieldVisibility.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.schema.GraphQLFieldDefinition; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.schema.GraphQLFieldsContainer; import graphql.schema.GraphQLInputFieldsContainer; import graphql.schema.GraphQLInputObjectField; @@ -12,6 +14,7 @@ * The default field visibility of graphql-java is that everything is visible */ @PublicApi +@NullMarked public class DefaultGraphqlFieldVisibility implements GraphqlFieldVisibility { public static final DefaultGraphqlFieldVisibility DEFAULT_FIELD_VISIBILITY = new DefaultGraphqlFieldVisibility(); @@ -22,7 +25,7 @@ public List getFieldDefinitions(GraphQLFieldsContainer f } @Override - public GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { + public @Nullable GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { return fieldsContainer.getFieldDefinition(fieldName); } @@ -32,7 +35,7 @@ public List getFieldDefinitions(GraphQLInputFieldsConta } @Override - public GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { + public @Nullable GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { return fieldsContainer.getFieldDefinition(fieldName); } } \ No newline at end of file diff --git a/src/main/java/graphql/schema/visibility/GraphqlFieldVisibility.java b/src/main/java/graphql/schema/visibility/GraphqlFieldVisibility.java index 91a9323280..20780b205b 100644 --- a/src/main/java/graphql/schema/visibility/GraphqlFieldVisibility.java +++ b/src/main/java/graphql/schema/visibility/GraphqlFieldVisibility.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.schema.GraphQLFieldDefinition; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.schema.GraphQLFieldsContainer; import graphql.schema.GraphQLInputFieldsContainer; import graphql.schema.GraphQLInputObjectField; @@ -14,6 +16,7 @@ * interface and reduce specific field visibility. */ @PublicApi +@NullMarked public interface GraphqlFieldVisibility { /** @@ -33,7 +36,7 @@ public interface GraphqlFieldVisibility { * * @return a {@link graphql.schema.GraphQLFieldDefinition} or null if it's not visible */ - GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName); + @Nullable GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName); /** @@ -55,7 +58,7 @@ default List getFieldDefinitions(GraphQLInputFieldsCont * * @return a {@link graphql.schema.GraphQLInputObjectField} or null if it's not visible */ - default GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { + default @Nullable GraphQLInputObjectField getFieldDefinition(GraphQLInputFieldsContainer fieldsContainer, String fieldName) { return fieldsContainer.getFieldDefinition(fieldName); } diff --git a/src/main/java/graphql/schema/visibility/NoIntrospectionGraphqlFieldVisibility.java b/src/main/java/graphql/schema/visibility/NoIntrospectionGraphqlFieldVisibility.java index 62aa16bbe0..2726cdc43e 100644 --- a/src/main/java/graphql/schema/visibility/NoIntrospectionGraphqlFieldVisibility.java +++ b/src/main/java/graphql/schema/visibility/NoIntrospectionGraphqlFieldVisibility.java @@ -2,6 +2,8 @@ import graphql.PublicApi; import graphql.schema.GraphQLFieldDefinition; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.schema.GraphQLFieldsContainer; import java.util.List; @@ -17,6 +19,7 @@ * can be used instead */ @PublicApi +@NullMarked @Deprecated(since = "2024-03-16") public class NoIntrospectionGraphqlFieldVisibility implements GraphqlFieldVisibility { @@ -36,7 +39,7 @@ public List getFieldDefinitions(GraphQLFieldsContainer f } @Override - public GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { + public @Nullable GraphQLFieldDefinition getFieldDefinition(GraphQLFieldsContainer fieldsContainer, String fieldName) { return blockedFields.getFieldDefinition(fieldsContainer, fieldName); } diff --git a/src/main/java/graphql/schema/visitor/GraphQLSchemaTraversalControl.java b/src/main/java/graphql/schema/visitor/GraphQLSchemaTraversalControl.java index 7020f088cc..5becbac153 100644 --- a/src/main/java/graphql/schema/visitor/GraphQLSchemaTraversalControl.java +++ b/src/main/java/graphql/schema/visitor/GraphQLSchemaTraversalControl.java @@ -1,7 +1,10 @@ package graphql.schema.visitor; import graphql.PublicApi; +import static graphql.Assert.assertNotNull; import graphql.schema.GraphQLSchemaElement; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import graphql.util.TraversalControl; import graphql.util.TraverserContext; import graphql.util.TreeTransformerUtil; @@ -12,8 +15,9 @@ * or {@link GraphQLSchemaVisitorEnvironment#changeNode(GraphQLSchemaElement)} say */ @PublicApi +@NullMarked public class GraphQLSchemaTraversalControl { - private final GraphQLSchemaElement element; + private final @Nullable GraphQLSchemaElement element; private final Control control; enum Control { @@ -39,12 +43,12 @@ public TraversalControl toTraversalControl() { static final GraphQLSchemaTraversalControl QUIT = new GraphQLSchemaTraversalControl(Control.QUIT, null); static final GraphQLSchemaTraversalControl DELETE = new GraphQLSchemaTraversalControl(Control.DELETE, null); - GraphQLSchemaTraversalControl(Control control, GraphQLSchemaElement element) { + GraphQLSchemaTraversalControl(Control control, @Nullable GraphQLSchemaElement element) { this.element = element; this.control = control; } - GraphQLSchemaElement getElement() { + @Nullable GraphQLSchemaElement getElement() { return element; } @@ -68,13 +72,13 @@ TraversalControl toTraversalControl(TraverserContext conte TreeTransformerUtil.deleteNode(context); } if (control == Control.CHANGE) { - TreeTransformerUtil.changeNode(context, element); + TreeTransformerUtil.changeNode(context, assertNotNull(element, "element should not be null for CHANGE")); } if (control == Control.INSERT_AFTER) { - TreeTransformerUtil.insertAfter(context, element); + TreeTransformerUtil.insertAfter(context, assertNotNull(element, "element should not be null for INSERT_AFTER")); } if (control == Control.INSERT_BEFORE) { - TreeTransformerUtil.insertAfter(context, element); + TreeTransformerUtil.insertAfter(context, assertNotNull(element, "element should not be null for INSERT_BEFORE")); } return TraversalControl.CONTINUE; } diff --git a/src/main/java/graphql/util/Anonymizer.java b/src/main/java/graphql/util/Anonymizer.java index a4929f9dd4..d0e6aa38f8 100644 --- a/src/main/java/graphql/util/Anonymizer.java +++ b/src/main/java/graphql/util/Anonymizer.java @@ -166,7 +166,7 @@ public static AnonymizeResult anonymizeSchemaAndQueries(GraphQLSchema schema, Li @Override public TraversalControl visitGraphQLTypeReference(GraphQLTypeReference graphQLTypeReference, TraverserContext context) { GraphQLNamedSchemaElement type = (GraphQLNamedSchemaElement) schema.getType(graphQLTypeReference.getName()); - String newName = newNameMap.get(type); + String newName = assertNotNull(newNameMap.get(type)); GraphQLTypeReference newReference = GraphQLTypeReference.typeRef(newName); return changeNode(context, newReference); } @@ -404,7 +404,7 @@ private static Value replaceValue(Value valueLiteral, GraphQLInputType argType, for (ObjectField objectField : objectFields) { String objectFieldName = objectField.getName(); Value objectFieldValue = objectField.getValue(); - GraphQLInputObjectField inputObjectTypeField = inputObjectType.getField(objectFieldName); + GraphQLInputObjectField inputObjectTypeField = assertNotNull(inputObjectType.getField(objectFieldName)); GraphQLInputType fieldType = unwrapNonNullAs(inputObjectTypeField.getType()); ObjectField newObjectField = objectField.transform(builder -> { builder.name(newNameMap.get(inputObjectTypeField)); @@ -480,7 +480,7 @@ public TraversalControl visitGraphQLArgument(GraphQLArgument graphQLArgument, Tr } GraphQLFieldDefinition fieldDefinition = (GraphQLFieldDefinition) parentNode; String fieldName = fieldDefinition.getName(); - GraphQLImplementingType implementingType = (GraphQLImplementingType) context.getParentContext().getParentNode(); + GraphQLImplementingType implementingType = (GraphQLImplementingType) assertNotNull(assertNotNull(context.getParentContext()).getParentNode()); Set matchingInterfaceFieldDefinitions = getSameFields(fieldName, implementingType.getName(), interfaceToImplementations, schema); String newName; if (matchingInterfaceFieldDefinitions.size() == 0) { @@ -565,7 +565,7 @@ public TraversalControl visitGraphQLEnumValueDefinition(GraphQLEnumValueDefiniti @Override public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition graphQLFieldDefinition, TraverserContext context) { String fieldName = graphQLFieldDefinition.getName(); - GraphQLImplementingType parentNode = (GraphQLImplementingType) context.getParentNode(); + GraphQLImplementingType parentNode = (GraphQLImplementingType) assertNotNull(context.getParentNode()); Set sameFields = getSameFields(fieldName, parentNode.getName(), interfaceToImplementations, schema); String newName; if (sameFields.size() == 0) { @@ -793,12 +793,12 @@ public TraversalControl visitArgument(QueryVisitorFieldArgumentEnvironment envir AtomicInteger defaultStringValueCounter = new AtomicInteger(1); AtomicInteger defaultIntValueCounter = new AtomicInteger(1); - Document newDocument = (Document) astTransformer.transform(document, new NodeVisitorStub() { + Document newDocument = (Document) assertNotNull(astTransformer.transform(document, new NodeVisitorStub() { @Override public TraversalControl visitDirective(Directive directive, TraverserContext context) { String newName = assertNotNull(astNodeToNewName.get(directive)); - GraphQLDirective directiveDefinition = schema.getDirective(directive.getName()); + GraphQLDirective directiveDefinition = assertNotNull(schema.getDirective(directive.getName())); context.setVar(GraphQLDirective.class, directiveDefinition); return changeNode(context, directive.transform(builder -> builder.name(newName))); } @@ -864,7 +864,7 @@ public TraversalControl visitVariableReference(VariableReference node, Traverser public TraversalControl visitFragmentDefinition(FragmentDefinition node, TraverserContext context) { String newName = assertNotNull(astNodeToNewName.get(node)); GraphQLType currentCondition = assertNotNull(schema.getType(node.getTypeCondition().getName())); - String newCondition = newNames.get(currentCondition); + String newCondition = assertNotNull(newNames.get(currentCondition), "newCondition should not be null"); return changeNode(context, node.transform(builder -> builder.name(newName).typeCondition(new TypeName(newCondition)))); } @@ -873,7 +873,7 @@ public TraversalControl visitInlineFragment(InlineFragment node, TraverserContex TypeName typeCondition = node.getTypeCondition(); if (typeCondition != null) { GraphQLType currentCondition = assertNotNull(schema.getType(typeCondition.getName())); - String newCondition = newNames.get(currentCondition); + String newCondition = assertNotNull(newNames.get(currentCondition), "newCondition should not be null"); return changeNode(context, node.transform(builder -> builder.typeCondition(new TypeName(newCondition)))); } return TraversalControl.CONTINUE; @@ -890,18 +890,18 @@ public TraversalControl visitArgument(Argument argument, TraverserContext GraphQLArgument graphQLArgumentDefinition; // An argument is either from a applied query directive or from a field if (context.getVarFromParents(GraphQLDirective.class) != null) { - GraphQLDirective directiveDefinition = context.getVarFromParents(GraphQLDirective.class); + GraphQLDirective directiveDefinition = assertNotNull(context.getVarFromParents(GraphQLDirective.class)); graphQLArgumentDefinition = directiveDefinition.getArgument(argument.getName()); } else { GraphQLFieldDefinition graphQLFieldDefinition = assertNotNull(context.getVarFromParents(GraphQLFieldDefinition.class)); graphQLArgumentDefinition = graphQLFieldDefinition.getArgument(argument.getName()); } - GraphQLInputType argumentType = graphQLArgumentDefinition.getType(); + GraphQLInputType argumentType = assertNotNull(graphQLArgumentDefinition).getType(); String newName = assertNotNull(astNodeToNewName.get(argument)); Value newValue = replaceValue(argument.getValue(), argumentType, newNames, defaultStringValueCounter, defaultIntValueCounter); return changeNode(context, argument.transform(builder -> builder.name(newName).value(newValue))); } - }); + })); return AstPrinter.printAstCompact(newDocument); } diff --git a/src/main/java/graphql/util/NodeAdapter.java b/src/main/java/graphql/util/NodeAdapter.java index b416d5c2cb..7dd1bc72ae 100644 --- a/src/main/java/graphql/util/NodeAdapter.java +++ b/src/main/java/graphql/util/NodeAdapter.java @@ -1,6 +1,7 @@ package graphql.util; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; import java.util.Map; @@ -12,6 +13,7 @@ * @param the generic type of object */ @PublicApi +@NullMarked public interface NodeAdapter { Map> getNamedChildren(T node); diff --git a/src/main/java/graphql/util/NodeLocation.java b/src/main/java/graphql/util/NodeLocation.java index 8a1f9f10bc..59de9b1fab 100644 --- a/src/main/java/graphql/util/NodeLocation.java +++ b/src/main/java/graphql/util/NodeLocation.java @@ -1,6 +1,8 @@ package graphql.util; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Objects; @@ -10,12 +12,13 @@ * Can be an index or a name with an index. */ @PublicApi +@NullMarked public class NodeLocation { - private final String name; + private final @Nullable String name; private final int index; - public NodeLocation(String name, int index) { + public NodeLocation(@Nullable String name, int index) { this.name = name; this.index = index; } @@ -23,7 +26,7 @@ public NodeLocation(String name, int index) { /** * @return the name or null if there is no name */ - public String getName() { + public @Nullable String getName() { return name; } @@ -40,7 +43,7 @@ public String toString() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/src/main/java/graphql/util/NodeMultiZipper.java b/src/main/java/graphql/util/NodeMultiZipper.java index 72801a920d..a615a41ad6 100644 --- a/src/main/java/graphql/util/NodeMultiZipper.java +++ b/src/main/java/graphql/util/NodeMultiZipper.java @@ -2,6 +2,8 @@ import com.google.common.collect.ImmutableList; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -18,6 +20,7 @@ import static graphql.util.NodeZipper.ModificationType.REPLACE; @PublicApi +@NullMarked public class NodeMultiZipper { private final T commonRoot; @@ -33,7 +36,7 @@ public NodeMultiZipper(T commonRoot, List> zippers, NodeAdapter /* * constructor without defensive copy of the zippers */ - private NodeMultiZipper(T commonRoot, List> zippers, NodeAdapter nodeAdapter, Object dummy) { + private NodeMultiZipper(T commonRoot, List> zippers, NodeAdapter nodeAdapter, @Nullable Object dummy) { this.commonRoot = assertNotNull(commonRoot); this.zippers = ImmutableList.copyOf(zippers); this.nodeAdapter = nodeAdapter; @@ -49,7 +52,7 @@ public static NodeMultiZipper newNodeMultiZipperTrusted(T commonRoot, Lis /** * @return can be null if the root node is marked as deleted */ - public T toRootNode() { + public @Nullable T toRootNode() { if (zippers.size() == 0) { return commonRoot; } @@ -88,7 +91,7 @@ public int size() { return zippers.size(); } - public NodeZipper getZipperForNode(T node) { + public @Nullable NodeZipper getZipperForNode(T node) { return FpKit.findOneOrNull(zippers, zipper -> zipper.getCurNode() == node); } @@ -125,7 +128,7 @@ private List> getDeepestZippers(Set> zippers) { Map>> grouped = FpKit.groupingBy(zippers, astZipper -> astZipper.getBreadcrumbs().size()); Integer maxLevel = Collections.max(grouped.keySet()); - return grouped.get(maxLevel); + return assertNotNull(grouped.get(maxLevel), "expected grouped to contain maxLevel key"); } private NodeZipper moveUp(T parent, List> sameParent) { diff --git a/src/main/java/graphql/util/NodeZipper.java b/src/main/java/graphql/util/NodeZipper.java index 0a5dfd46a7..d1de7dc49a 100644 --- a/src/main/java/graphql/util/NodeZipper.java +++ b/src/main/java/graphql/util/NodeZipper.java @@ -2,6 +2,8 @@ import com.google.common.collect.ImmutableList; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; @@ -13,6 +15,7 @@ import static graphql.Assert.assertNotNull; @PublicApi +@NullMarked public class NodeZipper { @@ -92,7 +95,7 @@ public NodeZipper moveUp() { /** * @return null if it is the root node and marked as deleted, otherwise never null */ - public T toRoot() { + public @Nullable T toRoot() { if (breadcrumbs.size() == 0 && modificationType != ModificationType.DELETE) { return this.curNode; } diff --git a/src/main/java/graphql/util/TraversalControl.java b/src/main/java/graphql/util/TraversalControl.java index 3f68ede32d..99331d877c 100644 --- a/src/main/java/graphql/util/TraversalControl.java +++ b/src/main/java/graphql/util/TraversalControl.java @@ -1,11 +1,13 @@ package graphql.util; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; /** * Special traversal control values */ @PublicApi +@NullMarked public enum TraversalControl { /** diff --git a/src/main/java/graphql/util/TraverserContext.java b/src/main/java/graphql/util/TraverserContext.java index 0f38dc5c0c..163e0230f8 100644 --- a/src/main/java/graphql/util/TraverserContext.java +++ b/src/main/java/graphql/util/TraverserContext.java @@ -1,6 +1,8 @@ package graphql.util; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Map; @@ -16,6 +18,7 @@ * @param type of tree node */ @PublicApi +@NullMarked public interface TraverserContext { enum Phase { @@ -34,14 +37,14 @@ enum Phase { * * @throws graphql.AssertException if the current node is deleted */ - T thisNode(); + @Nullable T thisNode(); /** * Returns the original, unchanged, not deleted Node. * * @return the original node */ - T originalThisNode(); + @Nullable T originalThisNode(); /** * Change the current node to the provided node. Only applicable in enter. @@ -78,7 +81,7 @@ enum Phase { * * @return context associated with the node parent */ - TraverserContext getParentContext(); + @Nullable TraverserContext getParentContext(); /** * The list of parent nodes starting from the current parent. @@ -92,7 +95,7 @@ enum Phase { * * @return The parent node. */ - T getParentNode(); + @Nullable T getParentNode(); /** @@ -107,7 +110,7 @@ enum Phase { * * @return the position or null if this node is a root node */ - NodeLocation getLocation(); + @Nullable NodeLocation getLocation(); /** * Informs that the current node has been already "visited" @@ -132,7 +135,7 @@ enum Phase { * * @return a variable value or {@code null} */ - S getVar(Class key); + @Nullable S getVar(Class key); /** * Searches for a context variable starting from the parent @@ -143,7 +146,7 @@ enum Phase { * * @return a variable value or {@code null} */ - S getVarFromParents(Class key); + @Nullable S getVarFromParents(Class key); /** * Stores a variable in the context diff --git a/src/main/java/graphql/util/TreeParallelTransformer.java b/src/main/java/graphql/util/TreeParallelTransformer.java index 5bbde1cd1b..acc7924c76 100644 --- a/src/main/java/graphql/util/TreeParallelTransformer.java +++ b/src/main/java/graphql/util/TreeParallelTransformer.java @@ -100,6 +100,7 @@ private EnterAction(CountedCompleter parent, DefaultTraverserContext currentCont public void compute() { currentContext.setPhase(TraverserContext.Phase.ENTER); currentContext.setVar(List.class, myZippers); + currentContext.setVar(NodeAdapter.class, nodeAdapter); TraversalControl traversalControl = visitor.enter(currentContext); assertNotNull(traversalControl, "result of enter must not be null"); assertTrue(QUIT != traversalControl, "can't return QUIT for parallel traversing"); diff --git a/src/main/java/graphql/util/TreeTransformer.java b/src/main/java/graphql/util/TreeTransformer.java index 970b0eac48..5a4523a254 100644 --- a/src/main/java/graphql/util/TreeTransformer.java +++ b/src/main/java/graphql/util/TreeTransformer.java @@ -3,6 +3,8 @@ import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedList; import java.util.List; @@ -11,6 +13,7 @@ import static graphql.Assert.assertNotNull; @PublicApi +@NullMarked public class TreeTransformer { private final NodeAdapter nodeAdapter; @@ -19,11 +22,11 @@ public TreeTransformer(NodeAdapter nodeAdapter) { this.nodeAdapter = nodeAdapter; } - public T transform(T root, TraverserVisitor traverserVisitor) { + public @Nullable T transform(T root, TraverserVisitor traverserVisitor) { return transform(root, traverserVisitor, ImmutableKit.emptyMap()); } - public T transform(T root, TraverserVisitor traverserVisitor, Map, Object> rootVars) { + public @Nullable T transform(T root, TraverserVisitor traverserVisitor, Map, Object> rootVars) { assertNotNull(root); @@ -31,7 +34,7 @@ public T transform(T root, TraverserVisitor traverserVisitor, Map, O @Override public TraversalControl enter(TraverserContext context) { - NodeZipper nodeZipper = new NodeZipper<>(context.thisNode(), context.getBreadcrumbs(), nodeAdapter); + NodeZipper nodeZipper = new NodeZipper<>(assertNotNull(context.thisNode()), context.getBreadcrumbs(), nodeAdapter); context.setVar(NodeZipper.class, nodeZipper); context.setVar(NodeAdapter.class, nodeAdapter); return traverserVisitor.enter(context); diff --git a/src/main/java/graphql/util/TreeTransformerUtil.java b/src/main/java/graphql/util/TreeTransformerUtil.java index 0ee41a3b3a..8c1839b1dc 100644 --- a/src/main/java/graphql/util/TreeTransformerUtil.java +++ b/src/main/java/graphql/util/TreeTransformerUtil.java @@ -1,13 +1,16 @@ package graphql.util; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; import java.util.Queue; +import static graphql.Assert.assertNotNull; import static graphql.Assert.assertTrue; @PublicApi +@NullMarked public class TreeTransformerUtil { /** @@ -22,10 +25,10 @@ public class TreeTransformerUtil { public static TraversalControl changeNode(TraverserContext context, T changedNode) { boolean changed = context.isChanged(); if (context.isParallel()) { - List> zippers = context.getVar(List.class); - NodeAdapter adapter = context.getVar(NodeAdapter.class); + List> zippers = assertNotNull(context.getVar(List.class)); + NodeAdapter adapter = assertNotNull(context.getVar(NodeAdapter.class)); if (changed) { - replaceZipperForNode(zippers, context.thisNode(), changedNode); + replaceZipperForNode(zippers, assertNotNull(context.thisNode()), changedNode); context.changeNode(changedNode); } else { NodeZipper nodeZipper = new NodeZipper<>(changedNode, context.getBreadcrumbs(), adapter); @@ -34,11 +37,11 @@ public static TraversalControl changeNode(TraverserContext context, T cha } return TraversalControl.CONTINUE; } else { - NodeZipper zipperWithChangedNode = context.getVar(NodeZipper.class).withNewNode(changedNode); + NodeZipper zipperWithChangedNode = ((NodeZipper) assertNotNull(context.getVar(NodeZipper.class))).withNewNode(changedNode); List> zippers = context.getSharedContextData(); if (changed) { // this is potentially expensive - replaceZipperForNode(zippers, context.thisNode(), changedNode); + replaceZipperForNode(zippers, assertNotNull(context.thisNode()), changedNode); context.changeNode(changedNode); } else { zippers.add(zipperWithChangedNode); @@ -57,14 +60,14 @@ private static void replaceZipperForNode(List> zippers, T curr public static TraversalControl deleteNode(TraverserContext context) { if (context.isParallel()) { - NodeAdapter adapter = context.getVar(NodeAdapter.class); - NodeZipper deleteNodeZipper = new NodeZipper<>(context.thisNode(), context.getBreadcrumbs(), adapter).deleteNode(); - List> zippers = context.getVar(List.class); + NodeAdapter adapter = assertNotNull(context.getVar(NodeAdapter.class)); + NodeZipper deleteNodeZipper = new NodeZipper<>(assertNotNull(context.thisNode()), context.getBreadcrumbs(), adapter).deleteNode(); + List> zippers = assertNotNull(context.getVar(List.class)); zippers.add(deleteNodeZipper); context.deleteNode(); return TraversalControl.CONTINUE; } else { - NodeZipper deleteNodeZipper = context.getVar(NodeZipper.class).deleteNode(); + NodeZipper deleteNodeZipper = ((NodeZipper) assertNotNull(context.getVar(NodeZipper.class))).deleteNode(); Queue> zippers = context.getSharedContextData(); zippers.add(deleteNodeZipper); context.deleteNode(); @@ -74,13 +77,13 @@ public static TraversalControl deleteNode(TraverserContext context) { public static TraversalControl insertAfter(TraverserContext context, T toInsertAfter) { if (context.isParallel()) { - NodeAdapter adapter = context.getVar(NodeAdapter.class); - NodeZipper insertNodeZipper = new NodeZipper<>(context.originalThisNode(), context.getBreadcrumbs(), adapter).insertAfter(toInsertAfter); - List> zippers = context.getVar(List.class); + NodeAdapter adapter = assertNotNull(context.getVar(NodeAdapter.class)); + NodeZipper insertNodeZipper = new NodeZipper<>(assertNotNull(context.originalThisNode()), context.getBreadcrumbs(), adapter).insertAfter(toInsertAfter); + List> zippers = assertNotNull(context.getVar(List.class)); zippers.add(insertNodeZipper); return TraversalControl.CONTINUE; } else { - NodeZipper insertNodeZipper = context.getVar(NodeZipper.class).insertAfter(toInsertAfter); + NodeZipper insertNodeZipper = ((NodeZipper) assertNotNull(context.getVar(NodeZipper.class))).insertAfter(toInsertAfter); Queue> zippers = context.getSharedContextData(); zippers.add(insertNodeZipper); return TraversalControl.CONTINUE; @@ -89,13 +92,13 @@ public static TraversalControl insertAfter(TraverserContext context, T to public static TraversalControl insertBefore(TraverserContext context, T toInsertBefore) { if (context.isParallel()) { - NodeAdapter adapter = context.getVar(NodeAdapter.class); - NodeZipper insertNodeZipper = new NodeZipper<>(context.originalThisNode(), context.getBreadcrumbs(), adapter).insertBefore(toInsertBefore); - List> zippers = context.getVar(List.class); + NodeAdapter adapter = assertNotNull(context.getVar(NodeAdapter.class)); + NodeZipper insertNodeZipper = new NodeZipper<>(assertNotNull(context.originalThisNode()), context.getBreadcrumbs(), adapter).insertBefore(toInsertBefore); + List> zippers = assertNotNull(context.getVar(List.class)); zippers.add(insertNodeZipper); return TraversalControl.CONTINUE; } else { - NodeZipper insertNodeZipper = context.getVar(NodeZipper.class).insertBefore(toInsertBefore); + NodeZipper insertNodeZipper = ((NodeZipper) assertNotNull(context.getVar(NodeZipper.class))).insertBefore(toInsertBefore); Queue> zippers = context.getSharedContextData(); zippers.add(insertNodeZipper); return TraversalControl.CONTINUE; diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 8672329248..e41bee29f2 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -12,140 +12,19 @@ class JSpecifyAnnotationsCheck extends Specification { private static final Set JSPECIFY_EXEMPTION_LIST = [ "graphql.analysis.QueryTraverser", - "graphql.extensions.ExtensionsBuilder", - "graphql.incremental.DeferPayload", - "graphql.incremental.DelayedIncrementalPartialResult", - "graphql.incremental.DelayedIncrementalPartialResultImpl", - "graphql.incremental.IncrementalExecutionResult", - "graphql.incremental.IncrementalExecutionResultImpl", - "graphql.incremental.IncrementalPayload", - "graphql.incremental.StreamPayload", - "graphql.introspection.Introspection", - "graphql.introspection.IntrospectionQuery", - "graphql.introspection.IntrospectionQueryBuilder", - "graphql.introspection.IntrospectionResultToSchema", - "graphql.introspection.IntrospectionWithDirectivesSupport", - "graphql.introspection.IntrospectionWithDirectivesSupport\$DirectivePredicateEnvironment", - "graphql.language.ScalarTypeDefinition", - "graphql.language.ScalarTypeExtensionDefinition", - "graphql.language.SchemaDefinition", - "graphql.language.SchemaExtensionDefinition", - "graphql.language.Selection", - "graphql.language.SelectionSet", - "graphql.language.SelectionSetContainer", - "graphql.language.TypeKind", - "graphql.language.TypeName", - "graphql.language.UnionTypeDefinition", - "graphql.language.UnionTypeExtensionDefinition", - "graphql.language.VariableDefinition", - "graphql.normalized.ExecutableNormalizedField", - "graphql.normalized.ExecutableNormalizedOperation", - "graphql.normalized.ExecutableNormalizedOperationFactory", - "graphql.normalized.ExecutableNormalizedOperationToAstCompiler", - "graphql.normalized.NormalizedInputValue", "graphql.normalized.incremental.NormalizedDeferredExecution", "graphql.normalized.nf.NormalizedDocument", "graphql.normalized.nf.NormalizedDocumentFactory", "graphql.normalized.nf.NormalizedField", "graphql.normalized.nf.NormalizedOperation", "graphql.normalized.nf.NormalizedOperationToAstCompiler", - "graphql.parser.InvalidSyntaxException", - "graphql.parser.MultiSourceReader", - "graphql.parser.Parser", - "graphql.parser.ParserEnvironment", - "graphql.parser.ParserOptions", - "graphql.schema.AsyncDataFetcher", - "graphql.schema.CoercingParseLiteralException", - "graphql.schema.CoercingParseValueException", - "graphql.schema.CoercingSerializeException", - "graphql.schema.DataFetcherFactories", - "graphql.schema.DataFetcherFactoryEnvironment", - "graphql.schema.DataFetchingFieldSelectionSet", - "graphql.schema.DefaultGraphqlTypeComparatorRegistry", - "graphql.schema.DelegatingDataFetchingEnvironment", - "graphql.schema.FieldCoordinates", - "graphql.schema.GraphQLAppliedDirectiveArgument", - "graphql.schema.GraphQLArgument", - "graphql.schema.GraphQLCompositeType", - "graphql.schema.GraphQLDirective", - "graphql.schema.GraphQLDirectiveContainer", - "graphql.schema.GraphQLEnumValueDefinition", - "graphql.schema.GraphQLFieldDefinition", - "graphql.schema.GraphQLFieldsContainer", - "graphql.schema.GraphQLImplementingType", - "graphql.schema.GraphQLInputFieldsContainer", - "graphql.schema.GraphQLInputObjectField", - "graphql.schema.GraphQLInputObjectType", - "graphql.schema.GraphQLInputSchemaElement", - "graphql.schema.GraphQLInputType", - "graphql.schema.GraphQLInputValueDefinition", - "graphql.schema.GraphQLInterfaceType", - "graphql.schema.GraphQLModifiedType", - "graphql.schema.GraphQLNamedOutputType", - "graphql.schema.GraphQLNamedSchemaElement", - "graphql.schema.GraphQLNamedType", - "graphql.schema.GraphQLNonNull", - "graphql.schema.GraphQLNullableType", - "graphql.schema.GraphQLObjectType", - "graphql.schema.GraphQLOutputType", - "graphql.schema.GraphQLSchemaElement", - "graphql.schema.GraphQLTypeReference", - "graphql.schema.GraphQLTypeVisitor", - "graphql.schema.GraphQLTypeVisitorStub", - "graphql.schema.GraphQLUnmodifiedType", - "graphql.schema.GraphqlElementParentTree", - "graphql.schema.GraphqlTypeComparatorEnvironment", - "graphql.schema.GraphqlTypeComparatorRegistry", - "graphql.schema.InputValueWithState", - "graphql.schema.SchemaElementChildrenContainer", - "graphql.schema.SchemaTransformer", - "graphql.schema.SchemaTraverser", - "graphql.schema.SelectedField", - "graphql.schema.StaticDataFetcher", - "graphql.schema.diff.DiffCategory", - "graphql.schema.diff.DiffEvent", - "graphql.schema.diff.DiffLevel", - "graphql.schema.diff.DiffSet", - "graphql.schema.diff.SchemaDiffSet", - "graphql.schema.diff.reporting.CapturingReporter", - "graphql.schema.diff.reporting.ChainedReporter", - "graphql.schema.diff.reporting.PrintStreamReporter", "graphql.schema.diffing.SchemaGraph", - "graphql.schema.idl.CombinedWiringFactory", - "graphql.schema.idl.MapEnumValuesProvider", - "graphql.schema.idl.NaturalEnumValuesProvider", - "graphql.schema.idl.RuntimeWiring", - "graphql.schema.idl.SchemaDirectiveWiring", - "graphql.schema.idl.SchemaDirectiveWiringEnvironment", - "graphql.schema.idl.SchemaGenerator", - "graphql.schema.idl.SchemaPrinter", - "graphql.schema.idl.TypeRuntimeWiring", - "graphql.schema.idl.errors.SchemaProblem", - "graphql.schema.idl.errors.StrictModeWiringException", - "graphql.schema.transform.FieldVisibilitySchemaTransformation", - "graphql.schema.transform.VisibleFieldPredicateEnvironment", - "graphql.schema.usage.SchemaUsage", - "graphql.schema.usage.SchemaUsageSupport", "graphql.schema.validation.OneOfInputObjectRules", - "graphql.schema.validation.SchemaValidationErrorClassification", - "graphql.schema.visibility.BlockedFields", - "graphql.schema.visibility.DefaultGraphqlFieldVisibility", - "graphql.schema.visibility.GraphqlFieldVisibility", - "graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility", - "graphql.schema.visitor.GraphQLSchemaTraversalControl", "graphql.util.CyclicSchemaAnalyzer", - "graphql.util.NodeAdapter", - "graphql.util.NodeLocation", - "graphql.util.NodeMultiZipper", - "graphql.util.NodeZipper", "graphql.util.querygenerator.QueryGenerator", "graphql.util.querygenerator.QueryGeneratorOptions", "graphql.util.querygenerator.QueryGeneratorOptions\$QueryGeneratorOptionsBuilder", - "graphql.util.querygenerator.QueryGeneratorResult", - "graphql.util.TraversalControl", - "graphql.util.TraverserContext", - "graphql.util.TreeTransformer", - "graphql.util.TreeTransformerUtil" + "graphql.util.querygenerator.QueryGeneratorResult" ] as Set def "ensure all public API and experimental API classes have @NullMarked annotation"() { diff --git a/src/test/groovy/graphql/schema/fetching/LambdaFetchingSupportTest.groovy b/src/test/groovy/graphql/schema/fetching/LambdaFetchingSupportTest.groovy index 9ad4df39ef..b0d9b0cafa 100644 --- a/src/test/groovy/graphql/schema/fetching/LambdaFetchingSupportTest.groovy +++ b/src/test/groovy/graphql/schema/fetching/LambdaFetchingSupportTest.groovy @@ -151,7 +151,7 @@ class LambdaFetchingSupportTest extends Specification { return GraphQLFieldDefinition.newFieldDefinition().name(fldName).type(Scalars.GraphQLString).build() } - @IgnoreIf({ System.getProperty("java.version").split('\\.')[0] as Integer > 11 }) + @IgnoreIf({ Runtime.version().feature() > 11 }) def "different class loaders induce certain behaviours"() { String sourceCode = ''' package com.dynamic;