diff --git a/gradle.properties b/gradle.properties index fd5fd742..1bfae8ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ org.gradle.jvmargs=-Xmx1G org.gradle.java.installations.auto-download=false org.gradle.daemon=false -version=0.2.4-SNAPSHOT +version=0.3.0-SNAPSHOT group=io.github.optimumcode \ No newline at end of file diff --git a/json-schema-validator/api/json-schema-validator.api b/json-schema-validator/api/json-schema-validator.api index ac63b4e5..842daa58 100644 --- a/json-schema-validator/api/json-schema-validator.api +++ b/json-schema-validator/api/json-schema-validator.api @@ -94,7 +94,7 @@ public abstract interface class io/github/optimumcode/json/schema/FormatValidato public static final field Companion Lio/github/optimumcode/json/schema/FormatValidator$Companion; public static fun Invalid ()Lio/github/optimumcode/json/schema/FormatValidationResult; public static fun Valid ()Lio/github/optimumcode/json/schema/FormatValidationResult; - public abstract fun validate (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/FormatValidationResult; + public abstract fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;)Lio/github/optimumcode/json/schema/FormatValidationResult; } public final class io/github/optimumcode/json/schema/FormatValidator$Companion { @@ -108,6 +108,8 @@ public final class io/github/optimumcode/json/schema/JsonSchema { public static final fun fromDefinition (Ljava/lang/String;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema; public static final fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchema; public static final fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema; + public final fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/ErrorCollector;)Z + public final fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/OutputCollector$Provider;)Ljava/lang/Object; public final fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/ErrorCollector;)Z public final fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/OutputCollector$Provider;)Ljava/lang/Object; } @@ -337,9 +339,15 @@ public abstract interface class io/github/optimumcode/json/schema/extension/Exte } public abstract interface class io/github/optimumcode/json/schema/extension/ExternalAssertion { + public abstract fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z public abstract fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z } +public final class io/github/optimumcode/json/schema/extension/ExternalAssertion$DefaultImpls { + public static fun validate (Lio/github/optimumcode/json/schema/extension/ExternalAssertion;Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z + public static fun validate (Lio/github/optimumcode/json/schema/extension/ExternalAssertion;Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z +} + public abstract interface class io/github/optimumcode/json/schema/extension/ExternalAssertionContext { public abstract fun getAnnotationCollector ()Lio/github/optimumcode/json/schema/extension/ExternalAnnotationCollector; public abstract fun getObjectPath ()Lio/github/optimumcode/json/pointer/JsonPointer; @@ -354,3 +362,28 @@ public abstract interface class io/github/optimumcode/json/schema/extension/Exte public abstract fun getSchemaPath ()Lio/github/optimumcode/json/pointer/JsonPointer; } +public abstract interface class io/github/optimumcode/json/schema/model/AbstractElement { + public abstract fun toString ()Ljava/lang/String; +} + +public abstract interface class io/github/optimumcode/json/schema/model/ArrayElement : io/github/optimumcode/json/schema/model/AbstractElement, kotlin/sequences/Sequence { + public abstract fun get (I)Lio/github/optimumcode/json/schema/model/AbstractElement; + public abstract fun getSize ()I +} + +public abstract interface class io/github/optimumcode/json/schema/model/ObjectElement : io/github/optimumcode/json/schema/model/AbstractElement, kotlin/sequences/Sequence { + public abstract fun contains (Ljava/lang/String;)Z + public abstract fun get (Ljava/lang/String;)Lio/github/optimumcode/json/schema/model/AbstractElement; + public abstract fun getKeys ()Ljava/util/Set; + public abstract fun getSize ()I +} + +public abstract interface class io/github/optimumcode/json/schema/model/PrimitiveElement : io/github/optimumcode/json/schema/model/AbstractElement { + public abstract fun getContent ()Ljava/lang/String; + public abstract fun getNumber ()Ljava/lang/Number; + public abstract fun isBoolean ()Z + public abstract fun isNull ()Z + public abstract fun isNumber ()Z + public abstract fun isString ()Z +} + diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/FormatValidator.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/FormatValidator.kt index 322b4e90..6351a596 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/FormatValidator.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/FormatValidator.kt @@ -1,11 +1,12 @@ package io.github.optimumcode.json.schema -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement import kotlin.jvm.JvmStatic /** - * The [FormatValidator] is used to check whether the [JsonElement] matches the expected format. - * If the [JsonElement] is not of the required type (e.g. validator expects string but the [JsonElement] is an object) + * The [FormatValidator] is used to check whether the [AbstractElement] matches the expected format. + * If the [AbstractElement] is not of the required type + * (e.g. validator expects string but the [AbstractElement] is an object) * the validator **MUST** return [FormatValidator.Valid] result */ @ExperimentalApi @@ -16,7 +17,7 @@ public interface FormatValidator { * @param element JSON element to validate against the expected format * @return the result of the validation */ - public fun validate(element: JsonElement): FormatValidationResult + public fun validate(element: AbstractElement): FormatValidationResult public companion object { @Suppress("ktlint:standard:function-naming", "FunctionName") diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/JsonSchema.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/JsonSchema.kt index dcede73d..a0e3d16c 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/JsonSchema.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/JsonSchema.kt @@ -6,6 +6,8 @@ import io.github.optimumcode.json.schema.internal.DefaultAssertionContext import io.github.optimumcode.json.schema.internal.DefaultReferenceResolver import io.github.optimumcode.json.schema.internal.IsolatedLoader import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion +import io.github.optimumcode.json.schema.internal.wrapper.wrap +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -28,6 +30,31 @@ public class JsonSchema internal constructor( public fun validate( value: JsonElement, errorCollector: ErrorCollector, + ): Boolean = validate(value.wrap(), errorCollector) + + /** + * Validates [value] against this [JsonSchema]. + * The provided [outputCollectorProvider] will be used to create [OutputCollector] + * which collects the validation result. + * + * @return validation result depending on [outputCollectorProvider] + */ + public fun validate( + value: JsonElement, + outputCollectorProvider: OutputCollector.Provider, + ): T = validate(value.wrap(), outputCollectorProvider) + + /** + * Validates [value] against this [JsonSchema]. + * If the [value] is valid against the schema the function returns `true`. + * Otherwise, it returns `false`. + * + * All reported errors will be reported to [ErrorCollector.onError] + */ + @ExperimentalApi + public fun validate( + value: AbstractElement, + errorCollector: ErrorCollector, ): Boolean { val context = DefaultAssertionContext(JsonPointer.ROOT, referenceResolver) return DelegateOutputCollector(errorCollector).use { @@ -42,8 +69,9 @@ public class JsonSchema internal constructor( * * @return validation result depending on [outputCollectorProvider] */ + @ExperimentalApi public fun validate( - value: JsonElement, + value: AbstractElement, outputCollectorProvider: OutputCollector.Provider, ): T { val context = DefaultAssertionContext(JsonPointer.ROOT, referenceResolver) diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAnnotationCollector.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAnnotationCollector.kt index e1883126..20de816f 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAnnotationCollector.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAnnotationCollector.kt @@ -1,7 +1,9 @@ package io.github.optimumcode.json.schema.extension import io.github.optimumcode.json.schema.AnnotationKey +import io.github.optimumcode.json.schema.ExperimentalApi +@ExperimentalApi public interface ExternalAnnotationCollector { /** * Adds annotation with provided [key] diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertion.kt index 305c51e7..0785ea59 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertion.kt @@ -1,6 +1,9 @@ package io.github.optimumcode.json.schema.extension import io.github.optimumcode.json.schema.ErrorCollector +import io.github.optimumcode.json.schema.ExperimentalApi +import io.github.optimumcode.json.schema.internal.wrapper.JsonWrapper +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement /** @@ -8,6 +11,8 @@ import kotlinx.serialization.json.JsonElement * This interface **does not** allow implementing custom applicators. * Only simple assertions (like: _format_, _type_) can be implemented. */ +@Suppress("detekt:ForbiddenComment") +@ExperimentalApi public interface ExternalAssertion { /** * Validates passes [element]. @@ -24,9 +29,28 @@ public interface ExternalAssertion { * @param errorCollector handler for [io.github.optimumcode.json.schema.ValidationError] produced by assertion * @return `true` if element is valid against assertion. Otherwise, returns `false` */ + public fun validate( + element: AbstractElement, + context: ExternalAssertionContext, + errorCollector: ErrorCollector, + ): Boolean = + // TODO: remove it after two minor/major release + validate(element.unwrap(), context, errorCollector) + + // TODO: increase level to error in the next release + @Deprecated( + message = "override validate(AbstractElement, ExternalAssertionContext, ErrorCollector) instead", + level = DeprecationLevel.WARNING, + ) public fun validate( element: JsonElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, - ): Boolean -} \ No newline at end of file + ): Boolean = throw UnsupportedOperationException() +} + +internal fun AbstractElement.unwrap(): JsonElement = + when (this) { + is JsonWrapper -> unwrap() + else -> error("unsupported element type: ${this::class.simpleName}") + } \ No newline at end of file diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionContext.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionContext.kt index 3c2e70a4..b66e005d 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionContext.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionContext.kt @@ -1,7 +1,9 @@ package io.github.optimumcode.json.schema.extension import io.github.optimumcode.json.pointer.JsonPointer +import io.github.optimumcode.json.schema.ExperimentalApi +@ExperimentalApi public interface ExternalAssertionContext { /** * A JSON pointer to the currently validating element in the object that is being validated diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionFactory.kt index 5f03f489..efaf2e88 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalAssertionFactory.kt @@ -1,7 +1,9 @@ package io.github.optimumcode.json.schema.extension +import io.github.optimumcode.json.schema.ExperimentalApi import kotlinx.serialization.json.JsonElement +@ExperimentalApi public interface ExternalAssertionFactory { /** * A keyword that is associated with the [ExternalAssertion] created by this factory. diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalLoadingContext.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalLoadingContext.kt index 96c6785a..5ce18897 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalLoadingContext.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/extension/ExternalLoadingContext.kt @@ -1,7 +1,9 @@ package io.github.optimumcode.json.schema.extension import io.github.optimumcode.json.pointer.JsonPointer +import io.github.optimumcode.json.schema.ExperimentalApi +@ExperimentalApi public interface ExternalLoadingContext { /** * A JSON pointer to the current position in schema associated with currently processing element diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/BooleanSchemaAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/BooleanSchemaAssertion.kt index 1aeaabcd..23af77cd 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/BooleanSchemaAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/BooleanSchemaAssertion.kt @@ -3,13 +3,13 @@ package io.github.optimumcode.json.schema.internal import io.github.optimumcode.json.pointer.JsonPointer import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.ValidationError -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal class FalseSchemaAssertion( private val path: JsonPointer, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { @@ -28,7 +28,7 @@ internal class FalseSchemaAssertion( internal object TrueSchemaAssertion : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaAssertion.kt index bb215a25..5847a4d8 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaAssertion.kt @@ -1,7 +1,7 @@ package io.github.optimumcode.json.schema.internal import io.github.optimumcode.json.schema.OutputCollector -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal interface JsonSchemaAssertion { /** @@ -20,7 +20,7 @@ internal interface JsonSchemaAssertion { * @return `true` if element is valid against assertion. Otherwise, returns `false` */ fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaRoot.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaRoot.kt index 40779afb..9aad8185 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaRoot.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/JsonSchemaRoot.kt @@ -3,7 +3,7 @@ package io.github.optimumcode.json.schema.internal import com.eygraber.uri.Uri import io.github.optimumcode.json.pointer.JsonPointer import io.github.optimumcode.json.schema.OutputCollector -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal class JsonSchemaRoot( private val scopeId: Uri, @@ -12,7 +12,7 @@ internal class JsonSchemaRoot( private val canBeReferencedRecursively: Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RecursiveRefSchemaAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RecursiveRefSchemaAssertion.kt index 8cecd8c6..ecb17e18 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RecursiveRefSchemaAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RecursiveRefSchemaAssertion.kt @@ -5,14 +5,14 @@ import io.github.optimumcode.json.pointer.plus import io.github.optimumcode.json.pointer.relative import io.github.optimumcode.json.schema.AbsoluteLocation import io.github.optimumcode.json.schema.OutputCollector -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal class RecursiveRefSchemaAssertion( private val basePath: JsonPointer, private val refId: RefId, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RefSchemaAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RefSchemaAssertion.kt index b1f91712..179a5b9c 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RefSchemaAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RefSchemaAssertion.kt @@ -6,7 +6,7 @@ import io.github.optimumcode.json.pointer.plus import io.github.optimumcode.json.pointer.relative import io.github.optimumcode.json.schema.AbsoluteLocation import io.github.optimumcode.json.schema.OutputCollector -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal class RefSchemaAssertion( private val basePath: JsonPointer, @@ -17,7 +17,7 @@ internal class RefSchemaAssertion( private lateinit var refAbsolutePath: Uri override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/ExternalAssertionFactoryAdapter.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/ExternalAssertionFactoryAdapter.kt index a14367e8..d5034cdc 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/ExternalAssertionFactoryAdapter.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/ExternalAssertionFactoryAdapter.kt @@ -7,6 +7,7 @@ import io.github.optimumcode.json.schema.extension.ExternalAssertionFactory import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement internal class ExternalAssertionFactoryAdapter( @@ -25,7 +26,7 @@ private class ExternalAssertionAdapter( private val externalAssertion: ExternalAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean = diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AdditionalItemsAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AdditionalItemsAssertion.kt index 64200fa2..e1c53831 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AdditionalItemsAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AdditionalItemsAssertion.kt @@ -5,8 +5,9 @@ import io.github.optimumcode.json.schema.AnnotationKey import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.lastIndex internal class AdditionalItemsAssertion( private val location: JsonPointer, @@ -16,12 +17,12 @@ internal class AdditionalItemsAssertion( private val returnIfNoIndex: Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } val lastProcessedIndex: Int = diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AllItemsAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AllItemsAssertion.kt index e5727bb5..2665b4a5 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AllItemsAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/AllItemsAssertion.kt @@ -5,8 +5,9 @@ import io.github.optimumcode.json.schema.AnnotationKey import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.lastIndex internal class AllItemsAssertion( private val location: JsonPointer, @@ -14,12 +15,12 @@ internal class AllItemsAssertion( private val annotationKey: AnnotationKey, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ArrayLengthAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ArrayLengthAssertion.kt index 78da775b..edff79ac 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ArrayLengthAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ArrayLengthAssertion.kt @@ -5,8 +5,8 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement internal class ArrayLengthAssertion( private val path: JsonPointer, @@ -15,12 +15,12 @@ internal class ArrayLengthAssertion( private val check: (Int, Int) -> Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } if (check(element.size, length)) { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactory.kt index 21355e40..66e07fb9 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactory.kt @@ -9,7 +9,8 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory -import kotlinx.serialization.json.JsonArray +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement import kotlinx.serialization.json.JsonElement @Suppress("unused") @@ -31,12 +32,12 @@ private class ContainsAssertion( private val containsAssertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } val foundElements = diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactoryDraft202012.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactoryDraft202012.kt index 6c67c8fb..f23bc6c2 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactoryDraft202012.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/ContainsAssertionFactoryDraft202012.kt @@ -10,7 +10,9 @@ import io.github.optimumcode.json.schema.internal.AssertionFactory import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.util.integerOrNull -import kotlinx.serialization.json.JsonArray +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive @@ -45,9 +47,9 @@ internal object ContainsAssertionFactoryDraft202012 : AssertionFactory { val allowNoMatch = element[MIN_CONTAINS_PROPERTY] ?.jsonPrimitive + ?.let(::JsonPrimitiveWrapper) ?.integerOrNull - ?.let { it == 0 } - ?: false + ?.let { it == 0 } == true return ContainsAssertionDraft202012(elementContext.schemaPath, containsAssertion, allowNoMatch) } } @@ -58,12 +60,12 @@ private class ContainsAssertionDraft202012( private val allowNoMatch: Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } val foundElements = diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/CountContainsAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/CountContainsAssertion.kt index 15bc837b..5a60d1c0 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/CountContainsAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/CountContainsAssertion.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal class CountContainsAssertion( private val path: JsonPointer, @@ -15,7 +15,7 @@ internal class CountContainsAssertion( private val operation: (expected: Number, actual: Number) -> Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactory.kt index f445610c..dfed3894 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactory.kt @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MaxContainsAssertionFactory : AbstractAssertionFactory("maxConta context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return CountContainsAssertion( path = context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactoryDraft202012.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactoryDraft202012.kt index 5756f1bd..e40fdf6d 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactoryDraft202012.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxContainsAssertionFactoryDraft202012.kt @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MaxContainsAssertionFactoryDraft202012 : AbstractAssertionFactor context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return CountContainsAssertion( path = context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxItemsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxItemsAssertionFactory.kt index 4d6f7b4a..d64973c1 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxItemsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MaxItemsAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MaxItemsAssertionFactory : AbstractAssertionFactory("maxItems") context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return ArrayLengthAssertion( context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactory.kt index 13b6e539..caa3ebc0 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactory.kt @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MinContainsAssertionFactory : AbstractAssertionFactory("minConta context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return CountContainsAssertion( path = context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactoryDraft202012.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactoryDraft202012.kt index 29a585f6..3b7dc1bf 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactoryDraft202012.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinContainsAssertionFactoryDraft202012.kt @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MinContainsAssertionFactoryDraft202012 : AbstractAssertionFactor context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return CountContainsAssertion( path = context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinItemsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinItemsAssertionFactory.kt index c4d474ae..9dc47a94 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinItemsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/MinItemsAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MinItemsAssertionFactory : AbstractAssertionFactory("minItems") context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxItemsValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxItemsValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxItemsValue >= 0) { "$property must be a non-negative integer" } return ArrayLengthAssertion( context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/PrefixItemsAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/PrefixItemsAssertion.kt index 09b404a0..81d795be 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/PrefixItemsAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/PrefixItemsAssertion.kt @@ -5,8 +5,8 @@ import io.github.optimumcode.json.schema.AnnotationKey import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement internal class PrefixItemsAssertion( private val location: JsonPointer, @@ -14,12 +14,12 @@ internal class PrefixItemsAssertion( private val annotationKey: AnnotationKey, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } var valid = true diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UnevaluatedItemsAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UnevaluatedItemsAssertion.kt index ac675e55..2d6d84b5 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UnevaluatedItemsAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UnevaluatedItemsAssertion.kt @@ -6,8 +6,9 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AnnotationCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.lastIndex internal class UnevaluatedItemsAssertion( private val location: JsonPointer, @@ -18,12 +19,12 @@ internal class UnevaluatedItemsAssertion( private val processedIndexesKey: AnnotationKey>? = null, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } val annotationCollector: AnnotationCollector = context.annotationCollector diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UniqueItemsAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UniqueItemsAssertionFactory.kt index 5ccaa5d9..247561d6 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UniqueItemsAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/array/UniqueItemsAssertionFactory.kt @@ -9,7 +9,8 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.TrueSchemaAssertion import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.areEqual -import kotlinx.serialization.json.JsonArray +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.booleanOrNull @@ -34,18 +35,18 @@ private class UniqueItemsAssertion( private val path: JsonPointer, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonArray) { + if (element !is ArrayElement) { return@use true } if (element.size < 2) { return@use true } - var duplicates: MutableList? = null + var duplicates: MutableList? = null val uniqueItems = buildList { element.forEach { el -> @@ -55,7 +56,7 @@ private class UniqueItemsAssertion( if (duplicates == null) { duplicates = mutableListOf() } - duplicates?.add(el) + duplicates.add(el) } } } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AllOfAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AllOfAssertionFactory.kt index 5bdf03c0..a3af1b56 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AllOfAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AllOfAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement @Suppress("unused") internal object AllOfAssertionFactory : AbstractAssertionsCollectionFactory("allOf") { @@ -20,7 +20,7 @@ private class AllOfAssertion( private val assertions: List, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AnyOfAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AnyOfAssertionFactory.kt index c6b7f8fc..b761374a 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AnyOfAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/AnyOfAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement @Suppress("unused") internal object AnyOfAssertionFactory : AbstractAssertionsCollectionFactory("anyOf") { @@ -20,7 +20,7 @@ private class AnyOfAssertion( private val assertions: List, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ElseAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ElseAssertionFactory.kt index 13f67793..8f4bc68a 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ElseAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ElseAssertionFactory.kt @@ -6,6 +6,7 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement internal object ElseAssertionFactory : AbstractAssertionFactory("else") { @@ -24,7 +25,7 @@ private class ElseAssertion( private val assertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/IfAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/IfAssertionFactory.kt index e65e2f44..82dc9be8 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/IfAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/IfAssertionFactory.kt @@ -7,6 +7,7 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement internal object IfAssertionFactory : AbstractAssertionFactory("if") { @@ -26,7 +27,7 @@ private class IfAssertion( private val condition: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/NotAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/NotAssertionFactory.kt index 4ea3fc0f..dba7a7b2 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/NotAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/NotAssertionFactory.kt @@ -7,6 +7,7 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement @Suppress("unused") @@ -26,7 +27,7 @@ private class NotAssertion( private val delegate: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/OneOfAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/OneOfAssertionFactory.kt index 542cb241..48a19073 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/OneOfAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/OneOfAssertionFactory.kt @@ -6,7 +6,7 @@ import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext -import kotlinx.serialization.json.JsonElement +import io.github.optimumcode.json.schema.model.AbstractElement internal object OneOfAssertionFactory : AbstractAssertionsCollectionFactory("oneOf") { override fun createAssertion( @@ -20,7 +20,7 @@ private class OneOfAssertion( private val assertions: List, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ThenAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ThenAssertionFactory.kt index da4add78..892ec7c9 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ThenAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/condition/ThenAssertionFactory.kt @@ -6,6 +6,7 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement internal object ThenAssertionFactory : AbstractAssertionFactory("then") { @@ -24,7 +25,7 @@ private class ThenAssertion( private val assertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/ConstAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/ConstAssertionFactory.kt index e0e63c56..e1f49959 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/ConstAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/ConstAssertionFactory.kt @@ -8,6 +8,8 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.areEqual +import io.github.optimumcode.json.schema.internal.wrapper.wrap +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement @Suppress("unused") @@ -16,16 +18,16 @@ internal object ConstAssertionFactory : AbstractAssertionFactory("const") { element: JsonElement, context: LoadingContext, ): JsonSchemaAssertion { - return ConstAssertion(context.schemaPath, element) + return ConstAssertion(context.schemaPath, element.wrap()) } } private class ConstAssertion( private val path: JsonPointer, - private val constValue: JsonElement, + private val constValue: AbstractElement, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/EnumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/EnumAssertionFactory.kt index ebc8c4d3..5463ef28 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/EnumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/EnumAssertionFactory.kt @@ -8,6 +8,8 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.areEqual +import io.github.optimumcode.json.schema.internal.wrapper.wrap +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement @@ -18,22 +20,25 @@ internal object EnumAssertionFactory : AbstractAssertionFactory("enum") { ): JsonSchemaAssertion { require(element is JsonArray) { "$property must be an array" } require(element.isNotEmpty()) { "$property must have at least one element" } + // NOTE: when migrate to abstract element for loading schema + // we need to use `io.github.optimumcode.json.schema.internal.util.areEqual` + // to remove duplicated elements val uniqueElements = element.toSet() require(uniqueElements.size == element.size) { "$property must consist of unique elements" } - return EnumAssertion(context.schemaPath, uniqueElements) + return EnumAssertion(context.schemaPath, uniqueElements.map { it.wrap() }) } } private class EnumAssertion( private val path: JsonPointer, - private val possibleElements: Set, + private val possibleElements: Collection, ) : JsonSchemaAssertion { init { require(possibleElements.isNotEmpty()) { "at least one element must be set" } } override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/FormatAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/FormatAssertionFactory.kt index 9af79abf..dd7c5954 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/FormatAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/FormatAssertionFactory.kt @@ -32,6 +32,7 @@ import io.github.optimumcode.json.schema.internal.formats.UriFormatValidator import io.github.optimumcode.json.schema.internal.formats.UriReferenceFormatValidator import io.github.optimumcode.json.schema.internal.formats.UriTemplateFormatValidator import io.github.optimumcode.json.schema.internal.formats.UuidFormatValidator +import io.github.optimumcode.json.schema.model.AbstractElement import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -99,7 +100,7 @@ private class FormatAssertion( private val assertion: Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/TypeAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/TypeAssertionFactory.kt index db8198f7..42db9f73 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/TypeAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/general/TypeAssertionFactory.kt @@ -7,25 +7,25 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory -import io.github.optimumcode.json.schema.internal.factories.number.util.number import io.github.optimumcode.json.schema.internal.util.parseNumberParts +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.ObjectElement +import io.github.optimumcode.json.schema.model.PrimitiveElement import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.booleanOrNull internal object TypeAssertionFactory : AbstractAssertionFactory("type") { private val typeValidations: Map = - linkedMapOf Boolean>( - "null" to { it is JsonNull }, - "string" to { it is JsonPrimitive && it.isString }, - "boolean" to { it is JsonPrimitive && !it.isString && it.booleanOrNull != null }, - "number" to { it is JsonPrimitive && !it.isString && (it.number != null) }, - "integer" to { it is JsonPrimitive && !it.isString && parseNumberParts(it)?.fractional == 0L }, - "array" to { it is JsonArray }, - "object" to { it is JsonObject }, + linkedMapOf Boolean>( + "null" to { it is PrimitiveElement && it.isNull }, + "string" to { it is PrimitiveElement && it.isString }, + "boolean" to { it is PrimitiveElement && it.isBoolean }, + "number" to { it is PrimitiveElement && it.isNumber }, + "integer" to { it is PrimitiveElement && it.isNumber && parseNumberParts(it)?.fractional == 0L }, + "array" to { it is ArrayElement }, + "object" to { it is ObjectElement }, ).mapValues { Validation(it.key, it.value) } override fun createFromProperty( @@ -68,7 +68,7 @@ internal object TypeAssertionFactory : AbstractAssertionFactory("type") { private class Validation( val name: String, - val check: (JsonElement) -> Boolean, + val check: (AbstractElement) -> Boolean, ) private class TypeAssertion( @@ -80,7 +80,7 @@ private class TypeAssertion( } override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MaximumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MaximumAssertionFactory.kt index 79be0619..ea769379 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MaximumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MaximumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -41,7 +41,7 @@ internal object Draft4MaximumAssertionFactory : AssertionFactory { ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MinimumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MinimumAssertionFactory.kt index bb37e10b..d6a63e5d 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MinimumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/Draft4MinimumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -41,7 +41,7 @@ internal object Draft4MinimumAssertionFactory : AssertionFactory { ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMaximumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMaximumAssertionFactory.kt index abbf212d..86685b32 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMaximumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMaximumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -17,7 +17,7 @@ internal object ExclusiveMaximumAssertionFactory : AbstractAssertionFactory("exc ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMinimumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMinimumAssertionFactory.kt index 6fbfb7d0..7b1db2c9 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMinimumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/ExclusiveMinimumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -17,7 +17,7 @@ internal object ExclusiveMinimumAssertionFactory : AbstractAssertionFactory("exc ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MaximumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MaximumAssertionFactory.kt index 71d88a2b..b8121167 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MaximumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MaximumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -17,7 +17,7 @@ internal object MaximumAssertionFactory : AbstractAssertionFactory("maximum") { ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MinimumAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MinimumAssertionFactory.kt index 952810e6..db0245a0 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MinimumAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MinimumAssertionFactory.kt @@ -5,7 +5,7 @@ import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion import io.github.optimumcode.json.schema.internal.factories.number.util.compareTo -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -17,7 +17,7 @@ internal object MinimumAssertionFactory : AbstractAssertionFactory("minimum") { ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val maximumValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } return NumberComparisonAssertion( context.schemaPath, maximumValue, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MultipleOfAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MultipleOfAssertionFactory.kt index feffac48..2c69054e 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MultipleOfAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/MultipleOfAssertionFactory.kt @@ -4,7 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.factories.number.util.NumberComparisonAssertion -import io.github.optimumcode.json.schema.internal.factories.number.util.number +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlin.math.abs @@ -22,7 +22,7 @@ internal object MultipleOfAssertionFactory : AbstractAssertionFactory("multipleO ): JsonSchemaAssertion { require(element is JsonPrimitive) { "$property must be a number" } val multipleOfValue: Number = - requireNotNull(element.number) { "$property must be a valid number" } + requireNotNull(JsonPrimitiveWrapper(element).number) { "$property must be a valid number" } require( when (multipleOfValue) { is Double -> multipleOfValue > 0.0 @@ -45,14 +45,14 @@ private fun isMultipleOf( b: Number, ): Boolean = when (a) { - is Double -> a isMultipleOf b + is Double -> a.isFinite() && a isMultipleOf b is Long -> a isMultipleOf b else -> false } private infix fun Double.isMultipleOf(number: Number): Boolean = when (number) { - is Double -> isZero(rem(this, number)) + is Double -> number.isFinite() && isZero(rem(this, number)) is Long -> isZero((this % number)) else -> false } @@ -60,7 +60,7 @@ private infix fun Double.isMultipleOf(number: Number): Boolean = private infix fun Long.isMultipleOf(number: Number): Boolean = when (number) { is Long -> this % number == 0L - is Double -> isZero(rem(this, number)) + is Double -> number.isFinite() && isZero(rem(this, number)) else -> false } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/util/NumberUtil.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/util/NumberUtil.kt index 792ab6ca..13789656 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/util/NumberUtil.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/number/util/NumberUtil.kt @@ -5,16 +5,11 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.doubleOrNull -import kotlinx.serialization.json.longOrNull +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement -internal val JsonPrimitive.number: Number? - get() = longOrNull ?: doubleOrNull - -internal operator fun Number.compareTo(maxValue: Number): Int { - return when (this) { +internal operator fun Number.compareTo(maxValue: Number): Int = + when (this) { is Double -> when (maxValue) { is Double -> compareTo(maxValue) @@ -30,6 +25,7 @@ internal operator fun Number.compareTo(maxValue: Number): Int { is Int -> compareTo(maxValue) else -> error("unexpected other value type: ${maxValue::class.simpleName}") } + is Int -> when (maxValue) { is Double -> compareTo(maxValue) @@ -37,9 +33,9 @@ internal operator fun Number.compareTo(maxValue: Number): Int { is Int -> compareTo(maxValue) else -> error("unexpected other value type: ${maxValue::class.simpleName}") } + else -> error("unexpected value type: ${this::class.simpleName}") } -} internal class NumberComparisonAssertion( private val path: JsonPointer, @@ -49,12 +45,12 @@ internal class NumberComparisonAssertion( private val check: (Number, Number) -> Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonPrimitive || element.isString) { + if (element !is PrimitiveElement || element.isString) { return@use true } val value: Number = element.number ?: return true diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/AdditionalPropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/AdditionalPropertiesAssertionFactory.kt index 815849b9..c2386ea7 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/AdditionalPropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/AdditionalPropertiesAssertionFactory.kt @@ -9,8 +9,9 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject internal object AdditionalPropertiesAssertionFactory : AbstractAssertionFactory("additionalProperties") { val ANNOTATION: AnnotationKey = AnnotationKeyFactory.createAggregatable(property, Boolean::or) @@ -30,12 +31,12 @@ private class AdditionalPropertiesAssertion( private val assertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } val annotationCollector: AnnotationCollector = context.annotationCollector diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/ConditionalRequiredPropertiesAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/ConditionalRequiredPropertiesAssertion.kt index 76a6601d..d97d8843 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/ConditionalRequiredPropertiesAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/ConditionalRequiredPropertiesAssertion.kt @@ -7,9 +7,9 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.TrueSchemaAssertion +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlin.jvm.JvmStatic @@ -19,17 +19,17 @@ internal class ConditionalRequiredPropertiesAssertion( private val dependencies: Set, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } val missingProperties = dependencies.asSequence() - .filter { !element.containsKey(it) } + .filter { !element.contains(it) } .toSet() if (missingProperties.isEmpty()) { return@use true diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/DependenciesAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/DependenciesAssertion.kt index 076d1d90..53d2688d 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/DependenciesAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/DependenciesAssertion.kt @@ -4,20 +4,20 @@ import io.github.optimumcode.json.pointer.JsonPointer import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement internal class DependenciesAssertion( private val location: JsonPointer, private val dependenciesAssertions: Map, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } var valid = true diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MaxPropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MaxPropertiesAssertionFactory.kt index e1d1701d..6236d46c 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MaxPropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MaxPropertiesAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MaxPropertiesAssertionFactory : AbstractAssertionFactory("maxPro context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxPropertiesValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxPropertiesValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxPropertiesValue >= 0) { "$property must be a non-negative integer" } return PropertiesNumberAssertion( context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MinPropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MinPropertiesAssertionFactory.kt index af870d22..386272ed 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MinPropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/MinPropertiesAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,8 @@ internal object MinPropertiesAssertionFactory : AbstractAssertionFactory("minPro context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val minPropertiesValue = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val minPropertiesValue = + requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(minPropertiesValue >= 0) { "$property must be a non-negative integer" } return PropertiesNumberAssertion( context.schemaPath, diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PatternPropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PatternPropertiesAssertionFactory.kt index f6b1e6de..b117fb1b 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PatternPropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PatternPropertiesAssertionFactory.kt @@ -8,6 +8,8 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject @@ -40,12 +42,12 @@ private class PatternAssertion( private val assertionsByRegex: Map, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesAssertionFactory.kt index c5ea2bad..b252b57d 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesAssertionFactory.kt @@ -8,6 +8,8 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject @@ -33,7 +35,7 @@ private class PropertiesAssertion( private val assertionsByProperty: Map, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { @@ -41,7 +43,7 @@ private class PropertiesAssertion( if (assertionsByProperty.isEmpty()) { return@use true } - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesNumberAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesNumberAssertion.kt index 74edf025..e486e827 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesNumberAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertiesNumberAssertion.kt @@ -5,8 +5,8 @@ import io.github.optimumcode.json.schema.OutputCollector import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement internal class PropertiesNumberAssertion( private val path: JsonPointer, @@ -15,12 +15,12 @@ internal class PropertiesNumberAssertion( private val check: (Int, Int) -> Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } if (check(element.size, length)) { diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertyNamesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertyNamesAssertionFactory.kt index dc32b673..2f9c71c1 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertyNamesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/PropertyNamesAssertionFactory.kt @@ -6,9 +6,10 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.internal.wrapper.StringWrapper +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive @Suppress("unused") internal object PropertyNamesAssertionFactory : AbstractAssertionFactory("propertyNames") { @@ -27,12 +28,12 @@ private class PropertyNamesAssertion( private val namesAssertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } var valid = true @@ -43,7 +44,7 @@ private class PropertyNamesAssertion( it.copy(message = "property $property: ${it.message}") }.use { namesAssertion.validate( - JsonPrimitive(property), + StringWrapper(property), ctx, this, ) diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/RequiredAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/RequiredAssertionFactory.kt index bee2d944..2019f76b 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/RequiredAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/RequiredAssertionFactory.kt @@ -8,9 +8,11 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.TrueSchemaAssertion import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement +import io.github.optimumcode.json.schema.model.isEmpty import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlin.math.max @@ -37,12 +39,12 @@ private class RequiredAssertion( private val requiredProperties: Set, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } val missingProperties = diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/UnevaluatedPropertiesAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/UnevaluatedPropertiesAssertionFactory.kt index db1295da..8787a4cf 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/UnevaluatedPropertiesAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/object/UnevaluatedPropertiesAssertionFactory.kt @@ -9,8 +9,9 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ObjectElement import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject internal object UnevaluatedPropertiesAssertionFactory : AbstractAssertionFactory("unevaluatedProperties") { val ANNOTATION: AnnotationKey = AnnotationKeyFactory.createAggregatable(property, Boolean::or) @@ -29,12 +30,12 @@ private class UnevaluatedPropertiesAssertion( private val assertion: JsonSchemaAssertion, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(location).use { - if (element !is JsonObject) { + if (element !is ObjectElement) { return@use true } val annotationCollector: AnnotationCollector = context.annotationCollector diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/LengthAssertion.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/LengthAssertion.kt index fafac473..0faf3a66 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/LengthAssertion.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/LengthAssertion.kt @@ -6,9 +6,9 @@ import io.github.optimumcode.json.schema.ValidationError import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.util.codePointCount -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.contentOrNull +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement +import io.github.optimumcode.json.schema.model.contentOrNull internal class LengthAssertion( private val path: JsonPointer, @@ -17,12 +17,12 @@ internal class LengthAssertion( private val check: (Int, Int) -> Boolean, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonPrimitive || !element.isString) { + if (element !is PrimitiveElement || !element.isString) { return@use true } val content = element.contentOrNull ?: return true diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MaxLengthAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MaxLengthAssertionFactory.kt index 91609a05..6e2348fa 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MaxLengthAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MaxLengthAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,7 @@ internal object MaxLengthAssertionFactory : AbstractAssertionFactory("maxLength" context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val maxLength = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val maxLength = requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(maxLength >= 0) { "$property must be a non-negative integer" } return LengthAssertion(context.schemaPath, maxLength, "must be less or equal to") { a, b -> a <= b } } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MinLengthAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MinLengthAssertionFactory.kt index 9cc0675c..97a251ab 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MinLengthAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/MinLengthAssertionFactory.kt @@ -4,6 +4,7 @@ import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory import io.github.optimumcode.json.schema.internal.util.integerOrNull +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -14,7 +15,7 @@ internal object MinLengthAssertionFactory : AbstractAssertionFactory("minLength" context: LoadingContext, ): JsonSchemaAssertion { require(element is JsonPrimitive && !element.isString) { "$property must be an integer" } - val minLength = requireNotNull(element.integerOrNull) { "$property must be a valid integer" } + val minLength = requireNotNull(JsonPrimitiveWrapper(element).integerOrNull) { "$property must be a valid integer" } require(minLength >= 0) { "$property must be a non-negative integer" } return LengthAssertion(context.schemaPath, minLength, "must be greater or equal to") { a, b -> a >= b } } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/PatternAssertionFactory.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/PatternAssertionFactory.kt index 33c78013..51d99aae 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/PatternAssertionFactory.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/factories/string/PatternAssertionFactory.kt @@ -7,9 +7,11 @@ import io.github.optimumcode.json.schema.internal.AssertionContext import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion import io.github.optimumcode.json.schema.internal.LoadingContext import io.github.optimumcode.json.schema.internal.factories.AbstractAssertionFactory +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement +import io.github.optimumcode.json.schema.model.contentOrNull import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.contentOrNull @Suppress("unused") internal object PatternAssertionFactory : AbstractAssertionFactory("pattern") { @@ -34,12 +36,12 @@ private class PatternAssertion( private val regex: Regex, ) : JsonSchemaAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: AssertionContext, errorCollector: OutputCollector<*>, ): Boolean { return errorCollector.updateKeywordLocation(path).use { - if (element !is JsonPrimitive || !element.isString) { + if (element !is PrimitiveElement || !element.isString) { return@use true } diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/formats/AbstractStringFormatValidator.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/formats/AbstractStringFormatValidator.kt index 0dca0923..7d4e1254 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/formats/AbstractStringFormatValidator.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/formats/AbstractStringFormatValidator.kt @@ -2,12 +2,12 @@ package io.github.optimumcode.json.schema.internal.formats import io.github.optimumcode.json.schema.FormatValidationResult import io.github.optimumcode.json.schema.FormatValidator -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonPrimitive +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement internal abstract class AbstractStringFormatValidator : FormatValidator { - override fun validate(element: JsonElement): FormatValidationResult { - if (element !is JsonPrimitive || !element.isString) { + override fun validate(element: AbstractElement): FormatValidationResult { + if (element !is PrimitiveElement || !element.isString) { return FormatValidator.Valid() } return validate(element.content) diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtil.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtil.kt index 73fb183b..1fee50ee 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtil.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtil.kt @@ -1,31 +1,41 @@ package io.github.optimumcode.json.schema.internal.util -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.booleanOrNull +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.ObjectElement +import io.github.optimumcode.json.schema.model.PrimitiveElement +import io.github.optimumcode.json.schema.model.getValue internal fun areEqual( - first: JsonElement, - second: JsonElement, + first: AbstractElement, + second: AbstractElement, ): Boolean { - if (first::class != second::class) { + if (areDifferentInstances(first, second)) { return false } return when (first) { - is JsonObject -> areEqualObjects(first, second as JsonObject) - is JsonArray -> areEqualArrays(first, second as JsonArray) - is JsonPrimitive -> areEqualPrimitives(first, second as JsonPrimitive) + is ObjectElement -> areEqualObjects(first, second as ObjectElement) + is ArrayElement -> areEqualArrays(first, second as ArrayElement) + is PrimitiveElement -> areEqualPrimitives(first, second as PrimitiveElement) } } +internal fun areDifferentInstances( + left: AbstractElement, + right: AbstractElement, +): Boolean = + when { + left is PrimitiveElement && right is PrimitiveElement -> false + left is ArrayElement && right is ArrayElement -> false + left is ObjectElement && right is ObjectElement -> false + else -> true + } + internal fun areEqualPrimitives( - first: JsonPrimitive, - second: JsonPrimitive, + first: PrimitiveElement, + second: PrimitiveElement, ): Boolean { - if (first is JsonNull && second is JsonNull) { + if (first.isNull && second.isNull) { return true } if (first.isString != second.isString) { @@ -35,22 +45,24 @@ internal fun areEqualPrimitives( first.content == second.content } else { when { - first.booleanOrNull != null || second.booleanOrNull != null -> first.content == second.content + first.isNull || second.isNull -> false + // probably content should be compared ignoring the case - YAML allows different values for boolean + first.isBoolean || second.isBoolean -> first.content == second.content else -> compareAsNumbers(first, second) } } } private fun compareAsNumbers( - first: JsonPrimitive, - second: JsonPrimitive, + first: PrimitiveElement, + second: PrimitiveElement, ): Boolean { return numberParts(first) == numberParts(second) } internal fun areEqualArrays( - first: JsonArray, - second: JsonArray, + first: ArrayElement, + second: ArrayElement, ): Boolean { if (first.size != second.size) { return false @@ -64,8 +76,8 @@ internal fun areEqualArrays( } internal fun areEqualObjects( - first: JsonObject, - second: JsonObject, + first: ObjectElement, + second: ObjectElement, ): Boolean { if (first.size != second.size) { return false diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/NumberParts.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/NumberParts.kt index cfdbf5dd..27eff3c7 100644 --- a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/NumberParts.kt +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/util/NumberParts.kt @@ -1,9 +1,6 @@ package io.github.optimumcode.json.schema.internal.util -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.booleanOrNull -import kotlinx.serialization.json.double +import io.github.optimumcode.json.schema.model.PrimitiveElement import kotlin.math.absoluteValue internal data class NumberParts( @@ -12,8 +9,8 @@ internal data class NumberParts( val precision: Int, ) -internal fun parseNumberParts(element: JsonPrimitive): NumberParts? { - return if (element.isString || element is JsonNull || element.booleanOrNull != null) { +internal fun parseNumberParts(element: PrimitiveElement): NumberParts? { + return if (element.isString || element.isNull || element.isBoolean) { null } else { numberParts(element) @@ -27,9 +24,12 @@ private const val TEN: Double = 10.0 /** * This function should be used only if you are certain that the [element] is a number */ -internal fun numberParts(element: JsonPrimitive): NumberParts { +@Suppress("detekt:ForbiddenComment") +// FIXME: if we add support for formats other then JSON we should handle +Inf, -Inf and NaN values correctly +internal fun numberParts(element: PrimitiveElement): NumberParts { if (element.content.run { contains(E_SMALL_CHAR) || contains(E_BIG_CHAR) }) { - return element.double.run { + val number = requireNotNull(element.number) { "element '${element.content}' is not a number" } + return number.toDouble().run { var precision = 0 var fractionalPart = rem(1.0).absoluteValue while (fractionalPart % 1.0 > 0) { @@ -66,7 +66,7 @@ internal fun numberParts(element: JsonPrimitive): NumberParts { } } -internal val JsonPrimitive.integerOrNull: Int? +internal val PrimitiveElement.integerOrNull: Int? get() = parseNumberParts(this)?.takeIf { it.fractional == 0L && it.integer <= Int.MAX_VALUE diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrappers.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrappers.kt new file mode 100644 index 00000000..0e89022b --- /dev/null +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrappers.kt @@ -0,0 +1,115 @@ +package io.github.optimumcode.json.schema.internal.wrapper + +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.ArrayElement +import io.github.optimumcode.json.schema.model.ObjectElement +import io.github.optimumcode.json.schema.model.PrimitiveElement +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.booleanOrNull +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.longOrNull +import kotlin.jvm.JvmInline + +internal interface JsonWrapper { + fun unwrap(): JsonElement +} + +@JvmInline +internal value class JsonObjectWrapper( + private val obj: JsonObject, +) : ObjectElement, JsonWrapper { + override val keys: Set + get() = obj.keys + + override fun get(key: String): AbstractElement? = obj[key]?.wrap() + + override fun contains(key: String): Boolean = obj.containsKey(key) + + override val size: Int + get() = obj.size + + override fun iterator(): Iterator> = + obj + .asSequence() + .map { + it.key to it.value.wrap() + }.iterator() + + override fun unwrap(): JsonElement = obj + + override fun toString(): String = obj.toString() +} + +@JvmInline +internal value class JsonArrayWrapper( + private val array: JsonArray, +) : ArrayElement, JsonWrapper { + override fun iterator(): Iterator = array.asSequence().map { it.wrap() }.iterator() + + override fun get(index: Int): AbstractElement = array[index].wrap() + + override val size: Int + get() = array.size + + override fun toString(): String = array.toString() + + override fun unwrap(): JsonElement = array +} + +@JvmInline +internal value class JsonPrimitiveWrapper( + private val primitive: JsonPrimitive, +) : PrimitiveElement, JsonWrapper { + override val isNull: Boolean + get() = primitive is JsonNull + override val isString: Boolean + get() = primitive.isString + override val number: Number? + get() = primitive.run { longOrNull ?: doubleOrNull } + override val isBoolean: Boolean + get() = primitive.run { !isString && booleanOrNull != null } + override val isNumber: Boolean + get() = + primitive.run { + !isString && (longOrNull ?: doubleOrNull) != null + } + override val content: String + get() = primitive.content + + override fun toString(): String = primitive.toString() + + override fun unwrap(): JsonElement = primitive +} + +internal fun JsonElement.wrap(): AbstractElement = + when (this) { + is JsonObject -> JsonObjectWrapper(this) + is JsonArray -> JsonArrayWrapper(this) + is JsonPrimitive -> JsonPrimitiveWrapper(this) + } + +@JvmInline +internal value class StringWrapper( + private val value: String, +) : PrimitiveElement, JsonWrapper { + override val isNull: Boolean + get() = false + override val isString: Boolean + get() = true + override val number: Number? + get() = value.toLongOrNull() ?: value.toDoubleOrNull() + override val isBoolean: Boolean + get() = false + override val isNumber: Boolean + get() = false + override val content: String + get() = value + + override fun toString(): String = value + + override fun unwrap(): JsonElement = JsonPrimitive(value) +} \ No newline at end of file diff --git a/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/model/Model.kt b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/model/Model.kt new file mode 100644 index 00000000..f789e231 --- /dev/null +++ b/json-schema-validator/src/commonMain/kotlin/io/github/optimumcode/json/schema/model/Model.kt @@ -0,0 +1,104 @@ +package io.github.optimumcode.json.schema.model + +import io.github.optimumcode.json.schema.ExperimentalApi + +/** + * [AbstractElement] represents one of 3 elements that can be consumed and validated: + * * [ObjectElement] - corresponds to a JSON/YAML object or [Map] + * * [ArrayElement] - corresponds to a collection of [AbstractElement]s + * * [PrimitiveElement] - corresponds to a scalar value (string, number, boolean) + */ +@ExperimentalApi +public sealed interface AbstractElement { + /** + * Implement [toString] method to provide a useful view that can be used in error message + */ + override fun toString(): String +} + +@ExperimentalApi +public interface PrimitiveElement : AbstractElement { + /** + * Returns `true` if the element is `null`. + * Otherwise, returns `false` + */ + public val isNull: Boolean + + /** + * Returns `true` if the element can be interpreted as string. + * Otherwise, returns `false` + */ + public val isString: Boolean + + /** + * Returns `true` if the element can be interpreted as a boolean. + * Otherwise, returns `false` + */ + public val isBoolean: Boolean + + /** + * Returns `true` if the element can be interpreted as a number. + * Otherwise, returns `false` + */ + public val isNumber: Boolean + + /** + * Tries to parse the element as a [Number]. + * Must return either [Long] or [Double]. + * If the element cannot be interpreted as a number, returns `null`. + */ + public val number: Number? + + /** + * Returns the content of the element as plain string + */ + public val content: String +} + +internal val PrimitiveElement.contentOrNull: String? + get() = if (isNull) null else content + +@ExperimentalApi +public interface ObjectElement : AbstractElement, Sequence> { + /** + * Returns the set of keys defined in the [ObjectElement] + */ + public val keys: Set + + /** + * Returns an [AbstractElement] associated with the [key]. + * If the [key] does not exist returns `null` + */ + public operator fun get(key: String): AbstractElement? + + /** + * Returns `true` if the [key] exists in the [ObjectElement]. + * Otherwise, returns `false` + */ + public operator fun contains(key: String): Boolean + + /** + * Returns number of keys in the [ObjectElement] + */ + public val size: Int +} + +internal fun ObjectElement.isEmpty(): Boolean = size == 0 + +internal fun ObjectElement.getValue(key: String): AbstractElement = get(key) ?: error("property '$key' not found") + +@ExperimentalApi +public interface ArrayElement : AbstractElement, Sequence { + /** + * Returns the [AbstractElement] associated with the [index]. + */ + public operator fun get(index: Int): AbstractElement + + /** + * Returns the number of [AbstractElement]s in the collection + */ + public val size: Int +} + +internal val ArrayElement.lastIndex: Int + get() = size - 1 \ No newline at end of file diff --git a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/base/JsonSchemaLoaderTest.kt b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/base/JsonSchemaLoaderTest.kt index 75531e62..1929414a 100644 --- a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/base/JsonSchemaLoaderTest.kt +++ b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/base/JsonSchemaLoaderTest.kt @@ -10,6 +10,8 @@ import io.github.optimumcode.json.schema.FormatValidator import io.github.optimumcode.json.schema.JsonSchemaLoader import io.github.optimumcode.json.schema.SchemaOption import io.github.optimumcode.json.schema.ValidationError +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldNotThrowAnyUnit @@ -19,7 +21,6 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.buildJsonArray import kotlinx.serialization.json.buildJsonObject @@ -225,8 +226,8 @@ class JsonSchemaLoaderTest : FunSpec() { } class MagicFormatValidator : FormatValidator { - override fun validate(element: JsonElement): FormatValidationResult { - return if (element is JsonPrimitive && element.isString) { + override fun validate(element: AbstractElement): FormatValidationResult { + return if (element is PrimitiveElement && element.isString) { if (element.content == "42") { FormatValidator.Valid() } else { diff --git a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionAnnotationTest.kt b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionAnnotationTest.kt index 15806a2b..6f139e67 100644 --- a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionAnnotationTest.kt +++ b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionAnnotationTest.kt @@ -5,6 +5,7 @@ import io.github.optimumcode.json.schema.AnnotationKey import io.github.optimumcode.json.schema.ErrorCollector import io.github.optimumcode.json.schema.JsonSchemaLoader import io.github.optimumcode.json.schema.ValidationError +import io.github.optimumcode.json.schema.model.AbstractElement import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly @@ -93,7 +94,7 @@ private object FooFactory : ExternalAssertionFactory { val annotationValue = element.content return object : ExternalAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, ): Boolean { @@ -117,7 +118,7 @@ private object BarFactory : ExternalAssertionFactory { val path = context.schemaPath return object : ExternalAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, ): Boolean { @@ -153,7 +154,7 @@ private object WrongBarFactory : ExternalAssertionFactory { val path = context.schemaPath return object : ExternalAssertion { override fun validate( - element: JsonElement, + element: AbstractElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, ): Boolean { diff --git a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionTest.kt b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionTest.kt index 93499e9f..15808ff3 100644 --- a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionTest.kt +++ b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/extension/JsonSchemaExtensionTest.kt @@ -5,6 +5,8 @@ import io.github.optimumcode.json.schema.ErrorCollector import io.github.optimumcode.json.schema.JsonSchema import io.github.optimumcode.json.schema.JsonSchemaLoader import io.github.optimumcode.json.schema.ValidationError +import io.github.optimumcode.json.schema.model.AbstractElement +import io.github.optimumcode.json.schema.model.PrimitiveElement import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrow @@ -146,11 +148,11 @@ private object SimpleDateFormatAssertionFactory : ExternalAssertionFactory { private val dateRegex = Regex("(\\d{4})-(\\d{2})-(\\d{2})") override fun validate( - element: JsonElement, + element: AbstractElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, ): Boolean { - if (element !is JsonPrimitive || !element.isString) { + if (element !is PrimitiveElement || !element.isString) { return true } return dateRegex.matches(element.content).also { @@ -186,11 +188,11 @@ private object SimpleTimeFormatAssertionFactory : ExternalAssertionFactory { private val timeRegex = Regex("\\d{2}:\\d{2}:\\d{2}(.\\d{1,9})?") override fun validate( - element: JsonElement, + element: AbstractElement, context: ExternalAssertionContext, errorCollector: ErrorCollector, ): Boolean { - if (element !is JsonPrimitive || !element.isString) { + if (element !is PrimitiveElement || !element.isString) { return true } return timeRegex.matches(element.content).also { diff --git a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtilTest.kt b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtilTest.kt index b27f35ad..a8b6ba6e 100644 --- a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtilTest.kt +++ b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/util/ElementEqualityUtilTest.kt @@ -1,5 +1,6 @@ package io.github.optimumcode.json.schema.internal.util +import io.github.optimumcode.json.schema.internal.wrapper.JsonPrimitiveWrapper import io.kotest.assertions.asClue import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FunSpec @@ -14,7 +15,7 @@ class ElementEqualityUtilTest : FunSpec() { init { test("extracts number parts from max long engineering format") { val (integer, fraction, precision) = - parseNumberParts(JsonUnquotedLiteral("1e308")) + parseNumberParts(JsonPrimitiveWrapper(JsonUnquotedLiteral("1e308"))) .shouldNotBeNull() assertSoftly { integer shouldBe Long.MAX_VALUE @@ -25,8 +26,8 @@ class ElementEqualityUtilTest : FunSpec() { test("correctly compares fractional part") { areEqualPrimitives( - JsonUnquotedLiteral("0.0075"), - JsonUnquotedLiteral("0.00075"), + JsonPrimitiveWrapper(JsonUnquotedLiteral("0.0075")), + JsonPrimitiveWrapper(JsonUnquotedLiteral("0.00075")), ) shouldBe false } @@ -36,7 +37,7 @@ class ElementEqualityUtilTest : FunSpec() { "751e-5", ).forEach { test("correctly extract all parts from float number in format $it") { - val parts = parseNumberParts(JsonUnquotedLiteral(it)).shouldNotBeNull() + val parts = parseNumberParts(JsonPrimitiveWrapper(JsonUnquotedLiteral(it))).shouldNotBeNull() assertSoftly { parts.asClue { p -> p.integer shouldBe 0 @@ -56,13 +57,13 @@ class ElementEqualityUtilTest : FunSpec() { test("numbers $first and $second are equal") { assertSoftly { areEqualPrimitives( - JsonUnquotedLiteral(first), - JsonUnquotedLiteral(second), + JsonPrimitiveWrapper(JsonUnquotedLiteral(first)), + JsonPrimitiveWrapper(JsonUnquotedLiteral(second)), ) shouldBe true areEqualPrimitives( - JsonUnquotedLiteral(second), - JsonUnquotedLiteral(first), + JsonPrimitiveWrapper(JsonUnquotedLiteral(second)), + JsonPrimitiveWrapper(JsonUnquotedLiteral(first)), ) shouldBe true } } diff --git a/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrapperTest.kt b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrapperTest.kt new file mode 100644 index 00000000..c05c6d1a --- /dev/null +++ b/json-schema-validator/src/commonTest/kotlin/io/github/optimumcode/json/schema/internal/wrapper/JsonWrapperTest.kt @@ -0,0 +1,125 @@ +package io.github.optimumcode.json.schema.internal.wrapper + +import io.github.optimumcode.json.schema.model.contentOrNull +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.booleans.shouldBeFalse +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.should +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.instanceOf +import io.kotest.matchers.types.shouldBeInstanceOf +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject + +class JsonWrapperTest : FunSpec() { + init { + mapOf( + buildJsonObject { } to JsonObjectWrapper::class, + buildJsonArray { } to JsonArrayWrapper::class, + JsonPrimitive("hello") to JsonPrimitiveWrapper::class, + JsonNull to JsonPrimitiveWrapper::class, + ).forEach { (node, wrapperClass) -> + test("node ${node::class.simpleName} wraps into ${wrapperClass.simpleName}") { + node.wrap() shouldBe instanceOf(wrapperClass) + } + } + + test("object wrapper") { + buildJsonObject { + put("a", JsonPrimitive("hello")) + put("b", buildJsonArray { }) + }.wrap().shouldBeInstanceOf { + assertSoftly { + it.size shouldBe 2 + it.keys shouldContainExactly setOf("a", "b") + it["a"].shouldBeInstanceOf() + it["b"].shouldBeInstanceOf() + it["c"].shouldBeNull() + ("a" in it).shouldBeTrue() + ("c" in it).shouldBeFalse() + } + } + } + + test("array wrapper") { + buildJsonArray { + add(JsonPrimitive("hello")) + add(buildJsonObject { }) + }.wrap().shouldBeInstanceOf { + assertSoftly { + it.size shouldBe 2 + it[0].shouldBeInstanceOf() + it[1].shouldBeInstanceOf() + } + } + } + + test("primitive wrapper for null") { + JsonNull.wrap().shouldBeInstanceOf { + assertSoftly { + it.isString.shouldBeFalse() + it.isNumber.shouldBeFalse() + it.isBoolean.shouldBeFalse() + it.isNull.shouldBeTrue() + it.content shouldBe "null" + it.contentOrNull.shouldBeNull() + } + } + } + + test("primitive wrapper for boolean") { + JsonPrimitive(true).wrap().shouldBeInstanceOf { + assertSoftly { + it.isString.shouldBeFalse() + it.isNumber.shouldBeFalse() + it.isBoolean.shouldBeTrue() + it.isNull.shouldBeFalse() + it.content shouldBe "true" + it.contentOrNull shouldBe "true" + } + } + } + + test("primitive wrapper for number") { + JsonPrimitive(42).wrap().shouldBeInstanceOf { + assertSoftly { + it.isString.shouldBeFalse() + it.isNumber.shouldBeTrue() + it.isBoolean.shouldBeFalse() + it.isNull.shouldBeFalse() + it.content shouldBe "42" + it.contentOrNull shouldBe "42" + } + } + } + + test("primitive wrapper for string") { + JsonPrimitive("42").wrap().shouldBeInstanceOf { + assertSoftly { + it.isString.shouldBeTrue() + it.isNumber.shouldBeFalse() + it.isBoolean.shouldBeFalse() + it.isNull.shouldBeFalse() + it.content shouldBe "42" + it.contentOrNull shouldBe "42" + } + } + } + + test("string wrapper for property") { + StringWrapper("prop").should { + it.isString.shouldBeTrue() + it.isNumber.shouldBeFalse() + it.isBoolean.shouldBeFalse() + it.isNull.shouldBeFalse() + it.content shouldBe "prop" + it.contentOrNull shouldBe "prop" + } + } + } +} \ No newline at end of file