diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 93f30ddaed97..e9ee4766196d 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -30,7 +30,7 @@ public abstract class JsonSchemaObject { public abstract @Nullable Boolean getConstantSchema(); @ApiStatus.Experimental - public abstract boolean hasChildFieldsExcept(@NotNull String @NotNull ... namesToSkip); + public abstract boolean hasChildFieldsExcept(@NotNull List<@NotNull String> namesToSkip); public abstract @NotNull Iterable getValidations(@Nullable JsonSchemaType type, @NotNull JsonValueAdapter value); @@ -154,9 +154,9 @@ public abstract class JsonSchemaObject { public abstract @NotNull Iterator getDefinitionNames(); - public abstract @Nullable String readChildNodeValue(@NotNull String @NotNull ... childNodeName); + public abstract @Nullable String readChildNodeValue(@NotNull String childNodeName); - public abstract boolean hasChildNode(@NotNull String @NotNull ... childNodeName); + public abstract boolean hasChildNode(@NotNull String childNodeName); public abstract @NotNull Iterator getPropertyNames(); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObjectImpl.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObjectImpl.java index e96d25434233..2da228beaa78 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObjectImpl.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObjectImpl.java @@ -121,12 +121,12 @@ public class JsonSchemaObjectImpl extends JsonSchemaObject { public final UserDataHolderBase myUserDataHolder = new UserDataHolderBase(); @Override - public @Nullable String readChildNodeValue(@NotNull String @NotNull ... childNodeName) { + public @Nullable String readChildNodeValue(@NotNull String childNodeName) { return null; } @Override - public boolean hasChildNode(@NotNull String @NotNull ... childNodeName) { + public boolean hasChildNode(@NotNull String childNodeName) { return false; } @@ -136,7 +136,7 @@ public class JsonSchemaObjectImpl extends JsonSchemaObject { } @Override - public boolean hasChildFieldsExcept(@NotNull String @NotNull ... namesToSkip) { + public boolean hasChildFieldsExcept(@NotNull List<@NotNull String> namesToSkip) { return false; } diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/JsonSchemaNodePointer.kt b/json/src/com/jetbrains/jsonSchema/impl/light/JsonSchemaNodePointer.kt index 883e8fd9b0f3..25065bb9130b 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/JsonSchemaNodePointer.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/JsonSchemaNodePointer.kt @@ -2,11 +2,14 @@ package com.jetbrains.jsonSchema.impl.light import com.jetbrains.jsonSchema.impl.JsonSchemaObject +import org.jetbrains.annotations.ApiStatus +@ApiStatus.Internal interface JsonSchemaNodePointer { val rawSchemaNode: T } +@ApiStatus.Internal interface JsonSchemaObjectFactory where V : JsonSchemaObject, V : JsonSchemaNodePointer { /** * @return an instance of schema object backed by physically existing schema node that can be found by a combined json pointer, where @@ -32,18 +35,18 @@ interface RawJsonSchemaNodeAccessor { /** * Resolve raw schema node from the given schema node by the given node's name */ - fun resolveRelativeNode(node: T, vararg relativeChildPath: String): T? + fun resolveRelativeNode(node: T, relativeChildPath: String? = null): T? - fun hasChildNode(node: T, vararg relativeChildPath: String): Boolean + fun hasChildNode(node: T, relativeChildPath: String): Boolean - fun readTextNodeValue(node: T, vararg relativeChildPath: String): String? - fun readBooleanNodeValue(node: T, vararg relativeChildPath: String): Boolean? - fun readNumberNodeValue(node: T, vararg relativeChildPath: String): Number? - fun readUntypedNodeValueAsText(node: T, vararg relativeChildPath: String): String? + fun readTextNodeValue(node: T, relativeChildPath: String? = null): String? + fun readBooleanNodeValue(node: T, relativeChildPath: String? = null): Boolean? + fun readNumberNodeValue(node: T, relativeChildPath: String? = null): Number? + fun readUntypedNodeValueAsText(node: T, relativeChildPath: String? = null): String? - fun readNodeKeys(node: T, vararg relativeChildPath: String): Sequence? + fun readNodeKeys(node: T, relativeChildPath: String? = null): Sequence? - fun readUntypedNodesCollection(node: T, vararg relativeChildPath: String): Sequence? - fun readNodeAsMapEntries(node: T, vararg relativeChildPath: String): Sequence>? - fun readNodeAsMultiMapEntries(node: T, vararg relativeChildPath: String): Sequence>>? + fun readUntypedNodesCollection(node: T, relativeChildPath: String? = null): Sequence? + fun readNodeAsMapEntries(node: T, relativeChildPath: String? = null): Sequence>? + fun readNodeAsMultiMapEntries(node: T, relativeChildPath: String? = null): Sequence>>? } \ No newline at end of file diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/InheritedJsonSchemaObjectView.kt b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/InheritedJsonSchemaObjectView.kt index 0db743d42384..750d04a59e92 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/InheritedJsonSchemaObjectView.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/InheritedJsonSchemaObjectView.kt @@ -35,15 +35,15 @@ internal class InheritedJsonSchemaObjectView( return other.ref } - override fun readChildNodeValue(vararg childNodeName: String): String? { + override fun readChildNodeValue(childNodeName: String): String? { return baseIfConditionOrOtherWithArgument(JsonSchemaObject::readChildNodeValue, childNodeName, String?::isNotBlank) } - override fun hasChildNode(vararg childNodeName: String): Boolean { - return other.hasChildNode(*childNodeName) + override fun hasChildNode(childNodeName: String): Boolean { + return other.hasChildNode(childNodeName) } - override fun hasChildFieldsExcept(namesToSkip: Array): Boolean { + override fun hasChildFieldsExcept(namesToSkip: List): Boolean { return booleanOrWithArgument(JsonSchemaObject::hasChildFieldsExcept, namesToSkip) } diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/JsonSchemaObjectBackedByJacksonBase.kt b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/JsonSchemaObjectBackedByJacksonBase.kt index 8c2331986eb9..331cd142c972 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/JsonSchemaObjectBackedByJacksonBase.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/JsonSchemaObjectBackedByJacksonBase.kt @@ -32,7 +32,7 @@ private const val INVALID_PATTERN_FALLBACK = "__invalid_ij_pattern" @ApiStatus.Internal abstract class JsonSchemaObjectBackedByJacksonBase( override val rawSchemaNode: JsonNode, - private val jsonPointer: String + private val jsonPointer: String, ) : JsonSchemaObjectLegacyAdapter(), JsonSchemaNodePointer { abstract override fun getRootSchemaObject(): RootJsonSchemaObjectBackedByJackson @@ -87,18 +87,18 @@ abstract class JsonSchemaObjectBackedByJacksonBase( return getRootSchemaObject().rawFile } - override fun hasChildFieldsExcept(namesToSkip: Array): Boolean { + override fun hasChildFieldsExcept(namesToSkip: List): Boolean { return JacksonSchemaNodeAccessor.readNodeKeys(rawSchemaNode) .orEmpty() .any { it !in namesToSkip } } - override fun hasChildNode(vararg childNodeName: String): Boolean { - return JacksonSchemaNodeAccessor.hasChildNode(rawSchemaNode, *childNodeName) + override fun hasChildNode(childNodeName: String): Boolean { + return JacksonSchemaNodeAccessor.hasChildNode(rawSchemaNode, childNodeName) } - override fun readChildNodeValue(vararg childNodeName: String): String? { - return JacksonSchemaNodeAccessor.readUntypedNodeValueAsText(rawSchemaNode, *childNodeName) + override fun readChildNodeValue(childNodeName: String): String? { + return JacksonSchemaNodeAccessor.readUntypedNodeValueAsText(rawSchemaNode, childNodeName) } override fun getConstantSchema(): Boolean? { @@ -474,11 +474,11 @@ abstract class JsonSchemaObjectBackedByJacksonBase( .toList() } - private fun createChildMap(vararg childMapName: String): Map? { - return JacksonSchemaNodeAccessor.readNodeAsMapEntries(rawSchemaNode, *childMapName) + private fun createChildMap(childMapName: String): Map? { + return JacksonSchemaNodeAccessor.readNodeAsMapEntries(rawSchemaNode, childMapName) ?.mapNotNull { (key, value) -> if (!value.isObject) return@mapNotNull null - val childObject = createResolvableChild(*childMapName, key) ?: return@mapNotNull null + val childObject = createResolvableChild(childMapName, key) ?: return@mapNotNull null key to childObject }?.toMap() } @@ -510,16 +510,21 @@ abstract class JsonSchemaObjectBackedByJacksonBase( } override fun getLanguageInjection(): String? { - return JacksonSchemaNodeAccessor.readTextNodeValue(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION) - ?: JacksonSchemaNodeAccessor.readTextNodeValue(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION, LANGUAGE) + val directChild = JacksonSchemaNodeAccessor.readTextNodeValue(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION) + if (directChild != null) return directChild + + val intermediateNode = JacksonSchemaNodeAccessor.resolveRelativeNode(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION) ?: return null + return JacksonSchemaNodeAccessor.readTextNodeValue(intermediateNode, LANGUAGE) } override fun getLanguageInjectionPrefix(): String? { - return JacksonSchemaNodeAccessor.readTextNodeValue(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION, PREFIX) + val intermediateNode = JacksonSchemaNodeAccessor.resolveRelativeNode(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION) ?: return null + return JacksonSchemaNodeAccessor.readTextNodeValue(intermediateNode, PREFIX) } override fun getLanguageInjectionPostfix(): String? { - return JacksonSchemaNodeAccessor.readTextNodeValue(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION, SUFFIX) + val intermediateNode = JacksonSchemaNodeAccessor.resolveRelativeNode(rawSchemaNode, X_INTELLIJ_LANGUAGE_INJECTION) ?: return null + return JacksonSchemaNodeAccessor.readTextNodeValue(intermediateNode, SUFFIX) } override fun isShouldValidateAgainstJSType(): Boolean { diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MergedJsonSchemaObjectView.kt b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MergedJsonSchemaObjectView.kt index 13fae51de4ca..4a0ec8735038 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MergedJsonSchemaObjectView.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MergedJsonSchemaObjectView.kt @@ -37,15 +37,15 @@ internal class MergedJsonSchemaObjectView( return other.ref } - override fun readChildNodeValue(vararg childNodeName: String): String? { + override fun readChildNodeValue(childNodeName: String): String? { return baseIfConditionOrOtherWithArgument(JsonSchemaObject::readChildNodeValue, childNodeName, String?::isNotBlank) } - override fun hasChildNode(vararg childNodeName: String): Boolean { + override fun hasChildNode(childNodeName: String): Boolean { return booleanOrWithArgument(JsonSchemaObject::hasChildNode, childNodeName) } - override fun hasChildFieldsExcept(namesToSkip: Array): Boolean { + override fun hasChildFieldsExcept(namesToSkip: List): Boolean { return booleanOrWithArgument(JsonSchemaObject::hasChildFieldsExcept, namesToSkip) } diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MissingJsonSchemaObject.kt b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MissingJsonSchemaObject.kt index 6a72f2e2376f..9b26cc78e05e 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MissingJsonSchemaObject.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/MissingJsonSchemaObject.kt @@ -270,7 +270,7 @@ internal object MissingJsonSchemaObject : JsonSchemaObjectBackedByJacksonBase(Mi throw UnsupportedOperationException(ERROR_MESSAGE) } - override fun hasChildFieldsExcept(namesToSkip: Array): Boolean { + override fun hasChildFieldsExcept(namesToSkip: List): Boolean { throw UnsupportedOperationException(ERROR_MESSAGE) } diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/schemaParsingUtils.kt b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/schemaParsingUtils.kt index cc15dc41ee0c..6156b9db1585 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/nodes/schemaParsingUtils.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/nodes/schemaParsingUtils.kt @@ -19,49 +19,43 @@ internal object JacksonSchemaNodeAccessor : RawJsonSchemaNodeAccessor return rootNode.at(compiledPointer)?.takeIf { it !is MissingNode } } - override fun resolveRelativeNode(node: JsonNode, vararg relativeChildPath: String): JsonNode? { - return relativeChildPath.asSequence().map(::escapeForbiddenJsonPointerSymbols).fold(node) { currentNode, childName -> - val childByName = currentNode.get(childName) - if (childByName != null) return@fold childByName - - val maybeIndex = childName.toIntOrNull() ?: return@fold MissingNode.getInstance() - return currentNode.get(maybeIndex) - }.takeIf { it !is MissingNode } + override fun resolveRelativeNode(node: JsonNode, relativeChildPath: String?): JsonNode? { + return getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) } - override fun hasChildNode(node: JsonNode, vararg relativeChildPath: String): Boolean { + override fun hasChildNode(node: JsonNode, relativeChildPath: String): Boolean { if (!node.isObject) return false - return !getChild(node, *relativeChildPath).isMissingNode + return !getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath).isMissingNode } - override fun readUntypedNodeValueAsText(node: JsonNode, vararg relativeChildPath: String): String? { + override fun readUntypedNodeValueAsText(node: JsonNode, relativeChildPath: String?): String? { if (!node.isObject) return null - return getChild(node, *relativeChildPath) + return getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) .takeIf { it !is MissingNode } ?.toPrettyString() } - override fun readTextNodeValue(node: JsonNode, vararg relativeChildPath: String): String? { + override fun readTextNodeValue(node: JsonNode, relativeChildPath: String?): String? { if (!node.isObject) return null - val maybeString = getChild(node, *relativeChildPath) + val maybeString = getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) return if (maybeString.isTextual) maybeString.asText() else null } - override fun readBooleanNodeValue(node: JsonNode, vararg relativeChildPath: String): Boolean? { - if (!(node.isObject || (node.isBoolean && relativeChildPath.isEmpty()))) return null - val maybeBoolean = getChild(node, *relativeChildPath) + override fun readBooleanNodeValue(node: JsonNode, relativeChildPath: String?): Boolean? { + if (!(node.isObject || (node.isBoolean && relativeChildPath == null))) return null + val maybeBoolean = if (relativeChildPath == null) node else getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) return if (maybeBoolean.isBoolean) maybeBoolean.asBoolean() else null } - override fun readNumberNodeValue(node: JsonNode, vararg relativeChildPath: String): Number? { + override fun readNumberNodeValue(node: JsonNode, relativeChildPath: String?): Number? { if (!node.isObject) return null - val maybeNumber = getChild(node, *relativeChildPath) + val maybeNumber = getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) return when { maybeNumber.isInt -> maybeNumber.asInt() maybeNumber.isDouble -> maybeNumber.asDouble() @@ -70,45 +64,43 @@ internal object JacksonSchemaNodeAccessor : RawJsonSchemaNodeAccessor } } - override fun readUntypedNodesCollection(node: JsonNode, vararg relativeChildPath: String): Sequence? { - return getChildArrayItems(node, *relativeChildPath) + override fun readUntypedNodesCollection(node: JsonNode, relativeChildPath: String?): Sequence? { + return getChildArrayItems(node, relativeChildPath) ?.mapNotNull(JacksonSchemaNodeAccessor::readAnything) } - override fun readNodeAsMapEntries(node: JsonNode, vararg relativeChildPath: String): Sequence>? { + override fun readNodeAsMapEntries(node: JsonNode, relativeChildPath: String?): Sequence>? { if (!node.isObject) return null - return getChild(node, *relativeChildPath) + return getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) .takeIf { it.isObject } ?.fields() ?.asSequence() ?.map { it.key to it.value } } - override fun readNodeAsMultiMapEntries(node: JsonNode, vararg relativeChildPath: String): Sequence>>? { - return readNodeAsMapEntries(node, *relativeChildPath) + override fun readNodeAsMultiMapEntries(node: JsonNode, relativeChildPath: String?): Sequence>>? { + return readNodeAsMapEntries(node, relativeChildPath) ?.mapNotNull { (stringKey, arrayValue) -> if (!arrayValue.isArray) return@mapNotNull null stringKey to arrayValue.elements().asSequence().mapNotNull { if (it.isTextual) it.asText() else null }.toList() } } - override fun readNodeKeys(node: JsonNode, vararg relativeChildPath: String): Sequence? { - val expandedNode = if (relativeChildPath.isEmpty()) - node - else - getChild(node, *relativeChildPath) + override fun readNodeKeys(node: JsonNode, relativeChildPath: String?): Sequence? { + val expandedNode = getExistingChildByNonEmptyPathOrSelf(node, relativeChildPath) return expandedNode.fieldNames().takeIf(Iterator::hasNext)?.asSequence() } - private fun getChild(node: JsonNode, vararg relativePath: String): JsonNode { - return relativePath.fold(node) { parentNode, childName -> - parentNode.get(childName) ?: return MissingNode.getInstance() - } + private fun getExistingChildByNonEmptyPathOrSelf(node: JsonNode, directChildName: String? = null): JsonNode { + return if (directChildName == null) + node + else + node.get(directChildName) ?: MissingNode.getInstance() } - private fun getChildArrayItems(node: JsonNode, vararg name: String): Sequence? { + private fun getChildArrayItems(node: JsonNode, name: String?): Sequence? { if (!node.isObject) return null - return getChild(node, *name) + return getExistingChildByNonEmptyPathOrSelf(node, name) .takeIf { it.isArray } ?.asSafely() ?.elements() diff --git a/json/src/com/jetbrains/jsonSchema/impl/light/versions/JsonSchemaInterpretationStrategy.kt b/json/src/com/jetbrains/jsonSchema/impl/light/versions/JsonSchemaInterpretationStrategy.kt index c2a2e5074a96..3610aadf89fa 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/light/versions/JsonSchemaInterpretationStrategy.kt +++ b/json/src/com/jetbrains/jsonSchema/impl/light/versions/JsonSchemaInterpretationStrategy.kt @@ -110,18 +110,11 @@ interface JsonSchemaInterpretationStrategy { || baseSchema.rawFile != null && baseSchema.rawFile == childSchema.rawFile } - private fun isNodeWithDifferentPointers( - baseSchema: JsonSchemaObject, - childSchema: JsonSchemaObject, - ): Boolean { - return baseSchema.pointer != childSchema.pointer - } - private fun isApplicatorBranchWithNonEmptyParent( baseSchema: JsonSchemaObject, childSchema: JsonSchemaObject, ): Boolean { - if (!baseSchema.hasChildFieldsExcept(ONE_OF, ALL_OF, ANY_OF)) return false + if (!baseSchema.hasChildFieldsExcept(APPLICATOR_MARKERS)) return false return sequence { yieldAll(baseSchema.oneOf.orEmpty()) @@ -134,10 +127,13 @@ interface JsonSchemaInterpretationStrategy { baseSchema: JsonSchemaObject, childSchema: JsonSchemaObject, ): Boolean { - if (!baseSchema.hasChildFieldsExcept(IF, THEN, ELSE)) return false + if (!baseSchema.hasChildFieldsExcept(IF_ELSE_MARKERS)) return false return baseSchema.ifThenElse?.any { condition -> condition.then == childSchema || condition.`else` == childSchema } ?: false } -} \ No newline at end of file +} + +private val IF_ELSE_MARKERS = listOf(IF, THEN, ELSE) +private val APPLICATOR_MARKERS = listOf(ONE_OF, ALL_OF, ANY_OF) \ No newline at end of file