From 697298ecfe06887e0e8b5f629d6abc6ae3aefaf9 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Thu, 15 May 2025 17:06:03 +1000 Subject: [PATCH] Cherry pick 3930 remove optional streams --- src/main/java/graphql/GraphqlErrorHelper.java | 9 ++++--- .../java/graphql/execution/Execution.java | 8 ++---- .../graphql/execution/ExecutionContext.java | 7 ++++- .../graphql/execution/ExecutionStrategy.java | 8 ++---- .../execution/ValuesResolverConversion.java | 22 +++++++++------- .../execution/ValuesResolverLegacy.java | 9 ++++--- .../incremental/DeferredExecutionSupport.java | 18 ++++++++----- ...spatchStrategyWithDeferAlwaysDispatch.java | 12 ++++++--- .../DelayedIncrementalPartialResultImpl.java | 12 +++++---- .../IncrementalExecutionResultImpl.java | 11 ++++---- .../incremental/IncrementalPayload.java | 17 ++++++++---- .../fetching/LambdaFetchingSupport.java | 26 ++++++++++++------- .../FieldVisibilitySchemaTransformation.java | 7 ++--- src/main/java/graphql/util/FpKit.java | 16 +++++++++++- src/test/groovy/graphql/util/FpKitTest.groovy | 7 +++++ 15 files changed, 119 insertions(+), 70 deletions(-) diff --git a/src/main/java/graphql/GraphqlErrorHelper.java b/src/main/java/graphql/GraphqlErrorHelper.java index 35a20d03f..391b223d9 100644 --- a/src/main/java/graphql/GraphqlErrorHelper.java +++ b/src/main/java/graphql/GraphqlErrorHelper.java @@ -1,13 +1,13 @@ package graphql; import graphql.language.SourceLocation; +import graphql.util.FpKit; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import static graphql.collect.ImmutableKit.mapAndDropNulls; @@ -77,8 +77,11 @@ public static Object location(SourceLocation location) { } static List fromSpecification(List> specificationMaps) { - return specificationMaps.stream() - .map(GraphqlErrorHelper::fromSpecification).collect(Collectors.toList()); + List list = FpKit.arrayListSizedTo(specificationMaps); + for (Map specificationMap : specificationMaps) { + list.add(fromSpecification(specificationMap)); + } + return list; } static GraphQLError fromSpecification(Map specificationMap) { diff --git a/src/main/java/graphql/execution/Execution.java b/src/main/java/graphql/execution/Execution.java index a784325f3..e93234713 100644 --- a/src/main/java/graphql/execution/Execution.java +++ b/src/main/java/graphql/execution/Execution.java @@ -180,9 +180,7 @@ private CompletableFuture executeOperation(ExecutionContext exe MergedSelectionSet fields = fieldCollector.collectFields( collectorParameters, operationDefinition.getSelectionSet(), - Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) + executionContext.hasIncrementalSupport() ); ResultPath path = ResultPath.rootPath(); @@ -255,9 +253,7 @@ private DataLoaderDispatchStrategy createDataLoaderDispatchStrategy(ExecutionCon return DataLoaderDispatchStrategy.NO_OP; } if (!executionContext.isSubscriptionOperation()) { - boolean deferEnabled = Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false); + boolean deferEnabled = executionContext.hasIncrementalSupport(); // Dedicated strategy for defer support, for safety purposes. return deferEnabled ? diff --git a/src/main/java/graphql/execution/ExecutionContext.java b/src/main/java/graphql/execution/ExecutionContext.java index a53ae621e..22e2d7b63 100644 --- a/src/main/java/graphql/execution/ExecutionContext.java +++ b/src/main/java/graphql/execution/ExecutionContext.java @@ -360,9 +360,14 @@ public ResultNodesInfo getResultNodesInfo() { return resultNodesInfo; } + @Internal + public boolean hasIncrementalSupport() { + GraphQLContext graphqlContext = getGraphQLContext(); + return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT); + } + @Internal public EngineRunningState getEngineRunningState() { return engineRunningState; } - } diff --git a/src/main/java/graphql/execution/ExecutionStrategy.java b/src/main/java/graphql/execution/ExecutionStrategy.java index 47e924549..3c59c02e4 100644 --- a/src/main/java/graphql/execution/ExecutionStrategy.java +++ b/src/main/java/graphql/execution/ExecutionStrategy.java @@ -295,9 +295,7 @@ private static Map buildFieldValueMap(List fieldNames, L DeferredExecutionSupport createDeferredExecutionSupport(ExecutionContext executionContext, ExecutionStrategyParameters parameters) { MergedSelectionSet fields = parameters.getFields(); - return Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) ? + return executionContext.hasIncrementalSupport() ? new DeferredExecutionSupport.DeferredExecutionSupportImpl( fields, parameters, @@ -920,9 +918,7 @@ protected Object completeValueForObject(ExecutionContext executionContext, Execu MergedSelectionSet subFields = fieldCollector.collectFields( collectorParameters, parameters.getField(), - Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) + executionContext.hasIncrementalSupport() ); ExecutionStepInfo newExecutionStepInfo = executionStepInfo.changeTypeWithPreservedNonNull(resolvedObjectType); diff --git a/src/main/java/graphql/execution/ValuesResolverConversion.java b/src/main/java/graphql/execution/ValuesResolverConversion.java index c53eeb64e..29e2587ab 100644 --- a/src/main/java/graphql/execution/ValuesResolverConversion.java +++ b/src/main/java/graphql/execution/ValuesResolverConversion.java @@ -602,16 +602,18 @@ private static List externalValueToInternalValueForList( ) throws CoercingParseValueException, NonNullableValueCoercedAsNullException { GraphQLInputType wrappedType = (GraphQLInputType) graphQLList.getWrappedType(); - return FpKit.toListOrSingletonList(value) - .stream() - .map(val -> externalValueToInternalValueImpl( - inputInterceptor, - fieldVisibility, - wrappedType, - val, - graphqlContext, - locale)) - .collect(toList()); + List listOrSingletonList = FpKit.toListOrSingletonList(value); + List list = FpKit.arrayListSizedTo(listOrSingletonList); + for (Object val : listOrSingletonList) { + list.add(externalValueToInternalValueImpl( + inputInterceptor, + fieldVisibility, + wrappedType, + val, + graphqlContext, + locale)); + } + return list; } /** diff --git a/src/main/java/graphql/execution/ValuesResolverLegacy.java b/src/main/java/graphql/execution/ValuesResolverLegacy.java index d5e58f465..d98a744f7 100644 --- a/src/main/java/graphql/execution/ValuesResolverLegacy.java +++ b/src/main/java/graphql/execution/ValuesResolverLegacy.java @@ -133,10 +133,11 @@ private static Value handleNumberLegacy(String stringValue) { private static Value handleListLegacy(Object value, GraphQLList type, GraphQLContext graphqlContext, Locale locale) { GraphQLType itemType = type.getWrappedType(); if (FpKit.isIterable(value)) { - List valuesNodes = FpKit.toListOrSingletonList(value) - .stream() - .map(item -> valueToLiteralLegacy(item, itemType, graphqlContext, locale)) - .collect(toList()); + List listOrSingletonList = FpKit.toListOrSingletonList(value); + List valuesNodes = FpKit.arrayListSizedTo(listOrSingletonList); + for (Object item : listOrSingletonList) { + valuesNodes.add(valueToLiteralLegacy(item, itemType, graphqlContext, locale)); + } return ArrayValue.newArrayValue().values(valuesNodes).build(); } return valueToLiteralLegacy(value, itemType, graphqlContext, locale); diff --git a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java index 5047ee560..cbe73045b 100644 --- a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java +++ b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -25,7 +26,6 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Supplier; -import java.util.stream.Collectors; /** * The purpose of this class hierarchy is to encapsulate most of the logic for deferring field execution, thus @@ -107,9 +107,12 @@ public List getNonDeferredFieldNames(List allFieldNames) { @Override public Set> createCalls(ExecutionStrategyParameters executionStrategyParameters) { - return deferredExecutionToFields.keySet().stream() - .map(deferredExecution -> this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters)) - .collect(Collectors.toSet()); + ImmutableSet deferredExecutions = deferredExecutionToFields.keySet(); + Set> set = new HashSet<>(deferredExecutions.size()); + for (DeferredExecution deferredExecution : deferredExecutions) { + set.add(this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters)); + } + return set; } private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution, ExecutionStrategyParameters executionStrategyParameters) { @@ -117,9 +120,10 @@ private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferr List mergedFields = deferredExecutionToFields.get(deferredExecution); - List>> calls = mergedFields.stream() - .map(currentField -> this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters)) - .collect(Collectors.toList()); + List>> calls = FpKit.arrayListSizedTo(mergedFields); + for (MergedField currentField : mergedFields) { + calls.add(this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters)); + } return new DeferredFragmentCall( deferredExecution.getLabel(), diff --git a/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java b/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java index 26c847b75..1a9ec7c82 100644 --- a/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java +++ b/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java @@ -6,6 +6,7 @@ import graphql.execution.ExecutionContext; import graphql.execution.ExecutionStrategyParameters; import graphql.execution.FieldValueInfo; +import graphql.execution.MergedField; import graphql.schema.DataFetcher; import graphql.util.LockKit; import org.dataloader.DataLoaderRegistry; @@ -195,10 +196,13 @@ public void fieldFetched(ExecutionContext executionContext, } private void increaseCallCounts(int curLevel, ExecutionStrategyParameters parameters) { - int nonDeferredFieldCount = (int) parameters.getFields().getSubFieldsList().stream() - .filter(field -> !field.isDeferred()) - .count(); - + int count = 0; + for (MergedField field : parameters.getFields().getSubFieldsList()) { + if (!field.isDeferred()) { + count++; + } + } + int nonDeferredFieldCount = count; callStack.lock.runLocked(() -> { callStack.increaseExpectedFetchCount(curLevel, nonDeferredFieldCount); callStack.increaseHappenedStrategyCalls(curLevel); diff --git a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java index 461d658e7..3412d7729 100644 --- a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java +++ b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java @@ -1,12 +1,12 @@ package graphql.incremental; import graphql.ExperimentalApi; +import graphql.util.FpKit; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @ExperimentalApi public class DelayedIncrementalPartialResultImpl implements DelayedIncrementalPartialResult { @@ -44,10 +44,12 @@ public Map toSpecification() { result.put("extensions", extensions); } - if(incrementalItems != null) { - result.put("incremental", incrementalItems.stream() - .map(IncrementalPayload::toSpecification) - .collect(Collectors.toList())); + if (incrementalItems != null) { + List> list = FpKit.arrayListSizedTo(incrementalItems); + for (IncrementalPayload incrementalItem : incrementalItems) { + list.add(incrementalItem.toSpecification()); + } + result.put("incremental", list); } return result; diff --git a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java index 8bd8e62a0..2f9470b94 100644 --- a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java +++ b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.stream.Collectors; @ExperimentalApi public class IncrementalExecutionResultImpl extends ExecutionResultImpl implements IncrementalExecutionResult { @@ -66,11 +65,11 @@ public Map toSpecification() { map.put("hasNext", hasNext); if (this.incremental != null) { - map.put("incremental", - this.incremental.stream() - .map(IncrementalPayload::toSpecification) - .collect(Collectors.toCollection(LinkedList::new)) - ); + LinkedList> linkedList = new LinkedList<>(); + for (IncrementalPayload incrementalPayload : this.incremental) { + linkedList.add(incrementalPayload.toSpecification()); + } + map.put("incremental", linkedList); } return map; diff --git a/src/main/java/graphql/incremental/IncrementalPayload.java b/src/main/java/graphql/incremental/IncrementalPayload.java index efeba3929..742d857c8 100644 --- a/src/main/java/graphql/incremental/IncrementalPayload.java +++ b/src/main/java/graphql/incremental/IncrementalPayload.java @@ -3,6 +3,7 @@ import graphql.ExperimentalApi; import graphql.GraphQLError; import graphql.execution.ResultPath; +import graphql.util.FpKit; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -11,8 +12,6 @@ import java.util.Map; import java.util.Objects; -import static java.util.stream.Collectors.toList; - /** * Represents a payload that can be resolved after the initial response. */ @@ -80,7 +79,11 @@ public Map toSpecification() { } protected Object errorsToSpec(List errors) { - return errors.stream().map(GraphQLError::toSpecification).collect(toList()); + List> list = FpKit.arrayListSizedTo(errors); + for (GraphQLError error : errors) { + list.add(error.toSpecification()); + } + return list; } public int hashCode() { @@ -88,8 +91,12 @@ public int hashCode() { } public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } IncrementalPayload that = (IncrementalPayload) obj; return Objects.equals(path, that.path) && Objects.equals(label, that.label) && diff --git a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java index 51ced4aba..5ff38f575 100644 --- a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java +++ b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java @@ -2,6 +2,7 @@ import graphql.Internal; import graphql.VisibleForTesting; +import graphql.util.FpKit; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; @@ -17,8 +18,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import static java.util.stream.Collectors.toList; - @Internal public class LambdaFetchingSupport { @@ -69,9 +68,12 @@ private static Method getCandidateMethod(Class sourceClass, String propertyNa Predicate getterPredicate = method -> isGetterNamed(method) && propertyName.equals(mkPropertyNameGetter(method)); List allGetterMethods = findMethodsForProperty(sourceClass, getterPredicate); - List pojoGetterMethods = allGetterMethods.stream() - .filter(LambdaFetchingSupport::isPossiblePojoMethod) - .collect(toList()); + List pojoGetterMethods = FpKit.arrayListSizedTo(allGetterMethods); + for (Method allGetterMethod : allGetterMethods) { + if (isPossiblePojoMethod(allGetterMethod)) { + pojoGetterMethods.add(allGetterMethod); + } + } if (!pojoGetterMethods.isEmpty()) { Method method = pojoGetterMethods.get(0); if (isBooleanGetter(method)) { @@ -97,7 +99,13 @@ private static Method checkForSingleParameterPeer(Method candidateMethod, List methods) { // we prefer isX() over getX() if both happen to be present - Optional isMethod = methods.stream().filter(method -> method.getName().startsWith("is")).findFirst(); + Optional isMethod = Optional.empty(); + for (Method method : methods) { + if (method.getName().startsWith("is")) { + isMethod = Optional.of(method); + break; + } + } return isMethod.orElse(methods.get(0)); } @@ -121,9 +129,9 @@ private static List findMethodsForProperty(Class sourceClass, Predica currentClass = currentClass.getSuperclass(); } - return methods.stream() - .sorted(Comparator.comparing(Method::getName)) - .collect(toList()); + List list = new ArrayList<>(methods); + list.sort(Comparator.comparing(Method::getName)); + return list; } private static boolean isPossiblePojoMethod(Method method) { diff --git a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java index 303b81b9c..325854129 100644 --- a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java +++ b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java @@ -62,9 +62,10 @@ public final GraphQLSchema apply(GraphQLSchema schema) { Set markedForRemovalTypes = new HashSet<>(); // query, mutation, and subscription types should not be removed - final Set protectedTypeNames = getOperationTypes(schema).stream() - .map(GraphQLObjectType::getName) - .collect(Collectors.toSet()); + final Set protectedTypeNames = new HashSet<>(); + for (GraphQLObjectType graphQLObjectType : getOperationTypes(schema)) { + protectedTypeNames.add(graphQLObjectType.getName()); + } beforeTransformationHook.run(); diff --git a/src/main/java/graphql/util/FpKit.java b/src/main/java/graphql/util/FpKit.java index e82abcbd1..f7f8c0051 100644 --- a/src/main/java/graphql/util/FpKit.java +++ b/src/main/java/graphql/util/FpKit.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import graphql.Internal; +import org.jspecify.annotations.NonNull; import java.lang.reflect.Array; import java.util.ArrayList; @@ -141,6 +142,19 @@ public static Collection toCollection(Object iterableResult) { return list; } + /** + * Creates an {@link ArrayList} sized appropriately to the collection, typically for copying + * + * @param collection the collection of a certain size + * @param to two + * + * @return a new {@link ArrayList} initially sized to the same as the collection + */ + public static @NonNull List arrayListSizedTo(@NonNull Collection collection) { + return new ArrayList<>(collection.size()); + } + + /** * Converts a value into a list if it's really a collection or array of things * else it turns it into a singleton list containing that one value @@ -301,7 +315,7 @@ public static int findIndex(List list, Predicate filter) { } public static List filterList(Collection list, Predicate filter) { - List result = new ArrayList<>(); + List result = arrayListSizedTo(list); for (T t : list) { if (filter.test(t)) { result.add(t); diff --git a/src/test/groovy/graphql/util/FpKitTest.groovy b/src/test/groovy/graphql/util/FpKitTest.groovy index 1996b06d5..3206dd70c 100644 --- a/src/test/groovy/graphql/util/FpKitTest.groovy +++ b/src/test/groovy/graphql/util/FpKitTest.groovy @@ -228,4 +228,11 @@ class FpKitTest extends Specification { then: intersection.isEmpty() } + + def "test sized allocation"() { + when: + def newArrayList = FpKit.arrayListSizedTo(["a", "b", "c"]) + then: + newArrayList instanceof ArrayList + } }