-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improves validation performance of types defined in the spec
- Loading branch information
Showing
16 changed files
with
149 additions
and
396 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 0 additions & 155 deletions
155
ion-schema/src/main/kotlin/com/amazon/ionschema/internal/SchemaCore.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
ion-schema/src/main/kotlin/com/amazon/ionschema/internal/SpecType.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.amazon.ionschema.internal | ||
|
||
import com.amazon.ion.IonType | ||
import com.amazon.ion.IonValue | ||
import com.amazon.ion.system.IonSystemBuilder | ||
import com.amazon.ionschema.Violation | ||
import com.amazon.ionschema.Violations | ||
import com.amazon.ionschema.internal.util.schemaTypeName | ||
|
||
/** | ||
* Represents a type that is defined in the Ion Schema Specification. | ||
* | ||
* All implementations should be as lightweight as possible (e.g. no delegation to other [TypeInternal] instances) | ||
* because these are the most commonly used types that are used as part of building almost every user-defined type. | ||
*/ | ||
internal sealed class SpecType : TypeInternal | ||
|
||
private val ionSystem = IonSystemBuilder.standard().build() | ||
|
||
/** | ||
* The `$any` type gets special treatment because it is a no-op. | ||
*/ | ||
private object UniversalType : SpecType() { | ||
override val schemaId: String? | ||
get() = null | ||
override fun getBaseType(): SpecType = this | ||
override fun isValidForBaseType(value: IonValue): Boolean = true | ||
override val name: String = "\$any" | ||
override val isl: IonValue = ionSystem.newSymbol("\$any") | ||
override fun validate(value: IonValue, issues: Violations) = Unit | ||
} | ||
|
||
/** | ||
* The `any` type gets special treatment because it is the default type for ISL 1.0 | ||
*/ | ||
private object AnyType : SpecType() { | ||
override val schemaId: String? | ||
get() = null | ||
override fun getBaseType(): SpecType = this | ||
override fun isValidForBaseType(value: IonValue): Boolean = true | ||
override val name: String = "any" | ||
override val isl: IonValue = ionSystem.newSymbol("any") | ||
override fun validate(value: IonValue, issues: Violations) { | ||
if (value.isNullValue) { | ||
val typeName = value.type.schemaTypeName() | ||
issues.add(Violation(isl, "type_mismatch", message = "expected type any, found null $typeName")) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* The `nothing` type gets special treatment because all data is invalid. | ||
*/ | ||
private object EmptyType : SpecType() { | ||
override val schemaId: String? | ||
get() = null | ||
override fun getBaseType(): SpecType = this | ||
override fun isValidForBaseType(value: IonValue): Boolean = true | ||
override val name: String = "nothing" | ||
override val isl: IonValue = ionSystem.newSymbol("nothing") | ||
override fun validate(value: IonValue, issues: Violations) { | ||
val typeName = value.type.schemaTypeName() | ||
issues.add(Violation(isl, "type_mismatch", message = "expected type nothing, found $typeName")) | ||
} | ||
} | ||
|
||
/** | ||
* An implementation of [SpecType] that is valid for a set of [IonType]s and optionally the corresponding typed nulls. | ||
*/ | ||
private class GenericSpecTypeImpl(override val name: String, private val ionTypes: Set<IonType>, private val nullsAllowed: Boolean) : SpecType() { | ||
override val schemaId: String? | ||
get() = null | ||
|
||
override fun getBaseType(): SpecType = this | ||
|
||
override fun isValidForBaseType(value: IonValue): Boolean = value.type in ionTypes | ||
|
||
override val isl: IonValue = ionSystem.newSymbol(name) | ||
|
||
override fun validate(value: IonValue, issues: Violations) { | ||
val isCorrectType = value.type in ionTypes | ||
val isNullCheckOkay = nullsAllowed or !value.isNullValue | ||
if (!isCorrectType || !isNullCheckOkay) { | ||
val maybeNull = if (value.isNullValue) "null " else "" | ||
val typeName = value.type.schemaTypeName() | ||
issues.add(Violation(isl, "type_mismatch", message = "expected type $name, found $maybeNull$typeName")) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Object for holding all instances of [SpecType]. | ||
*/ | ||
internal object SpecTypes { | ||
private val allTypes: MutableMap<String, SpecType> = mutableMapOf() | ||
|
||
init { | ||
// Micro DSL so that we can declaratively create the Spec types. | ||
infix fun String.isNonNull(types: Set<IonType>) { | ||
allTypes[this] = GenericSpecTypeImpl(this, types, nullsAllowed = false) | ||
} | ||
infix fun String.isMaybeNull(types: Set<IonType>) { | ||
allTypes[this] = GenericSpecTypeImpl(this, types, nullsAllowed = true) | ||
} | ||
|
||
// Creates all the SpecTypes that correspond directly to _one_ IonType. | ||
IonType.values().forEach { | ||
val typeName = it.schemaTypeName() | ||
if (it != IonType.NULL) typeName isNonNull setOf(it) | ||
if (it != IonType.DATAGRAM) "$$typeName" isMaybeNull setOf(it) | ||
} | ||
|
||
"text" isNonNull setOf(IonType.STRING, IonType.SYMBOL) | ||
"\$text" isMaybeNull setOf(IonType.STRING, IonType.SYMBOL) | ||
"number" isNonNull setOf(IonType.INT, IonType.FLOAT, IonType.DECIMAL) | ||
"\$number" isMaybeNull setOf(IonType.INT, IonType.FLOAT, IonType.DECIMAL) | ||
"lob" isNonNull setOf(IonType.BLOB, IonType.CLOB) | ||
"\$lob" isMaybeNull setOf(IonType.BLOB, IonType.CLOB) | ||
allTypes["any"] = AnyType | ||
allTypes["\$any"] = UniversalType | ||
allTypes["nothing"] = EmptyType | ||
} | ||
|
||
/** Gets one of the spec types by name */ | ||
operator fun get(name: String): SpecType? = allTypes[name] | ||
|
||
/** Gets all the spec types as a map */ | ||
fun asMap(): Map<String, SpecType> = allTypes | ||
|
||
/** Gets all the spec types as a sequence */ | ||
fun asSequence() = allTypes.values.asSequence() | ||
} |
Oops, something went wrong.