From 86e193d79f03aa9e8bad0ec8a0d86a879ffd9757 Mon Sep 17 00:00:00 2001 From: Nikita Katkov Date: Thu, 27 Jun 2024 14:32:27 +0200 Subject: [PATCH] [json] IJPL-156923 Cache element validity against a particular schema node to avoid double computation With the introduction of if-then-else resolve that selects a branch depending on a given instance file element, the number of calls to JsonSchemaResolver.isCorrect method increased significantly. It is expected, as any validation/reference contribution/other editor features require a resolved schema node. Resolve results are cached on language modification tracker because with the instance file updates, a previously valid if-else branch might become invalid GitOrigin-RevId: f197203a3c53a7c4e7c1d502faaff3d50a9451e3 --- .../jsonSchema/impl/JsonSchemaResolver.java | 7 ++-- .../impl/jsonSchemaValidityCache.kt | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 json/src/com/jetbrains/jsonSchema/impl/jsonSchemaValidityCache.kt diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java index 0b85db7a4f31..1e488a8ba76f 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java @@ -24,13 +24,14 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; +import static com.jetbrains.jsonSchema.impl.JsonSchemaValidityCacheKt.getOrComputeAdapterValidityAgainstGivenSchema; import static com.jetbrains.jsonSchema.impl.light.SchemaKeywordsKt.*; public final class JsonSchemaResolver { private final @NotNull Project myProject; private final @NotNull JsonSchemaObject mySchema; private final @NotNull JsonPointerPosition myPosition; - private final @Nullable JsonSchemaNodeExpansionRequest myExpansionRequest; + private final @NotNull JsonSchemaNodeExpansionRequest myExpansionRequest; /** @@ -209,8 +210,6 @@ public final class JsonSchemaResolver { } public static boolean isCorrect(final @NotNull JsonValueAdapter value, final @NotNull JsonSchemaObject schema) { - final JsonSchemaAnnotatorChecker checker = new JsonSchemaAnnotatorChecker(value.getDelegate().getProject(), JsonComplianceCheckerOptions.RELAX_ENUM_CHECK); - checker.checkByScheme(value, schema); - return checker.isCorrect(); + return getOrComputeAdapterValidityAgainstGivenSchema(value, schema); } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/jsonSchemaValidityCache.kt b/json/src/com/jetbrains/jsonSchema/impl/jsonSchemaValidityCache.kt new file mode 100644 index 000000000000..4dd29bebacfb --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/impl/jsonSchemaValidityCache.kt @@ -0,0 +1,41 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.jsonSchema.impl + +import com.intellij.openapi.util.Key +import com.intellij.psi.util.CachedValue +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.jetbrains.jsonSchema.extension.adapters.JsonValueAdapter +import com.jetbrains.jsonSchema.ide.JsonSchemaService +import java.util.concurrent.ConcurrentHashMap + +private val JSON_SCHEMA_VALIDATION_MAP = Key.create>>("JSON_SCHEMA_VALIDATION_MAP") + +internal fun getOrComputeAdapterValidityAgainstGivenSchema(value: JsonValueAdapter, schema: JsonSchemaObject): Boolean { + val delegatePsi = value.delegate + val cachedMap: MutableMap = + CachedValuesManager.getManager(delegatePsi.project).getCachedValue( + delegatePsi, + JSON_SCHEMA_VALIDATION_MAP, + CachedValueProvider { + CachedValueProvider.Result.create( + ConcurrentHashMap(), + delegatePsi.manager.modificationTracker.forLanguage(delegatePsi.language), + JsonSchemaService.Impl.get(delegatePsi.project) + ) + }, + false + ) + + val cachedValue = cachedMap[schema] + if (cachedValue != null) { + return cachedValue + } + + val checker = JsonSchemaAnnotatorChecker(value.delegate.project, JsonComplianceCheckerOptions.RELAX_ENUM_CHECK) + checker.checkByScheme(value, schema) + val computedValue = checker.isCorrect + + cachedMap[schema] = computedValue + return computedValue +} \ No newline at end of file