├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── build.gradle ├── cli ├── README.md ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── amazon │ │ └── ionschema │ │ └── cli │ │ ├── Main.kt │ │ ├── commands │ │ ├── RepairCommand.kt │ │ ├── ValidateCommand.kt │ │ ├── options.kt │ │ └── repair │ │ │ ├── FixTransitiveImportsCommand.kt │ │ │ └── TransitiveImportRewriter.kt │ │ └── util │ │ ├── PatchSet.kt │ │ ├── files.kt │ │ ├── ion.kt │ │ └── ionSchema.kt │ └── test │ ├── kotlin │ └── com │ │ └── amazon │ │ └── ionschema │ │ └── cli │ │ ├── VersionTest.kt │ │ ├── commands │ │ └── repair │ │ │ └── FixTransitiveImportsCommandTest.kt │ │ └── util │ │ └── filesTest.kt │ └── resources │ └── FixTransitiveImportsCommand │ ├── input │ ├── aliased_import.isl │ ├── export.isl │ ├── floats.isl │ ├── inline_imports.isl │ ├── ints.isl │ ├── type_import.isl │ └── wildcard_import.isl │ ├── output-KeepWildcards │ ├── aliased_import.isl │ ├── export.isl │ ├── floats.isl │ ├── inline_imports.isl │ ├── ints.isl │ ├── type_import.isl │ └── wildcard_import.isl │ ├── output-NoWildcards │ ├── aliased_import.isl │ ├── export.isl │ ├── floats.isl │ ├── inline_imports.isl │ ├── ints.isl │ ├── type_import.isl │ └── wildcard_import.isl │ └── output-PreferWildcards │ ├── aliased_import.isl │ ├── export.isl │ ├── floats.isl │ ├── inline_imports.isl │ ├── ints.isl │ ├── type_import.isl │ └── wildcard_import.isl ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ion-schema-cli ├── ion-schema ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── amazon │ │ └── ionschema │ │ ├── Authority.kt │ │ ├── AuthorityFilesystem.kt │ │ ├── Import.kt │ │ ├── InMemoryMapAuthority.kt │ │ ├── InvalidSchemaException.kt │ │ ├── IonSchemaException.kt │ │ ├── IonSchemaSchemas.kt │ │ ├── IonSchemaSystem.kt │ │ ├── IonSchemaSystemBuilder.kt │ │ ├── IonSchemaVersion.kt │ │ ├── ResourceAuthority.kt │ │ ├── Schema.kt │ │ ├── SchemaCache.kt │ │ ├── SchemaCacheDefault.kt │ │ ├── Type.kt │ │ ├── Violations.kt │ │ ├── internal │ │ ├── CommonViolations.kt │ │ ├── Constraint.kt │ │ ├── ConstraintFactory.kt │ │ ├── ConstraintFactoryDefault.kt │ │ ├── DeferredReference.kt │ │ ├── DeferredReferenceManager.kt │ │ ├── ImportImpl.kt │ │ ├── IonSchemaSystemImpl.kt │ │ ├── SchemaContent.kt │ │ ├── SchemaContentCache.kt │ │ ├── SchemaImpl_1_0.kt │ │ ├── SchemaImpl_2_0.kt │ │ ├── SchemaInternal.kt │ │ ├── TypeAliased.kt │ │ ├── TypeBuiltin.kt │ │ ├── TypeImpl.kt │ │ ├── TypeInline.kt │ │ ├── TypeInternal.kt │ │ ├── TypeNamed.kt │ │ ├── TypeNullable.kt │ │ ├── TypeOrNullDecorator.kt │ │ ├── TypeReference.kt │ │ ├── WarningMessagesUtils.kt │ │ ├── constraint │ │ │ ├── Annotations_1_0.kt │ │ │ ├── Annotations_2_0.kt │ │ │ ├── ByteLength.kt │ │ │ ├── CodepointLength.kt │ │ │ ├── ConstraintBase.kt │ │ │ ├── ConstraintBaseIntRange.kt │ │ │ ├── ContainerLength.kt │ │ │ ├── Contains.kt │ │ │ ├── Content.kt │ │ │ ├── Element.kt │ │ │ ├── Exponent.kt │ │ │ ├── FieldNames.kt │ │ │ ├── Fields.kt │ │ │ ├── Ieee754Float.kt │ │ │ ├── LogicConstraints.kt │ │ │ ├── NFA.kt │ │ │ ├── Occurs.kt │ │ │ ├── OrderedElements.kt │ │ │ ├── OrderedElementsNfaStatesBuilder.kt │ │ │ ├── Precision.kt │ │ │ ├── Regex.kt │ │ │ ├── Scale.kt │ │ │ ├── StateMachine.kt │ │ │ ├── TimestampOffset.kt │ │ │ ├── TimestampPrecision.kt │ │ │ ├── Type.kt │ │ │ ├── Utf8ByteLength.kt │ │ │ └── ValidValues.kt │ │ └── util │ │ │ ├── IntRange.kt │ │ │ ├── IonSchema_2_0.kt │ │ │ ├── IonValueExtensions.kt │ │ │ ├── Preconditions.kt │ │ │ ├── Range.kt │ │ │ ├── RangeBigDecimal.kt │ │ │ ├── RangeBoundaryType.kt │ │ │ ├── RangeInt.kt │ │ │ ├── RangeIntNonNegative.kt │ │ │ ├── RangeIonNumber.kt │ │ │ ├── RangeIonTimestamp.kt │ │ │ ├── RangeIonTimestampPrecision.kt │ │ │ ├── StringExtensions.kt │ │ │ └── regex.kt │ │ ├── model │ │ ├── ConsistentDecimal.kt │ │ ├── ConsistentTimestamp.kt │ │ ├── Constraint.kt │ │ ├── ContinuousRange.kt │ │ ├── DiscreteIntRange.kt │ │ ├── ExperimentalIonSchemaModel.kt │ │ ├── HeaderImport.kt │ │ ├── Ieee754InterchangeFormat.kt │ │ ├── NamedTypeDefinition.kt │ │ ├── SchemaDocument.kt │ │ ├── SchemaFooter.kt │ │ ├── SchemaHeader.kt │ │ ├── TimestampOffsetValue.kt │ │ ├── TimestampPrecisionRange.kt │ │ ├── TimestampPrecisionValue.kt │ │ ├── TypeArgument.kt │ │ ├── TypeDefinition.kt │ │ ├── UserReservedFields.kt │ │ ├── ValidValue.kt │ │ ├── VariablyOccurringTypeArgument.kt │ │ ├── aliases.kt │ │ └── util.kt │ │ ├── module.md │ │ ├── reader │ │ ├── IonSchemaReader.kt │ │ ├── IonTreePath.kt │ │ └── internal │ │ │ ├── FooterReader.kt │ │ │ ├── HeaderReader.kt │ │ │ ├── IonSchemaReaderV2_0.kt │ │ │ ├── ReadError.kt │ │ │ ├── ReaderContext.kt │ │ │ ├── TypeReader.kt │ │ │ ├── TypeReaderV2_0.kt │ │ │ ├── VersionedIonSchemaReader.kt │ │ │ ├── constraints │ │ │ ├── AnnotationsV2Reader.kt │ │ │ ├── ConstraintReader.kt │ │ │ ├── ContainsReader.kt │ │ │ ├── ElementV2Reader.kt │ │ │ ├── ExponentReader.kt │ │ │ ├── FieldNamesReader.kt │ │ │ ├── FieldsV2Reader.kt │ │ │ ├── Ieee754FloatReader.kt │ │ │ ├── LengthConstraintsReader.kt │ │ │ ├── LogicConstraintsReader.kt │ │ │ ├── OrderedElementsReader.kt │ │ │ ├── PrecisionReader.kt │ │ │ ├── RegexReader.kt │ │ │ ├── TimestampOffsetReader.kt │ │ │ ├── TimestampPrecisionReader.kt │ │ │ └── ValidValuesReader.kt │ │ │ ├── ranges.kt │ │ │ └── util.kt │ │ ├── util │ │ ├── Bag.kt │ │ ├── CloseableIterator.kt │ │ ├── IonSchemaResult.kt │ │ ├── RegexImplementation.kt │ │ ├── SchemaSymbolsUtil.kt │ │ └── bags.kt │ │ └── writer │ │ ├── IonSchemaWriter.kt │ │ └── internal │ │ ├── FooterWriter.kt │ │ ├── HeaderWriter.kt │ │ ├── IonSchemaWriterV2_0.kt │ │ ├── TypeWriter.kt │ │ ├── TypeWriterV2_0.kt │ │ ├── VersionedIonSchemaWriter.kt │ │ ├── constraints │ │ ├── AnnotationsV2Writer.kt │ │ ├── ConstraintWriter.kt │ │ ├── ContainsWriter.kt │ │ ├── ElementWriter.kt │ │ ├── ExponentWriter.kt │ │ ├── FieldNamesWriter.kt │ │ ├── FieldsWriter.kt │ │ ├── Ieee754FloatWriter.kt │ │ ├── LengthConstraintsWriter.kt │ │ ├── LogicConstraintsWriter.kt │ │ ├── OrderedElementsWriter.kt │ │ ├── PrecisionWriter.kt │ │ ├── RegexWriter.kt │ │ ├── TimestampOffsetWriter.kt │ │ ├── TimestampPrecisionWriter.kt │ │ └── ValidValuesWriter.kt │ │ ├── ranges.kt │ │ └── util.kt │ └── test │ ├── java │ └── com │ │ └── amazon │ │ └── ionschema │ │ └── util │ │ └── SchemaSymbolsUtilJavaApiTest.java │ └── kotlin │ └── com │ └── amazon │ └── ionschema │ ├── AlternateRegexImplementationTest.kt │ ├── AuthorityFilesystemTest.kt │ ├── FailFastOnInvalidAnnotationsTest.kt │ ├── InMemoryMapAuthorityTest.kt │ ├── IonSchemaSchemasTestsRunner.kt │ ├── IonSchemaSystemTest.kt │ ├── IonSchemaTests.kt │ ├── IonSchemaTestsRunner.kt │ ├── IonSchemaVersionTest.kt │ ├── ResourceAuthorityTest.kt │ ├── SchemaCacheTest.kt │ ├── SchemaImportTest.kt │ ├── SchemaTest.kt │ ├── TestFactory.kt │ ├── TestUtil.kt │ ├── TransitiveSchemaImportTest.kt │ ├── TypeTest.kt │ ├── internal │ ├── DeferredReferenceManagerTest.kt │ ├── DeferredReferenceTest.kt │ ├── SchemaContentCacheTest.kt │ ├── SchemaContentTest.kt │ ├── SchemaImpl_2_0_Test.kt │ ├── TypeNullableTest.kt │ ├── constraint │ │ ├── NFATest.kt │ │ ├── OneOfTest.kt │ │ └── OrderedElementsTest.kt │ └── util │ │ ├── AbstractRangeTest.kt │ │ ├── IonTimestampPrecisionTest.kt │ │ ├── PreconditionsTest.kt │ │ ├── RangeBoundaryTypeTest.kt │ │ ├── RangeIntNonNegativeTest.kt │ │ └── RangeIonNumberTest.kt │ ├── model │ ├── ConsistentDecimalTest.kt │ ├── ConsistentTimestampTest.kt │ ├── ContinuousRangeTest.kt │ ├── DiscreteRangeTest.kt │ ├── Ieee754InterchangeFormatTest.kt │ ├── SchemaDocumentTest.kt │ ├── TimestampOffsetValueTest.kt │ └── TimestampPrecisionValueTest.kt │ ├── reader │ ├── IonSchemaReaderV2_0Tests.kt │ ├── ReaderTests.kt │ └── internal │ │ ├── FooterReaderTest.kt │ │ ├── HeaderReaderTest.kt │ │ ├── ReaderContextTest.kt │ │ └── constraints │ │ ├── AnnotationsV2ReaderTest.kt │ │ ├── ContainsReaderTest.kt │ │ ├── ElementV2ReaderTest.kt │ │ ├── ExponentReaderTest.kt │ │ ├── FieldNamesReaderTest.kt │ │ ├── FieldsV2ReaderTest.kt │ │ ├── Ieee745FloatReaderTest.kt │ │ ├── LengthConstraintsReaderTest.kt │ │ ├── LogicConstraintsReaderTest.kt │ │ ├── OrderedElementsReaderTest.kt │ │ ├── PrecisionReaderTest.kt │ │ ├── RegexReaderTest.kt │ │ ├── TimestampOffsetReaderTest.kt │ │ ├── TimestampPrecisionReaderTest.kt │ │ └── ValidValuesReaderTest.kt │ ├── util │ ├── BagTest.kt │ ├── IonTreePathTest.kt │ └── SchemaSymbolsUtilTest.kt │ └── writer │ ├── IonSchemaWriterTest.kt │ ├── WriterTests.kt │ └── internal │ ├── FooterWriterTest.kt │ ├── HeaderWriterTest.kt │ └── constraints │ ├── AnnotationsV2WriterTest.kt │ ├── ConstraintTestBase.kt │ ├── ContainsWriterTest.kt │ ├── ElementWriterTest.kt │ ├── ExponentWriterTest.kt │ ├── FieldNamesWriterTest.kt │ ├── FieldsWriterTest.kt │ ├── Ieee754FloatWriterTest.kt │ ├── LengthConstraintsWriterTest.kt │ ├── LogicConstraintsWriterTest.kt │ ├── OrderedElementsWriterTest.kt │ ├── PrecisionWriterTest.kt │ ├── RegexWriterTest.kt │ ├── TimestampOffsetWriterTest.kt │ ├── TimestampPrecisionWriterTest.kt │ ├── ValidValuesWriterTest.kt │ └── util.kt └── settings.gradle /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | *Related PRs in ion-schema, ion-schema-tests, ion-schema-schemas:* 6 | 7 | 8 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 9 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "Build and Report Generation" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | java: [8, 11] 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | - name: Set up JDK 18 | uses: actions/setup-java@v3 19 | with: 20 | distribution: corretto 21 | java-version: ${{ matrix.java }} 22 | - name: Cache Gradle packages 23 | uses: actions/cache@v1 24 | with: 25 | path: ~/.gradle/caches 26 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 27 | restore-keys: ${{ runner.os }}-gradle 28 | - name: Build with Gradle 29 | run: ./gradlew build 30 | report-generation: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | with: 35 | submodules: recursive 36 | - name: Use Java 11 37 | uses: actions/setup-java@v3 38 | with: 39 | distribution: corretto 40 | java-version: 11 41 | - run: ./gradlew koverXmlReport 42 | - name: Upload Ion Schema Code Coverage 43 | uses: codecov/codecov-action@v3 44 | with: 45 | file: ion-schema/build/reports/kover/report.xml 46 | - name: Upload CLI Code Coverage 47 | uses: codecov/codecov-action@v3 48 | with: 49 | file: cli/build/reports/kover/report.xml 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.DS_Store 2 | /.gradle 3 | /.idea 4 | /build 5 | /*/build 6 | /out 7 | /*.iml 8 | *.swp 9 | *.~ 10 | ~$* 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ion-schema-tests"] 2 | path = ion-schema-tests 3 | url = https://github.com/amazon-ion/ion-schema-tests.git 4 | [submodule "ion-schema-schemas"] 5 | path = ion-schema/src/main/resources/ion-schema-schemas 6 | url = https://github.com/amazon-ion/ion-schema-schemas.git 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Ion Schema Reference Implementation (Kotlin) 2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | # Ion Schema CLI 2 | 3 | ``` 4 | Usage: ion-schema-cli [OPTIONS] COMMAND [ARGS]... 5 | 6 | Options: 7 | --version Show the version and exit 8 | -h, --help Show this message and exit 9 | 10 | ``` 11 | 12 | 13 | # Building the CLI 14 | 15 | The CLI is built during the main Gradle build. To build it separately, execute: 16 | 17 | ```shell 18 | ./gradlew cli:build 19 | ``` 20 | 21 | After building, distributable archive files are located in the `cli/build/distributions` directory (relative to the 22 | project root). 23 | 24 | # Using the CLI 25 | 26 | The following command will build any dependencies before starting the CLI. 27 | 28 | ```shell 29 | ./gradlew cli:run -q --args="" 30 | ``` 31 | 32 | For convenience, `ion-schema-cli` (in the root directory of the project) wraps this command, so you can also 33 | run the CLI without having to quote any arguments. 34 | ```shell 35 | ./ion-schema-cli --help 36 | ``` 37 | 38 | # Commands 39 | 40 | TODO—this section will contain details about the ion-schema-cli subcommands once they have been added. 41 | -------------------------------------------------------------------------------- /cli/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | plugins { 17 | id "org.jetbrains.kotlin.jvm" 18 | id 'application' 19 | } 20 | 21 | version = "0.1.0-SNAPSHOT" 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | dependencies { 28 | implementation project(':ion-schema') 29 | implementation 'com.amazon.ion:ion-java:1.+' 30 | implementation 'com.amazon.ion:ion-element:1.0.0' 31 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 32 | implementation "com.github.ajalt.clikt:clikt:3.2.0" 33 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 34 | testImplementation 'io.kotest:kotest-assertions-core-jvm:[4.0,5.0[' 35 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2' 36 | testImplementation 'org.junit.jupiter:junit-jupiter-params:5.6.2' 37 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2' 38 | } 39 | 40 | application { 41 | mainClass.set("com.amazon.ionschema.cli.MainKt") 42 | applicationName = 'ion-schema-cli' // startup script name 43 | } 44 | 45 | tasks['run'].standardInput = System.in 46 | -------------------------------------------------------------------------------- /cli/src/main/kotlin/com/amazon/ionschema/cli/Main.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.cli 17 | 18 | import com.amazon.ionschema.cli.commands.RepairCommand 19 | import com.amazon.ionschema.cli.commands.ValidateCommand 20 | import com.github.ajalt.clikt.core.NoOpCliktCommand 21 | import com.github.ajalt.clikt.core.context 22 | import com.github.ajalt.clikt.core.subcommands 23 | import com.github.ajalt.clikt.output.CliktHelpFormatter 24 | import com.github.ajalt.clikt.parameters.options.versionOption 25 | import java.util.Properties 26 | 27 | fun main(args: Array) = IonSchemaCli().main(args) 28 | 29 | /** 30 | * This is the root command. It doesn't run anything on its own—you must invoke a subcommand. 31 | */ 32 | class IonSchemaCli : NoOpCliktCommand( 33 | name = "ion-schema-cli", 34 | help = "TODO" 35 | ) { 36 | init { 37 | context { 38 | subcommands( 39 | ValidateCommand(), 40 | RepairCommand(), 41 | ) 42 | versionOption(getVersionString()) 43 | helpFormatter = CliktHelpFormatter(showRequiredTag = true, showDefaultValues = true) 44 | } 45 | } 46 | 47 | private fun getVersionString(): String { 48 | val propertiesStream = this.javaClass.getResourceAsStream("/cli.properties") 49 | val properties = Properties().apply { load(propertiesStream) } 50 | return "${properties.getProperty("version")}-${properties.getProperty("commit")}" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cli/src/main/kotlin/com/amazon/ionschema/cli/commands/RepairCommand.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.cli.commands 2 | 3 | import com.amazon.ionschema.cli.commands.repair.FixTransitiveImportsCommand 4 | import com.github.ajalt.clikt.core.NoOpCliktCommand 5 | import com.github.ajalt.clikt.core.context 6 | import com.github.ajalt.clikt.core.subcommands 7 | 8 | class RepairCommand : NoOpCliktCommand( 9 | help = "Fixes schemas that are affected by a bug in some way." 10 | ) { 11 | init { 12 | context { 13 | subcommands( 14 | FixTransitiveImportsCommand() 15 | ) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cli/src/main/kotlin/com/amazon/ionschema/cli/util/PatchSet.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.cli.util 2 | 3 | import java.util.TreeSet 4 | 5 | class PatchSet { 6 | private data class Patch(val start: Int, val endInclusive: Int, val replacementText: String) 7 | 8 | private val patchSet = TreeSet(compareByDescending { it.start }) 9 | 10 | fun hasChanges() = patchSet.isNotEmpty() 11 | 12 | fun patch(start: Int, endInclusive: Int, replacementText: String) { 13 | patchSet.add(Patch(start, endInclusive, replacementText)) 14 | } 15 | 16 | fun replaceAll(newText: String) { 17 | patchSet.add(Patch(0, -1, newText)) 18 | } 19 | 20 | fun applyTo(original: String): String { 21 | val sb = StringBuilder(original) 22 | for ((start, endInclusive, newText) in patchSet) { 23 | val endExclusive = if (endInclusive == -1) original.length else endInclusive + 1 24 | sb.replace(start, endExclusive, newText) 25 | } 26 | return sb.toString() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/src/main/kotlin/com/amazon/ionschema/cli/util/ion.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.cli.util 2 | 3 | import com.amazon.ionelement.api.AnyElement 4 | import com.amazon.ionelement.api.ContainerElement 5 | import com.amazon.ionelement.api.IonElement 6 | 7 | sealed class TraversalOrder 8 | internal object PreOrder : TraversalOrder() 9 | internal object PostOrder : TraversalOrder() 10 | 11 | /** 12 | * Visits every element contained in (and including) this [IonElement]. 13 | */ 14 | internal fun IonElement.recursivelyVisit(order: TraversalOrder, visitor: (AnyElement) -> Unit) { 15 | with(this.asAnyElement()) { 16 | if (order is PreOrder) visitor(this) 17 | if (this is ContainerElement) asContainerOrNull()?.values?.forEach { child -> child.recursivelyVisit(order, visitor) } 18 | if (order is PostOrder) visitor(this) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cli/src/test/kotlin/com/amazon/ionschema/cli/VersionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.cli 17 | 18 | import com.github.ajalt.clikt.core.PrintMessage 19 | import io.kotest.assertions.throwables.shouldThrow 20 | import io.kotest.matchers.should 21 | import io.kotest.matchers.string.shouldMatch 22 | import org.junit.jupiter.api.Test 23 | 24 | class VersionTest { 25 | 26 | @Test 27 | fun testVersionCommand() { 28 | shouldThrow { 29 | IonSchemaCli().parse(arrayOf("--version")) 30 | }.should { 31 | it.message shouldMatch """ion-schema-cli version \d+\.\d+\.\d+(-SNAPSHOT)?-[0-9a-f]{7}""" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cli/src/test/kotlin/com/amazon/ionschema/cli/util/filesTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.cli.util 2 | 3 | import org.junit.jupiter.api.Assertions 4 | import org.junit.jupiter.api.Test 5 | 6 | class DetectIndentTest { 7 | @Test 8 | fun `when no indentation should return null`() { 9 | val text = """ 10 | |Lorem ipsum dolor englebert. 11 | | 12 | |Quando omni flunkus moritati. 13 | """.trimMargin() 14 | Assertions.assertEquals(null, text.inferIndent()) 15 | } 16 | 17 | @Test 18 | fun `when spaces indentation should return correct number of spaces`() { 19 | val text = """ 20 | |type::{ 21 | | name: positive_int, 22 | | type: int, 23 | | valid_values: range::[1, max], 24 | |} 25 | """.trimMargin() 26 | Assertions.assertEquals(" ", text.inferIndent()) 27 | } 28 | 29 | @Test 30 | fun `when spaces indentation is inconsistent should return null`() { 31 | val text = """ 32 | |type::{ 33 | | name: positive_int, 34 | | type: int, 35 | | valid_values: range::[1, max], 36 | |} 37 | """.trimMargin() 38 | Assertions.assertEquals(null, text.inferIndent()) 39 | } 40 | 41 | @Test 42 | fun `when any tabs should return tab character`() { 43 | val t = '\t' 44 | val text = """ 45 | |class Foo { 46 | |${t}val bar = 1 47 | |} 48 | """.trimMargin() 49 | Assertions.assertEquals("\t", text.inferIndent()) 50 | } 51 | 52 | @Test 53 | fun `blank lines should not influence the outcome`() { 54 | val tab = '\t' 55 | val spaces = " " 56 | val text = """ 57 | |type::{ 58 | | 59 | | name: positive_int, 60 | | $spaces 61 | | type: int, 62 | |$tab$tab$tab 63 | | valid_values: range::[1, max], 64 | |} 65 | """.trimMargin() 66 | Assertions.assertEquals(" ", text.inferIndent()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/aliased_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | { id: "floats.isl", type: positive_float, as: aliased_positive_float }, 9 | { id: "floats.isl", type: positive_int, as: aliased_positive_int }, 10 | { id: "floats.isl", type: negative_int, as: aliased_negative_int }, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | aliased_positive_int, 19 | aliased_negative_int, 20 | aliased_positive_float 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/export.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | schema_header::{ 3 | imports: [ 4 | { id: "floats.isl" }, 5 | ], 6 | open_content: "Open content should be preserved!", 7 | } 8 | 9 | schema_footer::{ 10 | open_content: "Open content should be preserved!", 11 | } 12 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/floats.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | // In the "unfixed" version, this file transitively exports positive_int and negative_int 8 | imports: [ 9 | { id: "ints.isl" } 10 | ], 11 | open_content: "Open content should be preserved!", 12 | } 13 | 14 | type::{ 15 | name: positive_float, 16 | type: float, 17 | valid_values: [range::[exclusive::0, max], +inf], 18 | open_content: "Open content should be preserved!", 19 | } 20 | 21 | type::{ 22 | name: negative_float, 23 | type: float, 24 | valid_values: [range::[min, exclusive::0], -inf], 25 | open_content: "Open content should be preserved!", 26 | } 27 | 28 | schema_footer::{ 29 | open_content: "Open content should be preserved!", 30 | } 31 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/inline_imports.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | schema_header:: { 4 | // A comment that should be preserved! 5 | open_content: "Open content should be preserved!", 6 | } 7 | 8 | type::{ 9 | // A comment that should be preserved! 10 | name: foo, 11 | // Another comment that should be preserved! 12 | open_content: "Open content should be preserved!", 13 | any_of: [ 14 | nullable::{ id: "floats.isl", type: positive_int }, 15 | { id: "floats.isl", type: negative_int }, 16 | { id: "floats.isl", type: positive_float }, 17 | ], 18 | } 19 | 20 | schema_footer::{ 21 | // A comment that should be preserved! 22 | open_content: "Open content should be preserved!", 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/ints.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | type::{ 6 | name: positive_int, 7 | type: int, 8 | valid_values: range::[1, max], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: negative_int, 14 | type: int, 15 | valid_values: range::[min, -1], 16 | open_content: "Open content should be preserved!", 17 | } 18 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/type_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | { id: "floats.isl", type: negative_float }, 9 | { id: "floats.isl", type: positive_int }, 10 | { id: "floats.isl", type: negative_int }, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | positive_int, 19 | negative_float, 20 | negative_int, 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/input/wildcard_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ { id: "floats.isl" } ], 8 | open_content: "Open content should be preserved!", 9 | } 10 | 11 | type::{ 12 | name: Foo, 13 | any_of: [ 14 | positive_int, 15 | positive_float, 16 | negative_int, 17 | ], 18 | open_content: "Open content should be preserved!", 19 | } 20 | 21 | schema_footer::{ 22 | open_content: "Open content should be preserved!", 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/aliased_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:positive_float,as:aliased_positive_float}, 9 | {id:"ints.isl",type:negative_int,as:aliased_negative_int}, 10 | {id:"ints.isl",type:positive_int,as:aliased_positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | aliased_positive_int, 19 | aliased_negative_int, 20 | aliased_positive_float 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/export.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | // Schema 'export.isl' 4 | // 5 | // The purpose of this schema is to decouple consumers of the schema from the 6 | // implementation details (ie. specific locations) of each type that it provides, 7 | // and to indicate to consumers, which types they SHOULD use. Consumers of this 8 | // type CAN bypass this schema and import other types directly, but they SHOULD NOT 9 | // unless directed to do so by the owner(s)/author(s) of this schema. 10 | // 11 | // The type 12 | // type::{name:foobar,type:{id:"bar.isl",type:foo}} 13 | // is analogous to 14 | // [Javascript]: export { foo as foobar } from 'bar.isl' 15 | // [Rust]: pub use bar::foo as foobar; 16 | 17 | 18 | type::{name:negative_float,type:{id:"floats.isl",type:negative_float}} 19 | type::{name:negative_int,type:{id:"ints.isl",type:negative_int}} 20 | type::{name:positive_float,type:{id:"floats.isl",type:positive_float}} 21 | type::{name:positive_int,type:{id:"ints.isl",type:positive_int}} 22 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/floats.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | // In the "unfixed" version, this file transitively exports positive_int and negative_int 8 | imports: [], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: positive_float, 14 | type: float, 15 | valid_values: [range::[exclusive::0, max], +inf], 16 | open_content: "Open content should be preserved!", 17 | } 18 | 19 | type::{ 20 | name: negative_float, 21 | type: float, 22 | valid_values: [range::[min, exclusive::0], -inf], 23 | open_content: "Open content should be preserved!", 24 | } 25 | 26 | schema_footer::{ 27 | open_content: "Open content should be preserved!", 28 | } 29 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/inline_imports.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | schema_header:: { 4 | // A comment that should be preserved! 5 | open_content: "Open content should be preserved!", 6 | } 7 | 8 | type::{ 9 | // A comment that should be preserved! 10 | name: foo, 11 | // Another comment that should be preserved! 12 | open_content: "Open content should be preserved!", 13 | any_of: [ 14 | nullable::{id:"ints.isl",type:positive_int}, 15 | {id:"ints.isl",type:negative_int}, 16 | { id: "floats.isl", type: positive_float }, 17 | ], 18 | } 19 | 20 | schema_footer::{ 21 | // A comment that should be preserved! 22 | open_content: "Open content should be preserved!", 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/ints.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | type::{ 6 | name: positive_int, 7 | type: int, 8 | valid_values: range::[1, max], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: negative_int, 14 | type: int, 15 | valid_values: range::[min, -1], 16 | open_content: "Open content should be preserved!", 17 | } 18 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/type_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:negative_float}, 9 | {id:"ints.isl",type:negative_int}, 10 | {id:"ints.isl",type:positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | positive_int, 19 | negative_float, 20 | negative_int, 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-KeepWildcards/wildcard_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl"}, 9 | {id:"ints.isl",type:negative_int}, 10 | {id:"ints.isl",type:positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | positive_int, 19 | positive_float, 20 | negative_int, 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/aliased_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:positive_float,as:aliased_positive_float}, 9 | {id:"ints.isl",type:negative_int,as:aliased_negative_int}, 10 | {id:"ints.isl",type:positive_int,as:aliased_positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | aliased_positive_int, 19 | aliased_negative_int, 20 | aliased_positive_float 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/export.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | // Schema 'export.isl' 4 | // 5 | // The purpose of this schema is to decouple consumers of the schema from the 6 | // implementation details (ie. specific locations) of each type that it provides, 7 | // and to indicate to consumers, which types they SHOULD use. Consumers of this 8 | // type CAN bypass this schema and import other types directly, but they SHOULD NOT 9 | // unless directed to do so by the owner(s)/author(s) of this schema. 10 | // 11 | // The type 12 | // type::{name:foobar,type:{id:"bar.isl",type:foo}} 13 | // is analogous to 14 | // [Javascript]: export { foo as foobar } from 'bar.isl' 15 | // [Rust]: pub use bar::foo as foobar; 16 | 17 | 18 | type::{name:negative_float,type:{id:"floats.isl",type:negative_float}} 19 | type::{name:negative_int,type:{id:"ints.isl",type:negative_int}} 20 | type::{name:positive_float,type:{id:"floats.isl",type:positive_float}} 21 | type::{name:positive_int,type:{id:"ints.isl",type:positive_int}} 22 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/floats.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | // In the "unfixed" version, this file transitively exports positive_int and negative_int 8 | imports: [], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: positive_float, 14 | type: float, 15 | valid_values: [range::[exclusive::0, max], +inf], 16 | open_content: "Open content should be preserved!", 17 | } 18 | 19 | type::{ 20 | name: negative_float, 21 | type: float, 22 | valid_values: [range::[min, exclusive::0], -inf], 23 | open_content: "Open content should be preserved!", 24 | } 25 | 26 | schema_footer::{ 27 | open_content: "Open content should be preserved!", 28 | } 29 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/inline_imports.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | schema_header:: { 4 | // A comment that should be preserved! 5 | open_content: "Open content should be preserved!", 6 | } 7 | 8 | type::{ 9 | // A comment that should be preserved! 10 | name: foo, 11 | // Another comment that should be preserved! 12 | open_content: "Open content should be preserved!", 13 | any_of: [ 14 | nullable::{id:"ints.isl",type:positive_int}, 15 | {id:"ints.isl",type:negative_int}, 16 | { id: "floats.isl", type: positive_float }, 17 | ], 18 | } 19 | 20 | schema_footer::{ 21 | // A comment that should be preserved! 22 | open_content: "Open content should be preserved!", 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/ints.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | type::{ 6 | name: positive_int, 7 | type: int, 8 | valid_values: range::[1, max], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: negative_int, 14 | type: int, 15 | valid_values: range::[min, -1], 16 | open_content: "Open content should be preserved!", 17 | } 18 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/type_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:negative_float}, 9 | {id:"ints.isl",type:negative_int}, 10 | {id:"ints.isl",type:positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | positive_int, 19 | negative_float, 20 | negative_int, 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-NoWildcards/wildcard_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:positive_float}, 9 | {id:"ints.isl",type:negative_int}, 10 | {id:"ints.isl",type:positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | positive_int, 19 | positive_float, 20 | negative_int, 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/aliased_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl",type:positive_float,as:aliased_positive_float}, 9 | {id:"ints.isl",type:negative_int,as:aliased_negative_int}, 10 | {id:"ints.isl",type:positive_int,as:aliased_positive_int}, 11 | ], 12 | open_content: "Open content should be preserved!", 13 | } 14 | 15 | type::{ 16 | name: Foo, 17 | any_of: [ 18 | aliased_positive_int, 19 | aliased_negative_int, 20 | aliased_positive_float 21 | ], 22 | open_content: "Open content should be preserved!", 23 | } 24 | 25 | schema_footer::{ 26 | open_content: "Open content should be preserved!", 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/export.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | // Schema 'export.isl' 4 | // 5 | // The purpose of this schema is to decouple consumers of the schema from the 6 | // implementation details (ie. specific locations) of each type that it provides, 7 | // and to indicate to consumers, which types they SHOULD use. Consumers of this 8 | // type CAN bypass this schema and import other types directly, but they SHOULD NOT 9 | // unless directed to do so by the owner(s)/author(s) of this schema. 10 | // 11 | // The type 12 | // type::{name:foobar,type:{id:"bar.isl",type:foo}} 13 | // is analogous to 14 | // [Javascript]: export { foo as foobar } from 'bar.isl' 15 | // [Rust]: pub use bar::foo as foobar; 16 | 17 | 18 | type::{name:negative_float,type:{id:"floats.isl",type:negative_float}} 19 | type::{name:negative_int,type:{id:"ints.isl",type:negative_int}} 20 | type::{name:positive_float,type:{id:"floats.isl",type:positive_float}} 21 | type::{name:positive_int,type:{id:"ints.isl",type:positive_int}} 22 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/floats.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | // In the "unfixed" version, this file transitively exports positive_int and negative_int 8 | imports: [], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: positive_float, 14 | type: float, 15 | valid_values: [range::[exclusive::0, max], +inf], 16 | open_content: "Open content should be preserved!", 17 | } 18 | 19 | type::{ 20 | name: negative_float, 21 | type: float, 22 | valid_values: [range::[min, exclusive::0], -inf], 23 | open_content: "Open content should be preserved!", 24 | } 25 | 26 | schema_footer::{ 27 | open_content: "Open content should be preserved!", 28 | } 29 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/inline_imports.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | schema_header:: { 4 | // A comment that should be preserved! 5 | open_content: "Open content should be preserved!", 6 | } 7 | 8 | type::{ 9 | // A comment that should be preserved! 10 | name: foo, 11 | // Another comment that should be preserved! 12 | open_content: "Open content should be preserved!", 13 | any_of: [ 14 | nullable::{id:"ints.isl",type:positive_int}, 15 | {id:"ints.isl",type:negative_int}, 16 | { id: "floats.isl", type: positive_float }, 17 | ], 18 | } 19 | 20 | schema_footer::{ 21 | // A comment that should be preserved! 22 | open_content: "Open content should be preserved!", 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/ints.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | type::{ 6 | name: positive_int, 7 | type: int, 8 | valid_values: range::[1, max], 9 | open_content: "Open content should be preserved!", 10 | } 11 | 12 | type::{ 13 | name: negative_int, 14 | type: int, 15 | valid_values: range::[min, -1], 16 | open_content: "Open content should be preserved!", 17 | } 18 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/type_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl"}, 9 | {id:"ints.isl"}, 10 | ], 11 | open_content: "Open content should be preserved!", 12 | } 13 | 14 | type::{ 15 | name: Foo, 16 | any_of: [ 17 | positive_int, 18 | negative_float, 19 | negative_int, 20 | ], 21 | open_content: "Open content should be preserved!", 22 | } 23 | 24 | schema_footer::{ 25 | open_content: "Open content should be preserved!", 26 | } 27 | -------------------------------------------------------------------------------- /cli/src/test/resources/FixTransitiveImportsCommand/output-PreferWildcards/wildcard_import.isl: -------------------------------------------------------------------------------- 1 | $ion_schema_1_0 2 | 3 | "Open content should be preserved!" 4 | 5 | schema_header::{ 6 | // A comment that should be preserved! 7 | imports: [ 8 | {id:"floats.isl"}, 9 | {id:"ints.isl"}, 10 | ], 11 | open_content: "Open content should be preserved!", 12 | } 13 | 14 | type::{ 15 | name: Foo, 16 | any_of: [ 17 | positive_int, 18 | positive_float, 19 | negative_int, 20 | ], 21 | open_content: "Open content should be preserved!", 22 | } 23 | 24 | schema_footer::{ 25 | open_content: "Open content should be preserved!", 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=EMPTY 2 | signing.password=EMPTY 3 | signing.secretKeyRingFile=EMPTY 4 | 5 | ossrhUsername=EMPTY 6 | ossrhPassword=EMPTY 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-ion/ion-schema-kotlin/7a9ee6d06e9cfdaa530310f5d9be9f5a52680d3b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ion-schema-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This script is a wrapper for running the ion-schema-cli that ensures that 3 | # the latest changes are built every time you run the cli using this script. 4 | 5 | # `[ -t 0]` tests whether STDIN is a tty 6 | if [ -t 0 ]; then 7 | ./gradlew cli:install --quiet && exec cli/build/install/ion-schema-cli/bin/ion-schema-cli "$@" 8 | else 9 | # If STDIN is not TTY, then capture the data to prevent it from going to ./gradlew, 10 | # and then re-send it before running the ion-schema-cli launcher script 11 | DATA=$(cat) 12 | ./gradlew cli:install --quiet && (echo "${DATA[@]}" | exec cli/build/install/ion-schema-cli/bin/ion-schema-cli "$@") 13 | fi 14 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/Authority.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.util.CloseableIterator 20 | 21 | /** 22 | * An Authority is responsible for resolving a particular class of 23 | * schema identifiers. 24 | * 25 | * The structure of a schema identifier string is defined by the 26 | * Authority responsible for the schema/type(s) being imported. 27 | * 28 | * **Runtime resolution of a schema over a network presents availability and security risks, and should thereby be avoided.** 29 | * 30 | * @see AuthorityFilesystem 31 | */ 32 | interface Authority { 33 | /** 34 | * Provides a CloseableIterator for the requested schema identifier. 35 | * If an error condition is encountered while attempting to resolve the schema 36 | * identifier, this method should throw an exception. If no error conditions 37 | * were encountered, but the schema identifier can't be resolved, this method 38 | * should return [EMPTY_ITERATOR]. 39 | */ 40 | fun iteratorFor(iss: IonSchemaSystem, id: String): CloseableIterator 41 | } 42 | 43 | /** 44 | * A singleton iterator which has nothing to iterate over. 45 | */ 46 | val EMPTY_ITERATOR = object : CloseableIterator { 47 | override fun hasNext() = false 48 | override fun next(): IonValue = throw NoSuchElementException() 49 | override fun close() { } 50 | } 51 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/Import.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | /** 19 | * An Import represents all the types imported by a Schema 20 | * from one schema id. 21 | * 22 | * Note that multiple ISL imports referencing the same schema id 23 | * (each importing/aliasing an individual type) are represented by a 24 | * single Import instance. 25 | */ 26 | interface Import { 27 | /** 28 | * The id of the referenced schema. 29 | */ 30 | val id: String 31 | 32 | /** 33 | * Returns the schema referenced by the import. 34 | */ 35 | fun getSchema(): Schema 36 | 37 | /** 38 | * Returns the requested type, if present in this import; 39 | * otherwise returns null. If a type is aliased (via "as") 40 | * by the import, the type will only be returned from this 41 | * method by its alias (not the imported type's original name). 42 | */ 43 | fun getType(name: String): Type? 44 | 45 | /** 46 | * Returns an iterator over the types imported by this Import. 47 | * Callers must not rely on any particular ordering, as Types 48 | * may be returned in any order. 49 | */ 50 | fun getTypes(): Iterator 51 | } 52 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/InvalidSchemaException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | import com.amazon.ionschema.reader.internal.ReadError 19 | 20 | /** 21 | * Thrown when an invalid schema definition is encountered. 22 | */ 23 | class InvalidSchemaException(message: String) : IonSchemaException(message) { 24 | 25 | /** 26 | * Indicates whether this exception is a fail-fast exception (i.e. it should not be caught by any error collectors 27 | * in an Ion Schema reader). 28 | */ 29 | private var failFast = false 30 | internal fun isFailFast() = failFast 31 | 32 | companion object { 33 | /** 34 | * Factory function to create and throw a fail-fast [IonSchemaException] from a [ReadError]. 35 | */ 36 | internal fun failFast(error: ReadError): Nothing { 37 | val e = InvalidSchemaException(error.message) 38 | e.failFast = true 39 | throw e 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/IonSchemaException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | /** 19 | * The top-level Ion Schema exception. 20 | */ 21 | open class IonSchemaException(message: String) : RuntimeException(message) 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/IonSchemaSchemas.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | /** 19 | * Contains utilities for working with [Ion Schema Schemas](https://github.com/amazon-ion/ion-schema-schemas). 20 | */ 21 | object IonSchemaSchemas { 22 | 23 | @JvmStatic 24 | private val AUTHORITY = ResourceAuthority("ion-schema-schemas", IonSchemaSchemas.javaClass.classLoader) 25 | 26 | /** 27 | * Returns an [Authority] implementation that provides [Ion Schema Schemas](https://github.com/amazon-ion/ion-schema-schemas). 28 | */ 29 | @JvmStatic 30 | fun authority(): Authority = AUTHORITY 31 | 32 | /** 33 | * Schema ID to use to load the schema for a specific version of the Ion Schema language. The schema at this ID 34 | * has the types, `schema`, `schema_header`, `schema_footer`, `named_type_definition`, and `inline_type_definition` 35 | * which can be used to validate fragments of ISL or a whole Ion Schema document. 36 | */ 37 | @JvmStatic 38 | fun getSchemaIdForIslVersion(version: IonSchemaVersion) = when (version) { 39 | IonSchemaVersion.v1_0 -> "isl/ion_schema_1_0.isl" 40 | IonSchemaVersion.v2_0 -> "isl/ion_schema_2_0.isl" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/IonSchemaVersion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | import com.amazon.ion.IonSymbol 19 | import com.amazon.ion.IonValue 20 | import kotlin.contracts.ExperimentalContracts 21 | import kotlin.contracts.contract 22 | 23 | enum class IonSchemaVersion(val symbolText: String) { 24 | v1_0("\$ion_schema_1_0"), 25 | v2_0("\$ion_schema_2_0"); 26 | 27 | companion object { 28 | internal fun fromIonSymbolOrNull(symbol: IonSymbol): IonSchemaVersion? = values().singleOrNull { it.symbolText == symbol.stringValue() } 29 | 30 | /** 31 | * Tests if the IonValue is a value that is reserved for version markers, as per 32 | * [ISL Versioning](https://amazon-ion.github.io/ion-schema/docs/isl-versioning#ion-schema-version-markers). 33 | */ 34 | @OptIn(ExperimentalContracts::class) 35 | internal fun isVersionMarker(value: IonValue): Boolean { 36 | contract { returns(true) implies (value is IonSymbol) } 37 | return value is IonSymbol && !value.isNullValue && IonSchemaVersion.VERSION_MARKER_REGEX.matches(value.stringValue()) 38 | } 39 | 40 | @JvmStatic 41 | private val VERSION_MARKER_REGEX = Regex("^\\\$ion_schema_\\d.*") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/SchemaCacheDefault.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema 2 | 3 | import java.util.concurrent.ConcurrentHashMap 4 | 5 | /** 6 | * The default SchemaCache implementation, backed by a ConcurrentHashMap. 7 | */ 8 | class SchemaCacheDefault : SchemaCache { 9 | private val cache = ConcurrentHashMap() 10 | 11 | override fun getOrPut(key: String, resolver: () -> Schema): Schema = cache.getOrPut(key, resolver) 12 | 13 | override fun getOrNull(key: String): Schema? { 14 | return cache.get(key) 15 | } 16 | 17 | override fun invalidate(key: String) { 18 | cache.remove(key) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/CommonViolations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Violation 20 | 21 | /** 22 | * Provides methods to create Violations that are common across multiple 23 | * constraints. 24 | */ 25 | internal object CommonViolations { 26 | fun INVALID_TYPE(constraint: IonValue, value: IonValue) = Violation( 27 | constraint, 28 | "invalid_type", 29 | "not applicable for type %s".format(value.type.toString().toLowerCase()) 30 | ) 31 | 32 | fun NULL_VALUE(constraint: IonValue) = Violation( 33 | constraint, 34 | "null_value", 35 | "not applicable for null values" 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/Constraint.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Violations 20 | 21 | /** 22 | * Represents a single constraint. 23 | */ 24 | internal interface Constraint { 25 | /** 26 | * The name of the constraint. 27 | */ 28 | val name: String 29 | 30 | /** 31 | * Checks this constraint against the provided value, 32 | * adding [Violation]s and/or [ViolationChild]ren to issues 33 | * if the constraint is violated. 34 | */ 35 | fun validate(value: IonValue, issues: Violations) 36 | } 37 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/ConstraintFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.IonSchemaVersion 20 | 21 | /** 22 | * Factory for [Constraint] objects. 23 | */ 24 | internal interface ConstraintFactory { 25 | /** 26 | * If [name] is a recognized constraint name, returns `true`, otherwise `false` based on the ISL version that given [schema] uses. 27 | */ 28 | fun isConstraint(name: String, version: IonSchemaVersion): Boolean 29 | 30 | /** 31 | * Instantiates a new [Constraint] as defined by [ion]. 32 | * @param[ion] IonValue identifying the constraint to construct as well as its configuration 33 | * @param[schema] passed to constraints that require a schema object 34 | */ 35 | fun constraintFor(ion: IonValue, schema: SchemaInternal, referenceManager: DeferredReferenceManager): Constraint 36 | } 37 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/ImportImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ionschema.Import 19 | import com.amazon.ionschema.Schema 20 | import com.amazon.ionschema.Type 21 | 22 | /** 23 | * Implementation of [Import] for all user-provided ISL. 24 | * 25 | * This is no longer used to support any of the internal functionality of Ion Schema Kotlin. It exists only for the 26 | * [Schema.getImports] and [Schema.getImport] functions. 27 | * 28 | * Do not call any functions of this class (directly or indirectly) from within the init block of _any_ [Schema] 29 | * implementation. If you do, the call may fail, depending on the order that schema imports are resolved, which is not 30 | * guaranteed to be stable. 31 | * 32 | * This class (and [Import]) should be removed in v2.0.0 in favor of a map of schemaId to [Type] (or similar). 33 | */ 34 | internal class ImportImpl( 35 | override val id: String, 36 | private val schemaProvider: () -> Schema, 37 | private val types: Map 38 | ) : Import { 39 | 40 | override fun getSchema() = schemaProvider() 41 | 42 | override fun getType(name: String) = types[name] 43 | 44 | override fun getTypes(): Iterator = types.values.iterator() 45 | } 46 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/SchemaInternal.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ionschema.Schema 19 | 20 | /** 21 | * Provides some internal-only members of schema implementations. 22 | * Also, overrides some members of [Schema] to have internal-only return types. 23 | * 24 | * All implementations of a schema must implement [SchemaInternal]. 25 | */ 26 | internal interface SchemaInternal : Schema { 27 | val schemaId: String? 28 | 29 | /** 30 | * The in-scope types for a schema consist of the ISL core types, any types declared in that schema, and any types 31 | * that are imported to the schema. This function is intended for internal use only in order to detect naming 32 | * conflicts and to handle type references that are just a type name. 33 | */ 34 | fun getInScopeType(name: String): TypeInternal? 35 | 36 | // These are all present in [Schema], but are being overridden to have the internal return type. 37 | override fun getDeclaredType(name: String): TypeInternal? 38 | override fun getDeclaredTypes(): Iterator 39 | override fun getType(name: String): TypeInternal? 40 | override fun getTypes(): Iterator 41 | override fun getSchemaSystem(): IonSchemaSystemImpl 42 | } 43 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/TypeAliased.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonSymbol 19 | import com.amazon.ionschema.internal.constraint.ConstraintBase 20 | 21 | /** 22 | * Implementation of [Type] representing a type imported with an alias. 23 | */ 24 | internal class TypeAliased( 25 | ion: IonSymbol, 26 | internal val type: ImportedType 27 | ) : TypeInternal, ImportedType by type, ConstraintBase(ion) { 28 | 29 | override val name = ion.stringValue() 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/TypeInline.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonStruct 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.Violation 21 | import com.amazon.ionschema.Violations 22 | import com.amazon.ionschema.internal.constraint.ConstraintBase 23 | 24 | /** 25 | * Implementation of [Type] corresponding to inline type definitions. 26 | */ 27 | internal class TypeInline private constructor ( 28 | ion: IonStruct, 29 | private val type: TypeInternal 30 | ) : ConstraintBase(ion), TypeInternal by type { 31 | 32 | constructor(ionStruct: IonStruct, schema: SchemaInternal, referenceManager: DeferredReferenceManager) : 33 | this(ionStruct, TypeImpl(ionStruct, schema, referenceManager)) 34 | 35 | override val name = type.name 36 | 37 | override fun validate(value: IonValue, issues: Violations) { 38 | val violation = Violation(ion, "type_mismatch") 39 | type.validate(value, violation) 40 | if (!violation.isValid()) { 41 | violation.message = "expected type %s".format(name) 42 | issues.add(violation) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/TypeInternal.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Type 20 | 21 | /** 22 | * Internal methods for interacting with [Type]s. 23 | */ 24 | internal interface TypeInternal : Type, Constraint { 25 | 26 | /** 27 | * The name of the schemaId that this type was defined in. 28 | */ 29 | override val schemaId: String? 30 | 31 | @Deprecated("Only used for Ion Schema 1.0 code paths. No new usages should be introduced.") 32 | fun getBaseType(): TypeBuiltin 33 | 34 | @Deprecated("Only used for Ion Schema 1.0 code paths. No new usages should be introduced.") 35 | fun isValidForBaseType(value: IonValue): Boolean 36 | } 37 | 38 | /** 39 | * Represents a type that was imported from another schema. 40 | */ 41 | internal interface ImportedType : TypeInternal { 42 | /** 43 | * The name of the schemaId that this type was imported 44 | * from. This can only be different from [schemaId] if 45 | * transitive imports are enabled. 46 | */ 47 | val importedFromSchemaId: String 48 | 49 | /** 50 | * The name of the schemaId that this type was defined in. 51 | * Unlike [TypeInternal], this is always non-null since we 52 | * can't import types from a schema if that schema has no id. 53 | */ 54 | override val schemaId: String 55 | } 56 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/TypeNamed.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonSymbol 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.Violation 21 | import com.amazon.ionschema.Violations 22 | import com.amazon.ionschema.internal.constraint.ConstraintBase 23 | 24 | /** 25 | * Implementation of [Type] representing types identified only by name. 26 | */ 27 | internal class TypeNamed( 28 | ion: IonSymbol, 29 | internal val type: TypeInternal 30 | ) : TypeInternal by type, ConstraintBase(ion) { 31 | 32 | override fun validate(value: IonValue, issues: Violations) { 33 | val violation = Violation(ion, "type_mismatch") 34 | type.validate(value, violation) 35 | if (!violation.isValid()) { 36 | violation.message = "expected type %s".format(name) 37 | issues.add(violation) 38 | } 39 | } 40 | 41 | override val name = ion.stringValue() 42 | } 43 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/TypeOrNullDecorator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal 17 | 18 | import com.amazon.ion.IonType 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.IonSchemaVersion.v2_0 21 | import com.amazon.ionschema.Schema 22 | import com.amazon.ionschema.Violations 23 | import com.amazon.ionschema.internal.constraint.ConstraintBase 24 | import com.amazon.ionschema.internal.util.islRequire 25 | 26 | /** 27 | * [Type] decorator that implements the $null_or:: annotation. 28 | * 29 | * Name of this class is `TypeOrNull...` instead of `NullOrType...` to be consistent with other Type-based classes. 30 | */ 31 | internal class TypeOrNullDecorator( 32 | ion: IonValue, 33 | private val type: TypeInternal, 34 | schema: Schema 35 | ) : TypeInternal by type, ConstraintBase(ion) { 36 | 37 | init { 38 | islRequire(schema.ionSchemaLanguageVersion >= v2_0) { "'\$null_or::' not supported before Ion Schema 2.0" } 39 | } 40 | 41 | override fun validate(value: IonValue, issues: Violations) { 42 | if (value.type == IonType.NULL) { 43 | return 44 | } else { 45 | type.validate(value, issues) 46 | } 47 | } 48 | 49 | override val name = type.name 50 | } 51 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/ByteLength.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonLob 19 | import com.amazon.ion.IonValue 20 | 21 | /** 22 | * Implements the byte_length constraint. 23 | * 24 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#byte_length 25 | */ 26 | internal class ByteLength( 27 | ion: IonValue 28 | ) : ConstraintBaseIntRange(IonLob::class.java, ion) { 29 | 30 | override val violationCode = "invalid_byte_length" 31 | override val violationMessage = "invalid byte length %s, expected %s" 32 | 33 | override fun getIntValue(value: IonLob) = value.byteSize() 34 | } 35 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/CodepointLength.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonText 19 | import com.amazon.ion.IonValue 20 | 21 | /** 22 | * Implements the codepoint_length constraint. 23 | * 24 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#codepoint_length 25 | */ 26 | internal class CodepointLength( 27 | ion: IonValue 28 | ) : ConstraintBaseIntRange(IonText::class.java, ion) { 29 | 30 | override val violationCode = "invalid_codepoint_length" 31 | override val violationMessage = "invalid codepoint length %s, expected %s" 32 | 33 | override fun getIntValue(value: IonText) = value.stringValue().let { it.codePointCount(0, it.length) } 34 | } 35 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/ConstraintBase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Violations 20 | import com.amazon.ionschema.internal.CommonViolations 21 | import com.amazon.ionschema.internal.Constraint 22 | 23 | /** 24 | * Base class for constraint implementations. 25 | */ 26 | internal abstract class ConstraintBase( 27 | val ion: IonValue 28 | ) : Constraint { 29 | 30 | override val name = ion.fieldName 31 | 32 | internal inline fun validateAs(value: IonValue, issues: Violations, noinline customValidation: (T) -> Unit) = 33 | validateAs(T::class.java, value, issues, customValidation) 34 | 35 | internal fun validateAs(expectedClass: Class, value: IonValue, issues: Violations, customValidation: (T) -> Unit) { 36 | when { 37 | !expectedClass.isInstance(value) -> issues.add(CommonViolations.INVALID_TYPE(ion, value)) 38 | value.isNullValue -> issues.add(CommonViolations.NULL_VALUE(ion)) 39 | else -> 40 | @Suppress("UNCHECKED_CAST") 41 | customValidation(value as T) 42 | } 43 | } 44 | 45 | override fun toString() = ion.toString() 46 | } 47 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/ConstraintBaseIntRange.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Violation 20 | import com.amazon.ionschema.Violations 21 | import com.amazon.ionschema.internal.util.RangeFactory 22 | import com.amazon.ionschema.internal.util.RangeType 23 | 24 | /** 25 | * Base class for constraints that validate an int value 26 | * against a non-negative int range. 27 | */ 28 | internal abstract class ConstraintBaseIntRange( 29 | private val expectedClass: Class, 30 | ion: IonValue 31 | ) : ConstraintBase(ion) { 32 | 33 | internal val range = RangeFactory.rangeOf(ion, RangeType.INT_NON_NEGATIVE) 34 | 35 | internal abstract val violationCode: String 36 | internal abstract val violationMessage: String 37 | 38 | override fun validate(value: IonValue, issues: Violations) { 39 | validateAs(expectedClass, value, issues) { v -> 40 | @Suppress("UNCHECKED_CAST") 41 | val intValue = getIntValue(v as T) 42 | if (!range.contains(intValue)) { 43 | issues.add(Violation(ion, violationCode, violationMessage.format(intValue, range))) 44 | } 45 | } 46 | } 47 | 48 | abstract fun getIntValue(value: T): Int 49 | } 50 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/ContainerLength.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonContainer 19 | import com.amazon.ion.IonValue 20 | 21 | /** 22 | * Implements the container_length constraint. 23 | * 24 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#container_length 25 | */ 26 | internal class ContainerLength( 27 | ion: IonValue 28 | ) : ConstraintBaseIntRange(IonContainer::class.java, ion) { 29 | 30 | override val violationCode = "invalid_container_length" 31 | override val violationMessage = "invalid container length %s, expected %s" 32 | 33 | override fun getIntValue(value: IonContainer) = value.size() 34 | } 35 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Content.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonSymbol 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.InvalidSchemaException 21 | import com.amazon.ionschema.Violations 22 | 23 | /** 24 | * Implements the content constraint. 25 | * 26 | * This implementation exists solely to verify that the constraint 27 | * definition is valid. Validation logic for this constraint is 28 | * performed by the [Fields] constraint. 29 | * 30 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#content 31 | */ 32 | internal class Content(ion: IonValue) : ConstraintBase(ion) { 33 | 34 | init { 35 | if (!( 36 | ion is IonSymbol && 37 | !ion.isNullValue && 38 | ion.stringValue() == "closed" 39 | ) 40 | ) { 41 | throw InvalidSchemaException("Invalid content constraint: $ion") 42 | } 43 | } 44 | 45 | override fun validate(value: IonValue, issues: Violations) { 46 | // no-op, validation logic for this constraint is performed by the fields constraint 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Exponent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonDecimal 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.Violation 21 | import com.amazon.ionschema.Violations 22 | import com.amazon.ionschema.internal.util.RangeFactory 23 | import com.amazon.ionschema.internal.util.RangeType 24 | 25 | /** 26 | * Implements the exponent constraint. 27 | * 28 | * @see https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#exponent 29 | */ 30 | internal class Exponent( 31 | ion: IonValue 32 | ) : ConstraintBase(ion) { 33 | 34 | internal val range = RangeFactory.rangeOf(ion, RangeType.INT) 35 | 36 | override fun validate(value: IonValue, issues: Violations) { 37 | validateAs(value, issues) { v -> 38 | @Suppress("UNCHECKED_CAST") 39 | val exponent = v.bigDecimalValue().scale() * -1 40 | if (!range.contains(exponent)) { 41 | issues.add(Violation(ion, "invalid_exponent", "invalid exponent $exponent, expected $range")) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Precision.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonDecimal 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.InvalidSchemaException 21 | 22 | /** 23 | * Implements the precision constraint. 24 | * 25 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#precision 26 | */ 27 | internal class Precision( 28 | ion: IonValue 29 | ) : ConstraintBaseIntRange(IonDecimal::class.java, ion) { 30 | 31 | init { 32 | if (range.contains(0)) { 33 | throw InvalidSchemaException("Precision must be at least 1 ($ion)") 34 | } 35 | } 36 | 37 | override val violationCode = "invalid_precision" 38 | override val violationMessage = "invalid precision %s, expected %s" 39 | 40 | override fun getIntValue(value: IonDecimal) = value.bigDecimalValue().precision() 41 | } 42 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Scale.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonDecimal 19 | import com.amazon.ion.IonValue 20 | 21 | /** 22 | * Implements the scale constraint. 23 | * 24 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#scale 25 | */ 26 | internal class Scale( 27 | ion: IonValue 28 | ) : ConstraintBaseIntRange(IonDecimal::class.java, ion) { 29 | 30 | override val violationCode = "invalid_scale" 31 | override val violationMessage = "invalid scale %s, expected %s" 32 | 33 | override fun getIntValue(value: IonDecimal) = value.bigDecimalValue().scale() 34 | } 35 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/TimestampPrecision.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonTimestamp 19 | import com.amazon.ion.IonValue 20 | import com.amazon.ionschema.Violation 21 | import com.amazon.ionschema.Violations 22 | import com.amazon.ionschema.internal.util.IonTimestampPrecision 23 | import com.amazon.ionschema.internal.util.RangeFactory 24 | import com.amazon.ionschema.internal.util.RangeType 25 | 26 | /** 27 | * Implements the timestamp_precision constraint. 28 | * 29 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#timestamp_precision 30 | */ 31 | internal class TimestampPrecision( 32 | ion: IonValue 33 | ) : ConstraintBase(ion) { 34 | 35 | private val range = RangeFactory.rangeOf(ion, RangeType.ION_TIMESTAMP_PRECISION) 36 | 37 | override fun validate(value: IonValue, issues: Violations) { 38 | validateAs(value, issues) { v -> 39 | if (!range.contains(v)) { 40 | val actualPrecision = IonTimestampPrecision.toInt(v) 41 | issues.add( 42 | Violation( 43 | ion, "invalid_timestamp_precision", 44 | "invalid timestamp precision %s, expected %s".format(actualPrecision, ion) 45 | ) 46 | ) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Type.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.constraint 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.Violations 20 | import com.amazon.ionschema.internal.DeferredReferenceManager 21 | import com.amazon.ionschema.internal.SchemaInternal 22 | import com.amazon.ionschema.internal.TypeReference 23 | 24 | /** 25 | * Implements the type constraint. 26 | * 27 | * @see https://amazon-ion.github.io/ion-schema/docs/spec.html#type 28 | */ 29 | internal class Type( 30 | ion: IonValue, 31 | schema: SchemaInternal, 32 | referenceManager: DeferredReferenceManager, 33 | ) : ConstraintBase(ion) { 34 | 35 | private val typeReference = TypeReference.create(ion, schema, referenceManager) 36 | 37 | override fun validate(value: IonValue, issues: Violations) = typeReference().validate(value, issues) 38 | } 39 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/constraint/Utf8ByteLength.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.internal.constraint 2 | import com.amazon.ion.IonText 3 | import com.amazon.ion.IonValue 4 | 5 | /** 6 | * Implements the utf8_byte_length constraint. 7 | */ 8 | internal class Utf8ByteLength( 9 | ion: IonValue 10 | ) : ConstraintBaseIntRange(IonText::class.java, ion) { 11 | 12 | override val violationCode = "invalid_utf8_byte_length" 13 | override val violationMessage = "invalid utf8 byte length %s, expected %s" 14 | 15 | override fun getIntValue(value: IonText) = byteLength(value.stringValue()) 16 | 17 | private fun byteLength(cs: CharSequence): Int { 18 | var count = 0 19 | var skipLowSurrogate = false 20 | for (c in cs) { 21 | if (skipLowSurrogate) { 22 | count += 2 23 | skipLowSurrogate = false 24 | } else { 25 | val cValue = c.toInt() 26 | if (cValue < 0x80) { 27 | count++ 28 | } else if (cValue < 0x800) { 29 | count += 2 30 | } else if (c.isHighSurrogate()) { 31 | count += 2 32 | skipLowSurrogate = true 33 | } else { 34 | count += 3 35 | } 36 | } 37 | } 38 | 39 | return count 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/IonSchema_2_0.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.internal.util 2 | 3 | import com.amazon.ionschema.IonSchemaVersion 4 | import com.amazon.ionschema.internal.ConstraintFactoryDefault 5 | 6 | /** 7 | * A collection of utilities that are specific to Ion Schema 2.0. This doesn't have to be an object, but wrapping 8 | * these functions in an object conveniently namespaces them so that they can be referred to as [IonSchema_2_0.KEYWORDS], etc. 9 | */ 10 | internal object IonSchema_2_0 { 11 | /** 12 | * Keywords that are valid to use in a schema header 13 | */ 14 | val HEADER_KEYWORDS = setOf("imports", "user_reserved_fields") 15 | 16 | /** 17 | * Keywords that could be valid to use in a type definition. Not all of these are valid in all type definitions. 18 | * For example, `name` can only occur in top-level type definitions. 19 | */ 20 | val TYPE_KEYWORDS = ConstraintFactoryDefault.getConstraintNamesForVersion(IonSchemaVersion.v2_0) + setOf("name", "occurs") 21 | 22 | /** 23 | * Keywords that are valid in an import. 24 | */ 25 | val IMPORT_KEYWORDS = setOf("id", "type", "as") 26 | 27 | /** 28 | * Keywords that are valid in an inline import. 29 | */ 30 | val INLINE_IMPORT_KEYWORDS = setOf("id", "type") 31 | 32 | /** 33 | * Keywords that are valid as annotations on top-level types. 34 | */ 35 | val TOP_LEVEL_ANNOTATION_KEYWORDS = setOf("schema_header", "schema_footer", "type") 36 | 37 | /** 38 | * All Ion Schema keywords. 39 | */ 40 | val KEYWORDS = TOP_LEVEL_ANNOTATION_KEYWORDS + HEADER_KEYWORDS + TYPE_KEYWORDS + IMPORT_KEYWORDS 41 | 42 | /** 43 | * Regex to match symbols that are reserved in the Ion Schema specification. 44 | * See https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#reserved-symbols 45 | */ 46 | val RESERVED_WORDS_REGEX = Regex("(\\\$ion_schema(_.*)?|[a-z][a-z\\d]*(_[a-z\\d]+)*)") 47 | } 48 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/RangeBoundaryType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.IonValue 19 | import com.amazon.ionschema.InvalidSchemaException 20 | 21 | /** 22 | * Enum indicating whether an upper or lower range boundary is 23 | * inclusive or exclusive. 24 | */ 25 | internal enum class RangeBoundaryType { 26 | EXCLUSIVE, 27 | INCLUSIVE; 28 | 29 | companion object { 30 | fun forIon(ion: IonValue): RangeBoundaryType { 31 | val isExclusive = ion.hasTypeAnnotation("exclusive") 32 | return when { 33 | isRangeMin(ion) || isRangeMax(ion) -> { 34 | if (isExclusive) { 35 | throw InvalidSchemaException("Invalid range bound '$ion'") 36 | } 37 | INCLUSIVE 38 | } 39 | isExclusive -> EXCLUSIVE 40 | else -> INCLUSIVE 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/RangeIntNonNegative.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.IonList 19 | import com.amazon.ionschema.InvalidSchemaException 20 | 21 | /** 22 | * Implementation of Range restricted to non-negative integers. 23 | * Mostly delegates to RangeInt. 24 | */ 25 | internal class RangeIntNonNegative( 26 | private val ion: IonList, 27 | private val delegate: RangeInt = RangeInt(ion) 28 | ) : Range by delegate { 29 | 30 | init { 31 | if (!(compareValues(toInt(ion[0]), 0) >= 0 || isRangeMin(ion[0]))) { 32 | throw InvalidSchemaException("Invalid lower bound in positive int $ion") 33 | } 34 | 35 | if (!(compareValues(toInt(ion[1]), 0) >= 0 || isRangeMax(ion[1]))) { 36 | throw InvalidSchemaException("Invalid upper bound in positive int $ion") 37 | } 38 | } 39 | 40 | internal fun isAtMax(value: Int) = delegate.isAtMax(value) 41 | 42 | override fun toString() = delegate.toString() 43 | } 44 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/RangeIonNumber.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.IonDecimal 19 | import com.amazon.ion.IonFloat 20 | import com.amazon.ion.IonInt 21 | import com.amazon.ion.IonList 22 | import com.amazon.ion.IonValue 23 | import java.math.BigDecimal 24 | 25 | /** 26 | * Implementation of Range restricted to IonDecimal, IonFloat, 27 | * and IonInt (numeric) values. Mostly delegates to RangeBigDecimal. 28 | */ 29 | internal class RangeIonNumber private constructor ( 30 | private val delegate: RangeBigDecimal 31 | ) : Range { 32 | 33 | constructor (ion: IonList) : this(RangeBigDecimal(ion)) 34 | 35 | companion object { 36 | private fun toBigDecimal(ion: IonValue) = 37 | if (ion.isNullValue) { 38 | null 39 | } else { 40 | when (ion) { 41 | is IonDecimal -> ion.bigDecimalValue() 42 | is IonFloat -> ion.bigDecimalValue() 43 | is IonInt -> BigDecimal(ion.bigIntegerValue()) 44 | else -> null 45 | } 46 | } 47 | } 48 | 49 | override operator fun contains(value: IonValue): Boolean { 50 | val bdValue = toBigDecimal(value) 51 | return if (bdValue != null) { 52 | delegate.contains(bdValue) 53 | } else { 54 | false 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/RangeIonTimestamp.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.IonList 19 | import com.amazon.ion.IonTimestamp 20 | 21 | /** 22 | * Implementation of Range which mostly delegates to RangeBigDecimal. 23 | */ 24 | internal class RangeIonTimestamp private constructor ( 25 | private val delegate: RangeBigDecimal 26 | ) : Range { 27 | 28 | constructor (ion: IonList) : this(toRangeBigDecimal(ion)) 29 | 30 | companion object { 31 | private fun toRangeBigDecimal(ion: IonList): RangeBigDecimal { 32 | checkRange(ion) 33 | 34 | // convert to a decimal range 35 | val newRange = ion.system.newEmptyList() 36 | newRange.addTypeAnnotation("range") 37 | ion.forEach { ionValue -> 38 | val newValue = if (ionValue is IonTimestamp) { 39 | ion.system.newDecimal(ionValue.decimalMillis) 40 | } else { 41 | ionValue.clone() 42 | } 43 | ionValue.typeAnnotations.forEach { newValue.addTypeAnnotation(it) } 44 | newRange.add(newValue) 45 | } 46 | 47 | return RangeBigDecimal(newRange) 48 | } 49 | } 50 | 51 | override operator fun contains(value: IonTimestamp): Boolean { 52 | return delegate.contains(value.decimalMillis) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/internal/util/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | /** 19 | * String extension functions 20 | */ 21 | internal fun String.truncate(limit: Int, truncated: CharSequence = "..."): String { 22 | if (length < limit) { 23 | return this 24 | } 25 | return substring(0, limit) + truncated 26 | } 27 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/ConsistentDecimal.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.IonNumber 4 | import java.math.BigDecimal 5 | 6 | /** 7 | * Wrapper for BigDecimal where [equals], [hashCode], and [compareTo] are all consistent. 8 | */ 9 | class ConsistentDecimal(val bigDecimalValue: BigDecimal) : Comparable { 10 | 11 | private val normalized: BigDecimal = bigDecimalValue.stripTrailingZeros() 12 | 13 | override fun compareTo(other: ConsistentDecimal) = normalized.compareTo(other.normalized) 14 | 15 | override fun equals(other: Any?) = other is ConsistentDecimal && normalized == other.normalized 16 | 17 | override fun hashCode() = normalized.hashCode() 18 | 19 | override fun toString(): String { 20 | return "ConsistentDecimal(${normalized.unscaledValue()}E${normalized.scale() * -1})" 21 | } 22 | 23 | companion object { 24 | /** 25 | * Constructs a new [ConsistentDecimal] with the value from the given [IonNumber]. 26 | */ 27 | @JvmStatic 28 | fun fromIonNumber(ionNumber: IonNumber) = ConsistentDecimal(ionNumber.bigDecimalValue()) 29 | 30 | /** 31 | * Translates a long value into a [ConsistentDecimal] with a scale of zero. 32 | */ 33 | @JvmStatic 34 | fun valueOf(long: Long) = ConsistentDecimal(BigDecimal.valueOf(long)) 35 | 36 | /** 37 | * Translates a [Double] into a [ConsistentDecimal], using the [Double]'s canonical string representation 38 | * provided by the [Double.toString] method. 39 | */ 40 | @JvmStatic 41 | fun valueOf(double: Double) = ConsistentDecimal(BigDecimal.valueOf(double)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/ConsistentTimestamp.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.IonTimestamp 4 | import com.amazon.ion.Timestamp 5 | import java.math.BigDecimal 6 | 7 | /** 8 | * Wrapper for Timestamp where [equals], [hashCode], and [compareTo] are all consistent. 9 | */ 10 | class ConsistentTimestamp(val timestampValue: Timestamp) : Comparable { 11 | 12 | private val normalizedMillis: BigDecimal = timestampValue.decimalMillis.stripTrailingZeros() 13 | 14 | override fun compareTo(other: ConsistentTimestamp) = normalizedMillis.compareTo(other.normalizedMillis) 15 | 16 | override fun equals(other: Any?) = other is ConsistentTimestamp && normalizedMillis == other.normalizedMillis 17 | 18 | override fun hashCode() = normalizedMillis.hashCode() 19 | 20 | override fun toString(): String { 21 | return "ConsistentTimestamp($timestampValue)" 22 | } 23 | 24 | companion object { 25 | /** 26 | * Constructs a new [ConsistentTimestamp] with the value from the given [IonTimestamp]. 27 | */ 28 | @JvmStatic 29 | fun fromIonTimestamp(ionTimestamp: IonTimestamp) = ConsistentTimestamp(ionTimestamp.timestampValue()) 30 | 31 | /** 32 | * Returns a new [ConsistentTimestamp] that represents the point in time, precision and local offset defined in 33 | * Ion format by the [CharSequence]. 34 | * 35 | * @see Timestamp.valueOf 36 | */ 37 | @JvmStatic 38 | fun valueOf(ionFormattedTimestamp: CharSequence) = ConsistentTimestamp(Timestamp.valueOf(ionFormattedTimestamp)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/ExperimentalIonSchemaModel.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS) 4 | @RequiresOptIn 5 | annotation class ExperimentalIonSchemaModel 6 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/HeaderImport.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * Represents an import in the schema header. 5 | */ 6 | @ExperimentalIonSchemaModel 7 | sealed class HeaderImport { 8 | /** 9 | * An import that imports all types from a schema 10 | */ 11 | data class Wildcard(val id: String) : HeaderImport() 12 | /** 13 | * An import of a specific type from a schema, with an optional alias. 14 | */ 15 | data class Type(val id: String, val targetType: String, val asType: String? = null) : HeaderImport() 16 | } 17 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/Ieee754InterchangeFormat.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * Represents the IEEE-754 interchange formats supported by the `ieee754_float` constraint. 5 | * See [`ieee754_float`](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#ieee754_float) in the ISL 2.0 specification. 6 | * See also [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) on Wikipedia for more information about the different formats. 7 | */ 8 | enum class Ieee754InterchangeFormat { 9 | Binary16, 10 | Binary32, 11 | Binary64; 12 | 13 | /** 14 | * The symbol text for this value as defined in the ISL specification. 15 | */ 16 | val symbolText = name.toLowerCase() 17 | 18 | companion object { 19 | /** 20 | * Returns the [Ieee754InterchangeFormat] corresponding to `text`, or `null` if `text` does not correspond to any value. 21 | */ 22 | @JvmStatic 23 | fun fromSymbolTextOrNull(text: String): Ieee754InterchangeFormat? { 24 | return values().firstOrNull { it.symbolText == text } 25 | } 26 | 27 | /** 28 | * Returns the [Ieee754InterchangeFormat] corresponding to `text`. 29 | * @throws IllegalArgumentException if `text` does not correspond to any value. 30 | */ 31 | @JvmStatic 32 | fun fromSymbolText(text: String): Ieee754InterchangeFormat { 33 | return fromSymbolTextOrNull(text) 34 | ?: throw IllegalArgumentException("'$text' is not a supported ieee754 format value") 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/NamedTypeDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * Represents a top-level, named type definition. 5 | */ 6 | @ExperimentalIonSchemaModel 7 | data class NamedTypeDefinition(val typeName: String, val typeDefinition: TypeDefinition) : SchemaDocument.Content 8 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/SchemaDocument.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.IonSchemaVersion 5 | import com.amazon.ionschema.internal.util.islRequire 6 | 7 | /** 8 | * Represents an Ion Schema document. 9 | */ 10 | @ExperimentalIonSchemaModel 11 | data class SchemaDocument( 12 | val id: String?, 13 | val ionSchemaVersion: IonSchemaVersion, 14 | val items: List 15 | ) { 16 | val header: SchemaHeader? = items.filterIsInstance().singleOrNull() 17 | val footer: SchemaFooter? = items.filterIsInstance().singleOrNull() 18 | val declaredTypes: Map = let { 19 | val typeList = items.filterIsInstance() 20 | val typeMap = typeList.associateBy { it.typeName } 21 | islRequire(typeMap.size == typeList.size) { 22 | "Conflicting type names in schema" 23 | } 24 | typeMap 25 | } 26 | 27 | /** 28 | * Represents a top-level value in a schema document. 29 | * Implemented by [NamedTypeDefinition], [SchemaHeader], [SchemaFooter], and [OpenContent]. 30 | * This interface is not intended to be implemented by users of the library. 31 | */ 32 | interface Content 33 | 34 | /** 35 | * Represents top-level open content in a SchemaDocument. 36 | */ 37 | data class OpenContent(val value: IonValue) : Content 38 | } 39 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/SchemaFooter.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ionschema.util.emptyBag 4 | 5 | /** 6 | * Represents the schema footer for all versions of Ion Schema. 7 | */ 8 | @ExperimentalIonSchemaModel 9 | data class SchemaFooter(val openContent: OpenContentFields = emptyBag()) : SchemaDocument.Content 10 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/SchemaHeader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ionschema.util.emptyBag 4 | 5 | /** 6 | * Represents the schema header for all versions of Ion Schema. 7 | */ 8 | @ExperimentalIonSchemaModel 9 | data class SchemaHeader( 10 | val imports: Set = emptySet(), 11 | val userReservedFields: UserReservedFields = UserReservedFields(), 12 | val openContent: OpenContentFields = emptyBag() 13 | ) : SchemaDocument.Content 14 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/TimestampPrecisionRange.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.model 5 | 6 | /** 7 | * A [ContinuousRange] of [TimestampPrecisionValue]. 8 | * `TimestampPrecision` is a discrete measurement (i.e. there is no fractional number of digits of precision). 9 | * However, because Ion Schema models timestamp precision as an enum, there are possible precisions that exist between 10 | * the available enum values. For example, `timestamp_precision: range::[exclusive::second, exclusive::millisecond]` 11 | * allows 1 or 2 digits of precision for the fractional seconds of a timestamp. 12 | */ 13 | class TimestampPrecisionRange(start: Limit, end: Limit) : ContinuousRange(start, end) { 14 | private constructor(value: Limit.Closed) : this(value, value) 15 | constructor(value: TimestampPrecisionValue) : this(Limit.Closed(value)) 16 | } 17 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/TypeArgument.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * A TypeArgument represents (defines or references) a Type, allowing the Type to be used as an argument for a constraint. 5 | * 6 | * **_Do not implement this interface_**—It will become a sealed interface once `ion-schema-kotlin` is updated to use 7 | * language version 1.5. 8 | */ 9 | @ExperimentalIonSchemaModel 10 | sealed class TypeArgument { 11 | abstract val nullability: Nullability 12 | 13 | /** 14 | * Nullability modifiers for [TypeArgument]. 15 | */ 16 | enum class Nullability { 17 | /** 18 | * No special treatment of null values. 19 | */ 20 | None, 21 | /** 22 | * Ion Schema 1.0 nullability. See https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#core-types 23 | */ 24 | Nullable, 25 | /** 26 | * Ion Schema 2.0+ `$null_or` annotation. See https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#nullable-type-arguments 27 | */ 28 | OrNull, 29 | } 30 | 31 | /** 32 | * [TypeArgument] that is an anonymous type, defined inline. 33 | */ 34 | data class InlineType(val typeDefinition: TypeDefinition, override val nullability: Nullability = Nullability.None) : TypeArgument() 35 | 36 | /** 37 | * A [TypeArgument] that references another type by [typeName] only. 38 | * This can refer to any types that are defined in the same schema, imported via the schema header, or any built-in types. 39 | */ 40 | data class Reference(val typeName: String, override val nullability: Nullability = Nullability.None) : TypeArgument() 41 | 42 | /** 43 | * A [TypeArgument] that references a type from a different schema by [schemaId] and [typeName]. 44 | */ 45 | data class Import(val schemaId: String, val typeName: String, override val nullability: Nullability = Nullability.None) : TypeArgument() 46 | } 47 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/TypeDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ionschema.util.emptyBag 4 | 5 | /** 6 | * Represents the common fields of all type definitions; used to compose [NamedTypeDefinition] and [TypeArgument.InlineType]. 7 | * 8 | * Constraints are modeled as a [Set] because if there are two identical constraints, they are redundant. 9 | */ 10 | @ExperimentalIonSchemaModel 11 | data class TypeDefinition(val constraints: Set, val openContent: OpenContentFields = emptyBag()) { 12 | override fun equals(other: Any?): Boolean { 13 | return other is TypeDefinition && 14 | this.constraints == other.constraints && 15 | this.openContent == other.openContent 16 | } 17 | 18 | override fun hashCode(): Int { 19 | return constraints.hashCode() * 31 + openContent.hashCode() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/UserReservedFields.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.model 5 | 6 | /** 7 | * The collection of field names that are reserved by the user as open content fields. 8 | * See relevant section in [ISL 2.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#open-content). 9 | */ 10 | data class UserReservedFields( 11 | val type: Set = emptySet(), 12 | val header: Set = emptySet(), 13 | val footer: Set = emptySet(), 14 | ) { 15 | companion object { 16 | @JvmStatic 17 | val EMPTY = UserReservedFields() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/ValidValue.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.IonValue 4 | 5 | /** 6 | * Represents an argument to the `valid_values` constraint. 7 | * 8 | * Consumers of `ion-schema-kotlin` MAY NOT implement this interface. 9 | * 10 | * @see [Constraint.ValidValues] 11 | */ 12 | // TODO: Make "sealed" when updating to kotlin 1.5 or higher. 13 | interface ValidValue { 14 | /** 15 | * A single Ion value. May not be annotated. 16 | * Ignoring annotations, this value is compared using Ion equivalence with data that is being validated. 17 | * @see [Constraint.ValidValues] 18 | */ 19 | data class Value(val value: IonValue) : ValidValue { 20 | init { require(value.typeAnnotations.isEmpty()) { "valid value may not be annotated" } } 21 | } 22 | 23 | /** 24 | * A range over Ion numbers. 25 | */ 26 | class NumberRange(start: Limit, end: Limit) : ValidValue, ContinuousRange(start, end) { 27 | private constructor(value: Limit.Closed) : this(value, value) 28 | constructor(value: ConsistentDecimal) : this(Limit.Closed(value)) 29 | } 30 | 31 | /** 32 | * A range of timestamp values. 33 | */ 34 | class TimestampRange(start: Limit, end: Limit) : ValidValue, ContinuousRange(start, end) { 35 | private constructor(value: Limit.Closed) : this(value, value) 36 | constructor(value: ConsistentTimestamp) : this(Limit.Closed(value)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/VariablyOccurringTypeArgument.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * Represents a Type as a constraint argument with additional information about the number of times this type can occur. 5 | * See [Type Definitions](https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#type-definitions) in ISL 1.0 spec. 6 | * See [Variably Occurring Type Arguments](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#variably-occurring-type-arguments) in ISL 2.0 spec. 7 | */ 8 | @ExperimentalIonSchemaModel 9 | data class VariablyOccurringTypeArgument(val occurs: DiscreteIntRange, val typeArg: TypeArgument) { 10 | companion object { 11 | @JvmStatic 12 | val OCCURS_OPTIONAL = DiscreteIntRange(0, 1) 13 | @JvmStatic 14 | val OCCURS_REQUIRED = DiscreteIntRange(1, 1) 15 | 16 | @JvmStatic 17 | fun optional(arg: TypeArgument) = VariablyOccurringTypeArgument(OCCURS_OPTIONAL, arg) 18 | 19 | @JvmStatic 20 | fun required(arg: TypeArgument) = VariablyOccurringTypeArgument(OCCURS_REQUIRED, arg) 21 | } 22 | } 23 | 24 | @ExperimentalIonSchemaModel 25 | fun TypeArgument.optional() = VariablyOccurringTypeArgument.optional(this) 26 | 27 | @ExperimentalIonSchemaModel 28 | fun TypeArgument.required() = VariablyOccurringTypeArgument.required(this) 29 | 30 | @ExperimentalIonSchemaModel 31 | fun TypeArgument.occurs(n: Int) = VariablyOccurringTypeArgument(DiscreteIntRange(n), this) 32 | 33 | @ExperimentalIonSchemaModel 34 | fun TypeArgument.occurs(min: Int?, max: Int?) = VariablyOccurringTypeArgument(DiscreteIntRange(min, max), this) 35 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/aliases.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.util.Bag 5 | 6 | /** 7 | * Convenience alias for a collections of open content fields in schema headers, footers, and type definitions. 8 | * It is modeled as a [Bag] rather than a [List] because ordering is not guaranteed for structs, and we want to be able to 9 | * compare two collections of open content fields for equality. It is modeled as a [Bag] rather than a [Set] because we 10 | * don't know if a repeated field name and value has any meaning to the author of the schema, so we can't safely 11 | * deduplicate them as we can with constraints. 12 | */ 13 | typealias OpenContentFields = Bag> 14 | 15 | /** 16 | * Convenience alias for a set of [TypeArgument]. 17 | */ 18 | @ExperimentalIonSchemaModel 19 | typealias TypeArguments = Set 20 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/model/util.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | /** 4 | * Convenience function to easily negate a constraint. 5 | */ 6 | @ExperimentalIonSchemaModel 7 | internal fun not(c: Constraint): Constraint { 8 | return Constraint.Not(TypeArgument.InlineType(TypeDefinition(setOf(c)))) 9 | } 10 | 11 | /** 12 | * Convenience function for creating an inline type argument from a list of constraints. 13 | */ 14 | @ExperimentalIonSchemaModel 15 | internal fun inlineType(constraints: Set): TypeArgument.InlineType { 16 | return TypeArgument.InlineType(TypeDefinition(constraints)) 17 | } 18 | 19 | /** 20 | * Returns a set containing the results of applying the given [transform] function 21 | * to each element in the original collection. 22 | */ 23 | inline fun Iterable.mapToSet(transform: (T) -> R): Set { 24 | return mapTo(HashSet(), transform) 25 | } 26 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/module.md: -------------------------------------------------------------------------------- 1 | # Module ion-schema-kotlin 2 | Reference implementation of the [Ion Schema Specification](https://amazon-ion.github.io/ion-schema/docs/spec.html). 3 | 4 | # Package com.amazon.ionschema 5 | API for loading schemas and types, validating Ion data against those types, and examining validation details. 6 | 7 | # Package com.amazon.ionschema.util 8 | Utility code in support of the com.amazon.ionschema package. 9 | 10 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/FooterReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal 2 | 3 | import com.amazon.ion.IonStruct 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.internal.util.islRequire 6 | import com.amazon.ionschema.internal.util.islRequireExactAnnotations 7 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.SchemaFooter 10 | import com.amazon.ionschema.util.toBag 11 | 12 | @ExperimentalIonSchemaModel 13 | internal class FooterReader(private val isValidOpenContentField: ReaderContext.(String) -> Boolean) { 14 | 15 | fun readFooter(context: ReaderContext, footerValue: IonValue): SchemaFooter { 16 | islRequireIonTypeNotNull(footerValue) { "schema_footer must be a non-null struct; was: $footerValue" } 17 | islRequireExactAnnotations(footerValue, "schema_footer") { "schema_footer may not have extra annotations" } 18 | 19 | val unexpectedFieldNames = footerValue.map { it.fieldName }.filterNot { context.isValidOpenContentField(it) } 20 | 21 | islRequire(unexpectedFieldNames.isEmpty()) { "Found illegal field names $unexpectedFieldNames in schema footer: $footerValue" } 22 | 23 | val openContent = footerValue.map { it.fieldName to it }.toBag() 24 | return SchemaFooter(openContent) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/ReadError.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.reader.IonTreePath.Companion.treePath 5 | 6 | /** 7 | * Represents an error that occurred while attempting to read Ion as an Ion Schema value. 8 | * The [value] is the IonValue where the error was detected, and [message] is a description of the problem. 9 | */ 10 | data class ReadError(val value: IonValue, val message: String) { 11 | internal val path by lazy { value.treePath() } 12 | } 13 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/TypeReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal 2 | 3 | import com.amazon.ion.IonList 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.internal.util.islRequire 6 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 7 | import com.amazon.ionschema.model.DiscreteIntRange 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.NamedTypeDefinition 10 | import com.amazon.ionschema.model.TypeArgument 11 | import com.amazon.ionschema.model.TypeArguments 12 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument 13 | 14 | @ExperimentalIonSchemaModel 15 | internal interface TypeReader { 16 | 17 | /** 18 | * Reads a [NamedTypeDefinition]. 19 | */ 20 | fun readNamedTypeDefinition(context: ReaderContext, ion: IonValue): NamedTypeDefinition 21 | 22 | /** 23 | * Reads a [TypeArgument]. 24 | */ 25 | fun readTypeArg(context: ReaderContext, ion: IonValue, checkAnnotations: Boolean = true): TypeArgument 26 | 27 | /** 28 | * Reads a [VariablyOccurringTypeArgument]. 29 | */ 30 | fun readVariablyOccurringTypeArg(context: ReaderContext, ion: IonValue, defaultOccurs: DiscreteIntRange): VariablyOccurringTypeArgument 31 | 32 | /** 33 | * Reads a [TypeArguments]. 34 | */ 35 | fun readTypeArgumentList(context: ReaderContext, ion: IonValue): TypeArguments { 36 | val constraintName = ion.fieldName!! 37 | islRequireIonTypeNotNull(ion) { "Illegal argument for '$constraintName' constraint; must be non-null Ion list: $ion" } 38 | islRequire(ion.typeAnnotations.isEmpty()) { "Illegal argument for '$constraintName' constraint; must not have annotations; was: $ion" } 39 | return ion.readAllCatching(context) { readTypeArg(context, it) }.toSet() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/ConstraintReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | 8 | /** 9 | * Allows us to compose TypeReaders out of different combinations of constraint readers to enable code reuse across 10 | * multiple Ion Schema versions. 11 | */ 12 | @ExperimentalIonSchemaModel 13 | internal interface ConstraintReader { 14 | 15 | /** 16 | * Returns true if this constraint reader can read the given constraint. 17 | */ 18 | fun canRead(fieldName: String): Boolean 19 | 20 | /** 21 | * Returns a [Constraint] instance for the given field. 22 | * Should only be called after checking whether the constraint is supported by calling [canRead]. 23 | * Must throw [IllegalStateException] if called for an unsupported field name. 24 | */ 25 | fun readConstraint(context: ReaderContext, field: IonValue): Constraint 26 | } 27 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/ContainsReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonList 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 6 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.mapToSet 10 | import com.amazon.ionschema.reader.internal.ReaderContext 11 | import com.amazon.ionschema.reader.internal.invalidConstraint 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class ContainsReader : ConstraintReader { 15 | 16 | override fun canRead(fieldName: String): Boolean = fieldName == "contains" 17 | 18 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 19 | check(canRead(field.fieldName)) 20 | 21 | islRequireIonTypeNotNull(field) { invalidConstraint(field, "must be a non-null list") } 22 | islRequireNoIllegalAnnotations(field) { invalidConstraint(field, "must not have annotations") } 23 | return Constraint.Contains(field.mapToSet { it.clone() }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/ElementV2Reader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 5 | import com.amazon.ionschema.model.Constraint 6 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 7 | import com.amazon.ionschema.reader.internal.ReaderContext 8 | import com.amazon.ionschema.reader.internal.TypeReader 9 | import com.amazon.ionschema.reader.internal.invalidConstraint 10 | 11 | /** 12 | * Reads the `element` constraint for ISL 2.0 and higher 13 | */ 14 | @ExperimentalIonSchemaModel 15 | internal class ElementV2Reader(private val typeReader: TypeReader) : ConstraintReader { 16 | override fun canRead(fieldName: String): Boolean = fieldName == "element" 17 | 18 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 19 | check(canRead(field.fieldName)) 20 | 21 | islRequireNoIllegalAnnotations(field, "distinct", "\$null_or") { 22 | invalidConstraint( 23 | field, 24 | "type argument may only be annotated with 'distinct' or '\$null_or'" 25 | ) 26 | } 27 | 28 | val typeArg = typeReader.readTypeArg(context, field, checkAnnotations = false) 29 | return Constraint.Element(typeArg, distinct = field.hasTypeAnnotation("distinct")) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/ExponentReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | import com.amazon.ionschema.reader.internal.toDiscreteIntRange 8 | 9 | @ExperimentalIonSchemaModel 10 | internal class ExponentReader : ConstraintReader { 11 | override fun canRead(fieldName: String): Boolean = fieldName == "exponent" 12 | 13 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 14 | check(canRead(field.fieldName)) 15 | return Constraint.Exponent(field.toDiscreteIntRange()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/FieldNamesReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 5 | import com.amazon.ionschema.model.Constraint 6 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 7 | import com.amazon.ionschema.reader.internal.ReaderContext 8 | import com.amazon.ionschema.reader.internal.TypeReader 9 | import com.amazon.ionschema.reader.internal.invalidConstraint 10 | 11 | /** 12 | * Reads the `field_names` constraint for ISL 2.0 and higher 13 | */ 14 | @ExperimentalIonSchemaModel 15 | internal class FieldNamesReader(private val typeReader: TypeReader) : ConstraintReader { 16 | override fun canRead(fieldName: String): Boolean = fieldName == "field_names" 17 | 18 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 19 | check(canRead(field.fieldName)) 20 | 21 | islRequireNoIllegalAnnotations(field, "distinct", "\$null_or") { 22 | invalidConstraint( 23 | field, 24 | "type argument may only be annotated with 'distinct' or '\$null_or'" 25 | ) 26 | } 27 | val typeArg = typeReader.readTypeArg(context, field, checkAnnotations = false) 28 | 29 | return Constraint.FieldNames(typeArg, distinct = field.hasTypeAnnotation("distinct")) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/FieldsV2Reader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonStruct 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 6 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument 10 | import com.amazon.ionschema.reader.internal.ReaderContext 11 | import com.amazon.ionschema.reader.internal.TypeReader 12 | import com.amazon.ionschema.reader.internal.invalidConstraint 13 | 14 | /** 15 | * Reads the `fields` constraint for ISL 2.0 and higher 16 | */ 17 | @ExperimentalIonSchemaModel 18 | internal class FieldsV2Reader(private val typeReader: TypeReader) : ConstraintReader { 19 | override fun canRead(fieldName: String): Boolean = fieldName == "fields" 20 | 21 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 22 | check(canRead(field.fieldName)) 23 | 24 | islRequireIonTypeNotNull(field) { invalidConstraint(field, "must be a non-null struct") } 25 | islRequireNoIllegalAnnotations(field, "closed") { 26 | invalidConstraint(field, "argument may only be annotated with 'closed'") 27 | } 28 | require(!field.isEmpty) 29 | require(field.map { it.fieldName }.distinct().size == field.size()) 30 | return Constraint.Fields( 31 | fields = field.associate { 32 | it.fieldName to typeReader.readVariablyOccurringTypeArg( 33 | context, 34 | it, 35 | VariablyOccurringTypeArgument.OCCURS_OPTIONAL 36 | ) 37 | }, 38 | closed = field.hasTypeAnnotation("closed") 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/Ieee754FloatReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonSymbol 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.InvalidSchemaException 6 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.Ieee754InterchangeFormat 10 | import com.amazon.ionschema.reader.internal.ReaderContext 11 | import com.amazon.ionschema.reader.internal.invalidConstraint 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class Ieee754FloatReader : ConstraintReader { 15 | override fun canRead(fieldName: String): Boolean = fieldName == "ieee754_float" 16 | 17 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 18 | check(canRead(field.fieldName)) 19 | 20 | islRequireNoIllegalAnnotations(field) { invalidConstraint(field, "must not have annotations") } 21 | val format = when ((field as? IonSymbol)?.stringValue()) { 22 | "binary16" -> Ieee754InterchangeFormat.Binary16 23 | "binary32" -> Ieee754InterchangeFormat.Binary32 24 | "binary64" -> Ieee754InterchangeFormat.Binary64 25 | else -> throw InvalidSchemaException(invalidConstraint(field, "must be one of 'binary16', 'binary32', 'binary64'")) 26 | } 27 | 28 | return Constraint.Ieee754Float(format) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/LengthConstraintsReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | import com.amazon.ionschema.reader.internal.toDiscreteIntRange 8 | 9 | @ExperimentalIonSchemaModel 10 | internal class LengthConstraintsReader : ConstraintReader { 11 | companion object { 12 | private val CONSTRAINT_NAMES = setOf("byte_length", "codepoint_length", "container_length", "utf8_byte_length") 13 | } 14 | 15 | override fun canRead(fieldName: String): Boolean = fieldName in CONSTRAINT_NAMES 16 | 17 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 18 | check(canRead(field.fieldName)) 19 | 20 | val range = field.toDiscreteIntRange() 21 | 22 | return when (field.fieldName) { 23 | "byte_length" -> Constraint.ByteLength(range) 24 | "codepoint_length" -> Constraint.CodepointLength(range) 25 | "container_length" -> Constraint.ContainerLength(range) 26 | "utf8_byte_length" -> Constraint.Utf8ByteLength(range) 27 | else -> TODO("Unreachable!") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/LogicConstraintsReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | import com.amazon.ionschema.reader.internal.TypeReader 8 | 9 | @ExperimentalIonSchemaModel 10 | internal class LogicConstraintsReader(private val typeReader: TypeReader) : ConstraintReader { 11 | companion object { 12 | private val CONSTRAINT_NAMES = setOf("all_of", "any_of", "not", "one_of", "type") 13 | } 14 | 15 | override fun canRead(fieldName: String): Boolean = fieldName in CONSTRAINT_NAMES 16 | 17 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 18 | check(canRead(field.fieldName)) 19 | 20 | return when (field.fieldName) { 21 | "all_of" -> Constraint.AllOf(typeReader.readTypeArgumentList(context, field)) 22 | "any_of" -> Constraint.AnyOf(typeReader.readTypeArgumentList(context, field)) 23 | "not" -> Constraint.Not(typeReader.readTypeArg(context, field,)) 24 | "one_of" -> Constraint.OneOf(typeReader.readTypeArgumentList(context, field)) 25 | "type" -> Constraint.Type(typeReader.readTypeArg(context, field)) 26 | else -> TODO("Unreachable") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/OrderedElementsReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonList 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 6 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument.Companion.OCCURS_REQUIRED 10 | import com.amazon.ionschema.reader.internal.ReaderContext 11 | import com.amazon.ionschema.reader.internal.TypeReader 12 | import com.amazon.ionschema.reader.internal.invalidConstraint 13 | 14 | @ExperimentalIonSchemaModel 15 | internal class OrderedElementsReader(private val typeReader: TypeReader) : ConstraintReader { 16 | 17 | override fun canRead(fieldName: String): Boolean = fieldName == "ordered_elements" 18 | 19 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 20 | check(canRead(field.fieldName)) 21 | 22 | islRequireIonTypeNotNull(field) { invalidConstraint(field, "must be a non-null list") } 23 | islRequireNoIllegalAnnotations(field) { invalidConstraint(field, "must not have annotations") } 24 | return Constraint.OrderedElements( 25 | field.map { typeReader.readVariablyOccurringTypeArg(context, it, defaultOccurs = OCCURS_REQUIRED) } 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/PrecisionReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | import com.amazon.ionschema.reader.internal.toDiscreteIntRange 8 | 9 | @ExperimentalIonSchemaModel 10 | internal class PrecisionReader : ConstraintReader { 11 | override fun canRead(fieldName: String): Boolean = fieldName == "precision" 12 | 13 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 14 | check(canRead(field.fieldName)) 15 | return Constraint.Precision(field.toDiscreteIntRange()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/RegexReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonString 4 | import com.amazon.ion.IonValue 5 | import com.amazon.ionschema.IonSchemaVersion 6 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 7 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 8 | import com.amazon.ionschema.model.Constraint 9 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 10 | import com.amazon.ionschema.reader.internal.ReaderContext 11 | import com.amazon.ionschema.reader.internal.invalidConstraint 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class RegexReader(private val ionSchemaVersion: IonSchemaVersion) : ConstraintReader { 15 | override fun canRead(fieldName: String): Boolean = fieldName == "regex" 16 | 17 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 18 | check(canRead(field.fieldName)) 19 | 20 | islRequireIonTypeNotNull(field) { invalidConstraint(field, "must be a non-null string") } 21 | islRequireNoIllegalAnnotations(field, "i", "m") { invalidConstraint(field, "regex pattern may only be annotated with 'i' and 'm'") } 22 | 23 | return Constraint.Regex( 24 | field.stringValue(), 25 | multiline = field.hasTypeAnnotation("m"), 26 | caseInsensitive = field.hasTypeAnnotation("i"), 27 | ionSchemaVersion = ionSchemaVersion 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/TimestampOffsetReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonList 4 | import com.amazon.ion.IonString 5 | import com.amazon.ion.IonValue 6 | import com.amazon.ionschema.internal.util.islRequire 7 | import com.amazon.ionschema.internal.util.islRequireElementType 8 | import com.amazon.ionschema.internal.util.islRequireIonTypeNotNull 9 | import com.amazon.ionschema.internal.util.islRequireNoIllegalAnnotations 10 | import com.amazon.ionschema.internal.util.islRequireNotEmpty 11 | import com.amazon.ionschema.model.Constraint 12 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 13 | import com.amazon.ionschema.model.TimestampOffsetValue 14 | import com.amazon.ionschema.model.mapToSet 15 | import com.amazon.ionschema.reader.internal.ReaderContext 16 | import com.amazon.ionschema.reader.internal.invalidConstraint 17 | 18 | @ExperimentalIonSchemaModel 19 | internal class TimestampOffsetReader : ConstraintReader { 20 | 21 | override fun canRead(fieldName: String): Boolean = fieldName == "timestamp_offset" 22 | 23 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 24 | check(canRead(field.fieldName)) 25 | 26 | islRequireIonTypeNotNull(field) { invalidConstraint(field, "must be a non-null, non-empty list") } 27 | islRequire(field.isNotEmpty()) { invalidConstraint(field, "must be a non-null, non-empty list") } 28 | islRequireNoIllegalAnnotations(field) { invalidConstraint(field, "must not have annotations") } 29 | return Constraint.TimestampOffset( 30 | field.islRequireElementType("timestamp offset list") 31 | .islRequireNotEmpty("timestamp offset list") 32 | .mapToSet { TimestampOffsetValue.parse(it.stringValue()) } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/reader/internal/constraints/TimestampPrecisionReader.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal.constraints 2 | 3 | import com.amazon.ion.IonValue 4 | import com.amazon.ionschema.model.Constraint 5 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 6 | import com.amazon.ionschema.reader.internal.ReaderContext 7 | import com.amazon.ionschema.reader.internal.toTimestampPrecisionRange 8 | 9 | @ExperimentalIonSchemaModel 10 | internal class TimestampPrecisionReader : ConstraintReader { 11 | 12 | override fun canRead(fieldName: String): Boolean = fieldName == "timestamp_precision" 13 | 14 | override fun readConstraint(context: ReaderContext, field: IonValue): Constraint { 15 | check(canRead(field.fieldName)) 16 | return Constraint.TimestampPrecision(field.toTimestampPrecisionRange()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/util/Bag.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.util 2 | 3 | /** 4 | * A Bag (also known as a Multiset) is an unordered collection that allows duplicate elements. 5 | */ 6 | class Bag(elements: List) : Collection by elements { 7 | constructor() : this(emptyList()) 8 | 9 | override fun equals(other: Any?): Boolean { 10 | if (other === this) return true 11 | if (other !is Bag<*>) return false 12 | if (size != other.size) return false 13 | return this.groupingBy { it }.eachCount() == other.groupingBy { it }.eachCount() 14 | } 15 | 16 | override fun hashCode(): Int = this.sumBy { it.hashCode() } 17 | 18 | override fun toString(): String { 19 | return "Bag[${this.joinToString()}]" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/util/CloseableIterator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.util 17 | 18 | import java.io.Closeable 19 | 20 | /** 21 | * An Iterator that has the opportunity to free up any resources 22 | * when [close()] is called, after it is no longer needed. 23 | */ 24 | interface CloseableIterator : Iterator, Closeable 25 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/util/IonSchemaResult.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.util 2 | 3 | import com.amazon.ionschema.IonSchemaException 4 | 5 | /** 6 | * [IonSchemaResult] is a type that represents either success [Ok] or failure [Err]. 7 | */ 8 | sealed class IonSchemaResult { 9 | /** 10 | * Gets the [Ok] result value. Throws an [IonSchemaException] if the result is [Err]. 11 | */ 12 | abstract fun unwrap(): T 13 | 14 | /** 15 | * Gets the [Ok] value or `null` if not [Ok]. 16 | */ 17 | fun okValueOrNull(): T? = (this as? Ok)?.value 18 | 19 | /** 20 | * Gets the [Err] value or `null` if not an [Err]. 21 | */ 22 | fun errValueOrNull(): E? = (this as? Err)?.err 23 | 24 | /** 25 | * Returns `true` if the result is [Ok]. 26 | */ 27 | fun isOk() = this is Ok 28 | 29 | /** 30 | * Returns `true` if the result is [Err]. 31 | */ 32 | fun isErr() = this is Err 33 | 34 | /** 35 | * Contains the success value 36 | */ 37 | data class Ok(internal val value: T) : IonSchemaResult() { 38 | override fun unwrap(): T = value 39 | } 40 | 41 | /** 42 | * Contains the error value 43 | */ 44 | data class Err( 45 | val err: E, 46 | internal var toException: (E) -> IonSchemaException = { IonSchemaException("$it") } 47 | ) : IonSchemaResult() { 48 | override fun unwrap(): Nothing = throw toException(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/util/RegexImplementation.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.util 5 | 6 | import java.util.function.Predicate 7 | import java.util.regex.Pattern as JPattern 8 | 9 | /** 10 | * Interface that allows any regular expression implementation to be injected into an 11 | * [`IonSchemaSystem`][com.amazon.ionschema.IonSchemaSystem]. 12 | * 13 | * See [`IonSchemaSystemBuilder.withRegexImplementation`] 14 | * [com.amazon.ionschema.IonSchemaSystemBuilder.withRegexImplementation] for details. 15 | */ 16 | interface RegexImplementation { 17 | 18 | /** Compile a [pattern] string into a [Pattern]. */ 19 | fun compile(pattern: String, multiline: Boolean, caseInsensitive: Boolean): Pattern 20 | 21 | /** An abstraction over a compiled regular expression regardless of the particular regex implementation. */ 22 | open class Pattern( 23 | /** The regular expression from which this pattern was compiled */ 24 | val pattern: String, 25 | /** A predicate which can be used for finding a match on a subsequence of a string. */ 26 | test: Predicate 27 | ) : Predicate by test 28 | } 29 | 30 | /** Default [RegexImplementation] used by Ion Schema Kotlin. This is backed by the Java standard library. */ 31 | object DefaultRegexImplementation : RegexImplementation { 32 | 33 | override fun compile(pattern: String, multiline: Boolean, caseInsensitive: Boolean): RegexImplementation.Pattern { 34 | val flags = (if (multiline) JPattern.MULTILINE else 0) + 35 | (if (caseInsensitive) JPattern.CASE_INSENSITIVE else 0) 36 | 37 | return RegexImplementation.Pattern(pattern, JPattern.compile(pattern, flags).asPredicate()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/util/bags.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.util 2 | 3 | private val EMPTY_BAG: Bag = Bag(emptyList()) 4 | 5 | /** 6 | * Returns an empty read-only bag. 7 | */ 8 | fun emptyBag(): Bag = EMPTY_BAG 9 | 10 | /** 11 | * Returns a new read-only bag of given elements. 12 | */ 13 | fun bagOf(vararg values: E): Bag = if (values.isEmpty()) emptyBag() else Bag(values.toList()) 14 | 15 | /** 16 | * Returns a [Bag] containing all elements. 17 | */ 18 | fun Iterable.toBag(): Bag = Bag(this.toList()) 19 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/IonSchemaWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.IonSchemaVersion 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.SchemaDocument 10 | import com.amazon.ionschema.writer.internal.IonSchemaWriterV2_0 11 | import com.amazon.ionschema.writer.internal.VersionedIonSchemaWriter 12 | 13 | /** 14 | * Writes Ion Schema model to an IonWriter. 15 | */ 16 | @ExperimentalIonSchemaModel 17 | object IonSchemaWriter { 18 | /** 19 | * Writes a [SchemaDocument]. 20 | */ 21 | @JvmStatic 22 | fun writeSchema(ionWriter: IonWriter, schemaDocument: SchemaDocument) { 23 | val delegate: VersionedIonSchemaWriter = when (schemaDocument.ionSchemaVersion) { 24 | IonSchemaVersion.v1_0 -> TODO("IonSchemaWriter does not support ISL 1.0") 25 | IonSchemaVersion.v2_0 -> IonSchemaWriterV2_0 26 | } 27 | delegate.writeSchema(ionWriter, schemaDocument) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/FooterWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.SchemaFooter 9 | 10 | @ExperimentalIonSchemaModel 11 | object FooterWriter { 12 | fun writeFooter(ionWriter: IonWriter, schemaFooter: SchemaFooter) { 13 | ionWriter.setTypeAnnotations("schema_footer") 14 | ionWriter.writeStruct { 15 | for ((fieldName, fieldValue) in schemaFooter.openContent) { 16 | ionWriter.setFieldName(fieldName) 17 | fieldValue.writeTo(ionWriter) 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/IonSchemaWriterV2_0.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.IonSchemaVersion 8 | import com.amazon.ionschema.internal.util.islRequire 9 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 10 | import com.amazon.ionschema.model.NamedTypeDefinition 11 | import com.amazon.ionschema.model.SchemaDocument 12 | import com.amazon.ionschema.model.SchemaFooter 13 | import com.amazon.ionschema.model.SchemaHeader 14 | import com.amazon.ionschema.model.TypeDefinition 15 | 16 | @ExperimentalIonSchemaModel 17 | internal object IonSchemaWriterV2_0 : VersionedIonSchemaWriter { 18 | 19 | private val headerWriter = HeaderWriter 20 | private val typeWriter = TypeWriterV2_0 21 | private val footerWriter = FooterWriter 22 | 23 | override fun writeSchema(ionWriter: IonWriter, schemaDocument: SchemaDocument) { 24 | islRequire(schemaDocument.ionSchemaVersion == IonSchemaVersion.v2_0) { "IonSchemaWriterV2_0 only supports ISL 2.0" } 25 | ionWriter.writeSymbol("\$ion_schema_2_0") 26 | for (item in schemaDocument.items) { 27 | when (item) { 28 | is SchemaHeader -> headerWriter.writeHeader(ionWriter, item) 29 | is NamedTypeDefinition -> writeNamedType(ionWriter, item) 30 | is SchemaFooter -> footerWriter.writeFooter(ionWriter, item) 31 | is SchemaDocument.OpenContent -> item.value.writeTo(ionWriter) 32 | } 33 | } 34 | } 35 | 36 | override fun writeType(ionWriter: IonWriter, typeDefinition: TypeDefinition) { 37 | typeWriter.writeOrphanedTypeDefinition(ionWriter, typeDefinition) 38 | } 39 | 40 | override fun writeNamedType(ionWriter: IonWriter, namedTypeDefinition: NamedTypeDefinition) { 41 | typeWriter.writeNamedTypeDefinition(ionWriter, namedTypeDefinition) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/TypeWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.DiscreteIntRange 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.NamedTypeDefinition 10 | import com.amazon.ionschema.model.TypeArgument 11 | import com.amazon.ionschema.model.TypeArguments 12 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument 13 | 14 | @ExperimentalIonSchemaModel 15 | internal interface TypeWriter { 16 | 17 | /** 18 | * Writes a [NamedTypeDefinition] to the given [IonWriter]. 19 | */ 20 | fun writeNamedTypeDefinition(ionWriter: IonWriter, namedTypeDefinition: NamedTypeDefinition) 21 | 22 | /** 23 | * Writes a [TypeArgument] to the given [IonWriter]. 24 | */ 25 | fun writeTypeArg(ionWriter: IonWriter, typeArg: TypeArgument) 26 | 27 | /** 28 | * Writes a [VariablyOccurringTypeArgument] to the given [IonWriter]. 29 | */ 30 | fun writeVariablyOccurringTypeArg(ionWriter: IonWriter, varTypeArg: VariablyOccurringTypeArgument, elideOccursValue: DiscreteIntRange) 31 | } 32 | 33 | /** 34 | * Writes a [TypeArguments] to the given [IonWriter]. 35 | */ 36 | @ExperimentalIonSchemaModel 37 | internal fun TypeWriter.writeTypeArguments(ionWriter: IonWriter, typeArgs: TypeArguments) { 38 | ionWriter.writeToList(typeArgs) { writeTypeArg(ionWriter, it) } 39 | } 40 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/VersionedIonSchemaWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.NamedTypeDefinition 9 | import com.amazon.ionschema.model.SchemaDocument 10 | import com.amazon.ionschema.model.TypeDefinition 11 | 12 | @OptIn(ExperimentalIonSchemaModel::class) 13 | interface VersionedIonSchemaWriter { 14 | /** 15 | * Writes a [SchemaDocument]. 16 | */ 17 | fun writeSchema(ionWriter: IonWriter, schemaDocument: SchemaDocument) 18 | 19 | /** 20 | * Writes an orphaned [TypeDefinition]—that is an anonymous type definition that does not belong to any schema. 21 | */ 22 | fun writeType(ionWriter: IonWriter, typeDefinition: TypeDefinition) 23 | 24 | /** 25 | * Writes a [NamedTypeDefinition]. 26 | */ 27 | fun writeNamedType(ionWriter: IonWriter, namedTypeDefinition: NamedTypeDefinition) 28 | } 29 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/AnnotationsV2Writer.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.Constraint.AnnotationsV2.Modifier.Closed 9 | import com.amazon.ionschema.model.Constraint.AnnotationsV2.Modifier.Exact 10 | import com.amazon.ionschema.model.Constraint.AnnotationsV2.Modifier.Required 11 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 12 | import com.amazon.ionschema.writer.internal.TypeWriter 13 | import com.amazon.ionschema.writer.internal.writeToList 14 | 15 | @ExperimentalIonSchemaModel 16 | internal class AnnotationsV2Writer(private val typeWriter: TypeWriter) : ConstraintWriter { 17 | 18 | override val supportedClasses = setOf( 19 | Constraint.AnnotationsV2.Simplified::class, 20 | Constraint.AnnotationsV2.Standard::class 21 | ) 22 | 23 | override fun IonWriter.write(c: Constraint) { 24 | check(c is Constraint.AnnotationsV2) 25 | 26 | setFieldName("annotations") 27 | when (c) { 28 | is Constraint.AnnotationsV2.Standard -> typeWriter.writeTypeArg(this, c.type) 29 | is Constraint.AnnotationsV2.Simplified -> { 30 | when (c.modifier) { 31 | Closed -> setTypeAnnotations("closed") 32 | Required -> setTypeAnnotations("required") 33 | Exact -> setTypeAnnotations("closed", "required") 34 | } 35 | writeToList(c.annotations) { writeSymbol(it) } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/ConstraintWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import kotlin.reflect.KClass 10 | 11 | /** 12 | * Allows us to compose TypeWriters out of different combinations of constraint writers to enable code reuse across 13 | * multiple Ion Schema versions. 14 | */ 15 | @ExperimentalIonSchemaModel 16 | internal interface ConstraintWriter { 17 | /** 18 | * Returns the constraint types that can be written by this constraint writer. 19 | */ 20 | val supportedClasses: Set> 21 | 22 | /** 23 | * Writes a [Constraint] instance to the given IonWriter. 24 | * Must throw [IllegalStateException] if called for an unsupported constraint type. 25 | * Equivalent to [write], but more convenient for callers. 26 | */ 27 | fun writeTo(ionWriter: IonWriter, constraint: Constraint) = ionWriter.write(constraint) 28 | 29 | /** 30 | * Writes a [Constraint] instance to the given IonWriter. 31 | * Must throw [IllegalStateException] if called for an unsupported constraint type. 32 | * Equivalent to [writeTo], but more convenient for implementers. 33 | */ 34 | fun IonWriter.write(c: Constraint) 35 | } 36 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/ContainsWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeIonValue 10 | import com.amazon.ionschema.writer.internal.writeToList 11 | 12 | @ExperimentalIonSchemaModel 13 | internal object ContainsWriter : ConstraintWriter { 14 | override val supportedClasses = setOf(Constraint.Contains::class) 15 | 16 | override fun IonWriter.write(c: Constraint) { 17 | check(c is Constraint.Contains) 18 | setFieldName("contains") 19 | writeToList(c.values) { writeIonValue(it) } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/ElementWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.IonSchemaException 8 | import com.amazon.ionschema.IonSchemaVersion 9 | import com.amazon.ionschema.model.Constraint 10 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 11 | import com.amazon.ionschema.writer.internal.TypeWriter 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class ElementWriter(private val typeWriter: TypeWriter, private val ionSchemaVersion: IonSchemaVersion) : ConstraintWriter { 15 | override val supportedClasses = setOf(Constraint.Element::class) 16 | 17 | override fun IonWriter.write(c: Constraint) { 18 | check(c is Constraint.Element) 19 | 20 | setFieldName("element") 21 | if (c.distinct) { 22 | if (ionSchemaVersion == IonSchemaVersion.v1_0) { 23 | throw IonSchemaException("Ion Schema 1.0 does not support 'distinct' elements") 24 | } else { 25 | setTypeAnnotations("distinct") 26 | } 27 | } 28 | typeWriter.writeTypeArg(this@write, c.type) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/ExponentWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeRange 10 | 11 | @ExperimentalIonSchemaModel 12 | internal object ExponentWriter : ConstraintWriter { 13 | override val supportedClasses = setOf(Constraint.Exponent::class) 14 | override fun IonWriter.write(c: Constraint) { 15 | check(c is Constraint.Exponent) 16 | setFieldName("exponent") 17 | writeRange(c.range) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/FieldNamesWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.TypeWriter 10 | 11 | @ExperimentalIonSchemaModel 12 | internal class FieldNamesWriter(private val typeWriter: TypeWriter) : ConstraintWriter { 13 | override val supportedClasses = setOf(Constraint.FieldNames::class) 14 | 15 | override fun IonWriter.write(c: Constraint) { 16 | check(c is Constraint.FieldNames) 17 | setFieldName("field_names") 18 | if (c.distinct) { 19 | setTypeAnnotations("distinct") 20 | } 21 | typeWriter.writeTypeArg(this@write, c.type) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/FieldsWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.IonSchemaVersion 8 | import com.amazon.ionschema.model.Constraint 9 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 10 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument.Companion.OCCURS_OPTIONAL 11 | import com.amazon.ionschema.writer.internal.TypeWriter 12 | import com.amazon.ionschema.writer.internal.writeToStruct 13 | 14 | @ExperimentalIonSchemaModel 15 | internal class FieldsWriter(private val typeWriter: TypeWriter, private val ionSchemaVersion: IonSchemaVersion) : ConstraintWriter { 16 | override val supportedClasses = setOf(Constraint.Fields::class) 17 | 18 | override fun IonWriter.write(c: Constraint) { 19 | check(c is Constraint.Fields) 20 | 21 | if (c.closed && ionSchemaVersion == IonSchemaVersion.v1_0) { 22 | setFieldName("content") 23 | writeSymbol("closed") 24 | } 25 | 26 | setFieldName("fields") 27 | if (c.closed && ionSchemaVersion != IonSchemaVersion.v1_0) setTypeAnnotations("closed") 28 | writeToStruct(c.fields) { 29 | typeWriter.writeVariablyOccurringTypeArg(this@writeToStruct, it, elideOccursValue = OCCURS_OPTIONAL) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/Ieee754FloatWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | 10 | @ExperimentalIonSchemaModel 11 | object Ieee754FloatWriter : ConstraintWriter { 12 | override val supportedClasses = setOf(Constraint.Ieee754Float::class) 13 | 14 | override fun IonWriter.write(c: Constraint) { 15 | check(c is Constraint.Ieee754Float) 16 | setFieldName("ieee754_float") 17 | writeSymbol(c.format.symbolText) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LengthConstraintsWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeRange 10 | import kotlin.reflect.KClass 11 | 12 | @ExperimentalIonSchemaModel 13 | internal object LengthConstraintsWriter : ConstraintWriter { 14 | 15 | override val supportedClasses: Set> = setOf( 16 | Constraint.ByteLength::class, 17 | Constraint.CodepointLength::class, 18 | Constraint.ContainerLength::class, 19 | Constraint.Utf8ByteLength::class, 20 | ) 21 | 22 | override fun IonWriter.write(c: Constraint) { 23 | when (c) { 24 | is Constraint.ByteLength -> { 25 | setFieldName("byte_length") 26 | writeRange(c.range) 27 | } 28 | is Constraint.CodepointLength -> { 29 | setFieldName("codepoint_length") 30 | writeRange(c.range) 31 | } 32 | is Constraint.ContainerLength -> { 33 | setFieldName("container_length") 34 | writeRange(c.range) 35 | } 36 | is Constraint.Utf8ByteLength -> { 37 | setFieldName("utf8_byte_length") 38 | writeRange(c.range) 39 | } 40 | else -> throw IllegalStateException("Unsupported constraint. Should be unreachable.") 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.TypeWriter 10 | import com.amazon.ionschema.writer.internal.writeTypeArguments 11 | import kotlin.reflect.KClass 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class LogicConstraintsWriter(private val typeWriter: TypeWriter) : ConstraintWriter { 15 | override val supportedClasses: Set> = setOf( 16 | Constraint.AllOf::class, 17 | Constraint.AnyOf::class, 18 | Constraint.Not::class, 19 | Constraint.OneOf::class, 20 | Constraint.Type::class, 21 | ) 22 | 23 | override fun IonWriter.write(c: Constraint) { 24 | when (c) { 25 | is Constraint.AllOf -> { 26 | setFieldName("all_of") 27 | typeWriter.writeTypeArguments(this@write, c.types) 28 | } 29 | is Constraint.AnyOf -> { 30 | setFieldName("any_of") 31 | typeWriter.writeTypeArguments(this@write, c.types) 32 | } 33 | is Constraint.OneOf -> { 34 | setFieldName("one_of") 35 | typeWriter.writeTypeArguments(this@write, c.types) 36 | } 37 | is Constraint.Not -> { 38 | setFieldName("not") 39 | typeWriter.writeTypeArg(this@write, c.type) 40 | } 41 | is Constraint.Type -> { 42 | setFieldName("type") 43 | typeWriter.writeTypeArg(this@write, c.type) 44 | } 45 | else -> check(false) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.VariablyOccurringTypeArgument.Companion.OCCURS_REQUIRED 10 | import com.amazon.ionschema.writer.internal.TypeWriter 11 | import com.amazon.ionschema.writer.internal.writeToList 12 | 13 | @ExperimentalIonSchemaModel 14 | internal class OrderedElementsWriter(private val typeWriter: TypeWriter) : ConstraintWriter { 15 | override val supportedClasses = setOf(Constraint.OrderedElements::class) 16 | 17 | override fun IonWriter.write(c: Constraint) { 18 | check(c is Constraint.OrderedElements) 19 | setFieldName("ordered_elements") 20 | writeToList(c.types) { 21 | typeWriter.writeVariablyOccurringTypeArg(this, it, elideOccursValue = OCCURS_REQUIRED) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/PrecisionWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeRange 10 | 11 | @ExperimentalIonSchemaModel 12 | internal object PrecisionWriter : ConstraintWriter { 13 | override val supportedClasses = setOf(Constraint.Precision::class) 14 | 15 | override fun IonWriter.write(c: Constraint) { 16 | check(c is Constraint.Precision) 17 | setFieldName("precision") 18 | writeRange(c.range) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/RegexWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | 10 | @ExperimentalIonSchemaModel 11 | internal object RegexWriter : ConstraintWriter { 12 | override val supportedClasses = setOf(Constraint.Regex::class) 13 | 14 | override fun IonWriter.write(c: Constraint) { 15 | check(c is Constraint.Regex) 16 | setFieldName("regex") 17 | if (c.caseInsensitive) addTypeAnnotation("i") 18 | if (c.multiline) addTypeAnnotation("m") 19 | writeString(c.pattern) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/TimestampOffsetWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeToList 10 | 11 | @ExperimentalIonSchemaModel 12 | internal object TimestampOffsetWriter : ConstraintWriter { 13 | override val supportedClasses = setOf(Constraint.TimestampOffset::class) 14 | 15 | override fun IonWriter.write(c: Constraint) { 16 | check(c is Constraint.TimestampOffset) 17 | setFieldName("timestamp_offset") 18 | writeToList(c.offsets) { writeString(it.toString()) } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/TimestampPrecisionWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.writer.internal.writeTimestampPrecisionRange 10 | 11 | @ExperimentalIonSchemaModel 12 | internal object TimestampPrecisionWriter : ConstraintWriter { 13 | override val supportedClasses = setOf(Constraint.TimestampPrecision::class) 14 | 15 | override fun IonWriter.write(c: Constraint) { 16 | check(c is Constraint.TimestampPrecision) 17 | setFieldName("timestamp_precision") 18 | writeTimestampPrecisionRange(c.range) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/ValidValuesWriter.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.ValidValue 10 | import com.amazon.ionschema.writer.internal.writeIonValue 11 | import com.amazon.ionschema.writer.internal.writeNumberRange 12 | import com.amazon.ionschema.writer.internal.writeTimestampRange 13 | import com.amazon.ionschema.writer.internal.writeToList 14 | 15 | @ExperimentalIonSchemaModel 16 | internal object ValidValuesWriter : ConstraintWriter { 17 | override val supportedClasses = setOf(Constraint.ValidValues::class) 18 | 19 | override fun IonWriter.write(c: Constraint) { 20 | check(c is Constraint.ValidValues) 21 | setFieldName("valid_values") 22 | writeToList(c.values) { 23 | when (it) { 24 | is ValidValue.NumberRange -> writeNumberRange(it) 25 | is ValidValue.TimestampRange -> writeTimestampRange(it) 26 | is ValidValue.Value -> writeIonValue(it.value) 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/util.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.IonType 7 | import com.amazon.ion.IonValue 8 | import com.amazon.ion.IonWriter 9 | 10 | /** 11 | * Steps into a struct, writes [content] to this [IonWriter], and steps out of the struct in a `finally` block. 12 | */ 13 | internal inline fun IonWriter.writeStruct(content: IonWriter.() -> Unit) { 14 | try { 15 | stepIn(IonType.STRUCT) 16 | content() 17 | } finally { 18 | stepOut() 19 | } 20 | } 21 | 22 | /** 23 | * Steps into a struct, invokes [valueWriter] for each element of [values], and steps out of the struct in a `finally` block. 24 | */ 25 | internal inline fun IonWriter.writeToStruct(values: Map, valueWriter: IonWriter.(T) -> Unit) { 26 | try { 27 | stepIn(IonType.STRUCT) 28 | values.forEach { (k, v) -> 29 | setFieldName(k) 30 | valueWriter(v) 31 | } 32 | } finally { 33 | stepOut() 34 | } 35 | } 36 | 37 | /** 38 | * Steps into a list, writes [content] to this [IonWriter], and steps out of the list in a `finally` block. 39 | */ 40 | internal inline fun IonWriter.writeList(content: IonWriter.() -> Unit) { 41 | try { 42 | stepIn(IonType.LIST) 43 | content() 44 | } finally { 45 | stepOut() 46 | } 47 | } 48 | 49 | /** 50 | * Steps into a list, invokes [valueWriter] for each element of [values], and steps out of the list in a `finally` block. 51 | */ 52 | internal inline fun IonWriter.writeToList(values: Iterable, valueWriter: IonWriter.(T) -> Unit) { 53 | try { 54 | stepIn(IonType.LIST) 55 | values.forEach { valueWriter(it) } 56 | } finally { 57 | stepOut() 58 | } 59 | } 60 | 61 | /** 62 | * Writes an [IonValue] to an [IonWriter]. 63 | */ 64 | internal fun IonWriter.writeIonValue(value: IonValue) = value.writeTo(this) 65 | -------------------------------------------------------------------------------- /ion-schema/src/test/java/com/amazon/ionschema/util/SchemaSymbolsUtilJavaApiTest.java: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.Set; 7 | 8 | public class SchemaSymbolsUtilJavaApiTest { 9 | 10 | @Test 11 | public void testApi() { 12 | // Test just to make sure that these functions are nicely usable from Java 13 | Set symbolTexts = SchemaSymbolsUtil.getSymbolsTextForPath( 14 | "./src/main/resources/ion-schema-schemas/", 15 | // This lambda could be omitted, except that the ion-schema-schemas directory contains ISL 1.0, and 16 | // the SchemaSymbolsUtil doesn't support that yet. 17 | f -> f.getPath().contains("isl/ion_schema_2_0") 18 | ); 19 | Assertions.assertEquals(SchemaSymbolsUtilTest.ION_SCHEMA_2_0_SYMBOLS, symbolTexts); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/AuthorityFilesystemTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema 17 | 18 | import org.junit.jupiter.api.Assertions.assertFalse 19 | import org.junit.jupiter.api.Assertions.fail 20 | import org.junit.jupiter.api.Test 21 | import org.junit.jupiter.api.assertThrows 22 | import java.io.FileNotFoundException 23 | 24 | class AuthorityFilesystemTest { 25 | @Test 26 | fun nonExistentPath() { 27 | assertThrows { 28 | AuthorityFilesystem("non-existent-path") 29 | } 30 | } 31 | 32 | @Test 33 | fun unknownSchemaId() { 34 | val iss = IonSchemaSystemBuilder.standard().build() 35 | val authority = IonSchemaTests.authorityFor(IonSchemaVersion.v1_0) 36 | val iter = authority.iteratorFor(iss, "unknown_schema_id") 37 | assertFalse(iter.hasNext()) 38 | try { 39 | iter.next() 40 | fail() 41 | } catch (e: NoSuchElementException) { 42 | } 43 | iter.close() 44 | } 45 | 46 | @Test 47 | fun iteratorFor_outsideBasePath() { 48 | val iss = IonSchemaSystemBuilder.standard().build() 49 | val authority = AuthorityFilesystem("../ion-schema-tests") 50 | assertThrows { 51 | authority.iteratorFor(iss, "../schema_private/some_file.isl") 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/InMemoryMapAuthorityTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertFalse 5 | import kotlin.test.assertTrue 6 | import kotlin.test.fail 7 | 8 | class InMemoryMapAuthorityTest { 9 | 10 | @Test 11 | fun unknownSchemaId() { 12 | val iss = IonSchemaSystemBuilder.standard().build() 13 | val authority = InMemoryMapAuthority.fromIonText( 14 | "my_schema" to """ 15 | type::{ 16 | name: int_list, 17 | type: list, 18 | element: int 19 | } 20 | """ 21 | ) 22 | val iter = authority.iteratorFor(iss, "unknown_schema_id") 23 | assertFalse(iter.hasNext()) 24 | try { 25 | iter.next() 26 | fail() 27 | } catch (e: NoSuchElementException) { 28 | } 29 | iter.close() 30 | } 31 | 32 | @Test 33 | fun fromIonText_knownSchemaId() { 34 | val iss = IonSchemaSystemBuilder.standard().build() 35 | val authority = InMemoryMapAuthority.fromIonText( 36 | "my_schema" to """ 37 | type::{ 38 | name: int_list, 39 | type: list, 40 | element: int 41 | } 42 | """ 43 | ) 44 | val iter = authority.iteratorFor(iss, "my_schema") 45 | assertTrue(iter.hasNext()) 46 | iter.close() 47 | } 48 | 49 | @Test 50 | fun fromIonValues_knownSchemaId() { 51 | val iss = IonSchemaSystemBuilder.standard().build() 52 | val authority = InMemoryMapAuthority.fromIonValues( 53 | "my_schema" to iss.ionSystem.loader.load( 54 | """ 55 | type::{ 56 | name: int_list, 57 | type: list, 58 | element: int 59 | } 60 | """ 61 | )!! 62 | ) 63 | val iter = authority.iteratorFor(iss, "my_schema") 64 | assertTrue(iter.hasNext()) 65 | iter.close() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/TestFactory.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema 2 | 3 | import org.junit.jupiter.api.DynamicNode 4 | import org.junit.jupiter.api.TestFactory 5 | 6 | /** 7 | * Interface-based alternative to [org.junit.jupiter.api.TestFactory] so that we can create test classes by composition/delegation. 8 | */ 9 | internal interface TestFactory { 10 | @TestFactory 11 | fun generateTests(): Iterable 12 | } 13 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/internal/constraint/OneOfTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.internal.constraint 2 | 3 | import com.amazon.ionschema.ION 4 | import com.amazon.ionschema.IonSchemaSystemBuilder 5 | import org.junit.jupiter.api.Assertions.assertFalse 6 | import org.junit.jupiter.api.Test 7 | 8 | class OneOfTest { 9 | val iss = IonSchemaSystemBuilder.standard().build() 10 | 11 | @Test 12 | fun issue278() { 13 | // Reproduction of https://github.com/amazon-ion/ion-schema-kotlin/issues/278 14 | val schemaText = """ 15 | ${'$'}ion_schema_2_0 16 | type::{ 17 | name: foo, 18 | one_of: [foo_a, foo_b] 19 | } 20 | 21 | type::{ 22 | name: foo_a, 23 | type: struct, 24 | fields: { 25 | id: { type: string, occurs: required }, 26 | } 27 | } 28 | 29 | type::{ 30 | name: foo_b, 31 | type: struct, 32 | fields: { 33 | digest: { type: string, occurs: required }, 34 | } 35 | } 36 | """ 37 | 38 | val schema = iss.newSchema(schemaText) 39 | val type = schema.getType("foo")!! 40 | 41 | // In issue #278, this was throwing a ClassCastException. 42 | // This should return normally and indicate that the value is invalid. 43 | val result = type.validate(ION.singleValue("""{ id: "abc", digest: "def" }""")) 44 | assertFalse(result.isValid()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/internal/util/RangeBoundaryTypeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.system.IonSystemBuilder 19 | import com.amazon.ionschema.IonSchemaException 20 | import com.amazon.ionschema.internal.util.RangeBoundaryType.EXCLUSIVE 21 | import com.amazon.ionschema.internal.util.RangeBoundaryType.INCLUSIVE 22 | import org.junit.jupiter.api.Assertions.assertEquals 23 | import org.junit.jupiter.api.Assertions.fail 24 | import org.junit.jupiter.api.Test 25 | 26 | internal class RangeBoundaryTypeTest { 27 | private val ION = IonSystemBuilder.standard().build() 28 | 29 | @Test fun exclusive() { assert(EXCLUSIVE, "exclusive::5") } 30 | @Test fun inclusive() { assert(INCLUSIVE, "min") } 31 | @Test fun max() { assert(INCLUSIVE, "max") } 32 | @Test fun min() { assert(INCLUSIVE, "min") } 33 | @Test fun max_exclusive() { assertException("exclusive::max") } 34 | @Test fun min_exclusive() { assertException("exclusive::min") } 35 | 36 | private fun assert(expected: RangeBoundaryType, str: String) { 37 | assertEquals(expected, RangeBoundaryType.forIon(ION.singleValue(str))) 38 | } 39 | 40 | private fun assertException(str: String) { 41 | try { 42 | RangeBoundaryType.forIon(ION.singleValue(str)) 43 | fail("Expected an IonSchemaException") 44 | } catch (e: IonSchemaException) { 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/internal/util/RangeIntNonNegativeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | package com.amazon.ionschema.internal.util 17 | 18 | import com.amazon.ion.IonList 19 | import org.junit.jupiter.api.Test 20 | 21 | internal class RangeIntNonNegativeTest : AbstractRangeTest(RangeType.INT_NON_NEGATIVE) { 22 | 23 | @Suppress("UNCHECKED_CAST") 24 | override fun rangeOf(ion: IonList) = RangeFactory.rangeOf(ion, RangeType.INT_NON_NEGATIVE) as Range 25 | 26 | @Test 27 | fun range_int_inclusive() { 28 | assertValidRangeAndValues( 29 | "range::[0, 100]", 30 | listOf(0, 100), 31 | listOf(-1, 101) 32 | ) 33 | } 34 | 35 | @Test 36 | fun range_int_exclusive() { 37 | assertValidRangeAndValues( 38 | "range::[exclusive::0, exclusive::100]", 39 | listOf(1, 99), 40 | listOf(0, 100) 41 | ) 42 | } 43 | 44 | @Test 45 | fun range_invalid() { 46 | assertInvalidRange("range::[-1, 0]") 47 | assertInvalidRange("range::[0, -1]") 48 | 49 | assertInvalidRange("range::[exclusive::1, 1]") 50 | assertInvalidRange("range::[1, exclusive::1]") 51 | 52 | assertInvalidRange("range::[0, exclusive::1]") 53 | assertInvalidRange("range::[exclusive::0, 1]") 54 | 55 | assertInvalidRange("range::[0d0, 1]") 56 | assertInvalidRange("range::[0, 1d0]") 57 | assertInvalidRange("range::[0e0, 1]") 58 | assertInvalidRange("range::[0, 1e0]") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/model/ConsistentDecimalTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import java.math.BigDecimal 6 | 7 | class ConsistentDecimalTest { 8 | 9 | @Test 10 | fun `compareTo, hashCode, and equals are consistent for fractional numbers with different precision`() { 11 | val a = ConsistentDecimal(BigDecimal("20.100000")) 12 | val b = ConsistentDecimal(BigDecimal("20.1")) 13 | 14 | assertEquals(a, b) 15 | assertEquals(a.hashCode(), b.hashCode()) 16 | assertEquals(0, a.compareTo(b)) 17 | assertEquals("$a", "$b") 18 | println("$a == $b") 19 | } 20 | 21 | @Test 22 | fun `compareTo, hashCode, and equals are consistent for integers with different precision`() { 23 | val a = ConsistentDecimal(BigDecimal("10.00000")) 24 | val b = ConsistentDecimal(BigDecimal("10")) 25 | 26 | assertEquals(a, b) 27 | assertEquals(a.hashCode(), b.hashCode()) 28 | assertEquals(0, a.compareTo(b)) 29 | assertEquals("$a", "$b") 30 | println("$a == $b") 31 | } 32 | 33 | @Test 34 | fun `compareTo, hashCode, and equals are consistent for zeros with different precision`() { 35 | val a = ConsistentDecimal(BigDecimal("0.00000")) 36 | val b = ConsistentDecimal(BigDecimal("0")) 37 | 38 | assertEquals(a, b) 39 | assertEquals(a.hashCode(), b.hashCode()) 40 | assertEquals(0, a.compareTo(b)) 41 | assertEquals("$a", "$b") 42 | println("$a == $b") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/model/ConsistentTimestampTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import com.amazon.ion.Timestamp 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class ConsistentTimestampTest { 8 | 9 | @Test 10 | fun `compareTo, hashCode, and equals are consistent when precision is different`() { 11 | val a = ConsistentTimestamp(Timestamp.valueOf("2022-01-01T00:00Z")) 12 | val b = ConsistentTimestamp(Timestamp.valueOf("2022-01-01T00:00:00.000Z")) 13 | 14 | assertEquals(a, b) 15 | assertEquals(a.hashCode(), b.hashCode()) 16 | assertEquals(0, a.compareTo(b)) 17 | println("$a == $b") 18 | } 19 | 20 | @Test 21 | fun `compareTo, hashCode, and equals are consistent when offset is different`() { 22 | val a = ConsistentTimestamp(Timestamp.valueOf("2022-01-01T04:30:00+04:30")) 23 | val b = ConsistentTimestamp(Timestamp.valueOf("2022-01-01T00:00:00Z")) 24 | 25 | assertEquals(a, b) 26 | assertEquals(a.hashCode(), b.hashCode()) 27 | assertEquals(0, a.compareTo(b)) 28 | println("$a == $b") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/model/Ieee754InterchangeFormatTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.model 2 | 3 | import org.junit.jupiter.api.Assertions 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.assertThrows 6 | import org.junit.jupiter.params.ParameterizedTest 7 | import org.junit.jupiter.params.provider.EnumSource 8 | 9 | class Ieee754InterchangeFormatTest { 10 | 11 | @ParameterizedTest(name = "symbolText for {0}") 12 | @EnumSource 13 | fun `test symbolText`(iif: Ieee754InterchangeFormat) { 14 | val expected = when (iif) { 15 | Ieee754InterchangeFormat.Binary16 -> "binary16" 16 | Ieee754InterchangeFormat.Binary32 -> "binary32" 17 | Ieee754InterchangeFormat.Binary64 -> "binary64" 18 | } 19 | Assertions.assertEquals(expected, iif.symbolText) 20 | } 21 | 22 | @ParameterizedTest(name = "fromSymbolTextOrNull for {0}") 23 | @EnumSource 24 | fun `test fromSymbolTextOrNull`(iif: Ieee754InterchangeFormat) { 25 | val symbolText = iif.symbolText 26 | Assertions.assertEquals(iif, Ieee754InterchangeFormat.fromSymbolTextOrNull(symbolText)) 27 | } 28 | 29 | @Test 30 | fun `fromSymbolTextOrNull should return null when not a valid timestamp precision value`() { 31 | Assertions.assertNull(Ieee754InterchangeFormat.fromSymbolTextOrNull("unary42")) 32 | } 33 | 34 | @ParameterizedTest(name = "fromSymbolText for {0}") 35 | @EnumSource 36 | fun `test fromSymbolText`(iif: Ieee754InterchangeFormat) { 37 | val symbolText = iif.symbolText 38 | Assertions.assertEquals(iif, Ieee754InterchangeFormat.fromSymbolText(symbolText)) 39 | } 40 | 41 | @Test 42 | fun `fromSymbolTextOrNull should throw exception when not a valid timestamp precision value`() { 43 | assertThrows { Ieee754InterchangeFormat.fromSymbolText("unary42") } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/reader/internal/ReaderContextTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.reader.internal 2 | 3 | import com.amazon.ion.system.IonSystemBuilder 4 | import com.amazon.ionschema.IonSchemaException 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | import org.junit.jupiter.api.assertThrows 8 | 9 | class ReaderContextTest { 10 | companion object { 11 | private val ION = IonSystemBuilder.standard().build() 12 | } 13 | 14 | @Test 15 | fun `reportError() should throw when failFast is true`() { 16 | val context = ReaderContext(failFast = true) 17 | assertThrows { 18 | context.reportError(ReadError(ION.singleValue("-1"), "Warp drive setting must be positive.")) 19 | } 20 | } 21 | 22 | @Test 23 | fun `reportError() should not throw when failFast is false`() { 24 | val context = ReaderContext(failFast = false) 25 | val error1 = ReadError(ION.singleValue("-1"), "Warp drive setting must be positive.") 26 | val error2 = ReadError(ION.singleValue("11"), "Warp drive setting must be less than 10.") 27 | context.reportError(error1) 28 | context.reportError(error2) 29 | assertEquals(listOf(error1, error2), context.readErrors) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/util/BagTest.kt: -------------------------------------------------------------------------------- 1 | package com.amazon.ionschema.util 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Assertions.assertNotEquals 5 | import org.junit.jupiter.api.Assertions.assertSame 6 | import org.junit.jupiter.api.Test 7 | 8 | class BagTest { 9 | 10 | @Test 11 | fun `A Bag should not be equal to any other type of collection`() { 12 | val bag = bagOf(1, 2, 3) 13 | val set = setOf(1, 2, 3, 4) 14 | assertNotEquals(bag, set) 15 | } 16 | 17 | @Test 18 | fun `Bags with different content should not be equal`() { 19 | val bag1 = bagOf(1, 2, 3) 20 | val bag2 = bagOf(1, 2, 3, 4) 21 | assertNotEquals(bag1, bag2) 22 | } 23 | 24 | @Test 25 | fun `Bags with different quantities of the same content should not be equal`() { 26 | val bag1 = bagOf(1, 2, 3) 27 | val bag2 = bagOf(1, 1, 2, 2, 3, 3) 28 | assertNotEquals(bag1, bag2) 29 | } 30 | 31 | @Test 32 | fun `Bags with the same content in supposedly different order should be equal`() { 33 | val bag1 = bagOf(1, 2, 3) 34 | val bag2 = bagOf(3, 2, 1) 35 | assertEquals(bag1, bag2) 36 | } 37 | 38 | @Test 39 | fun `empty Bags should be equal`() { 40 | val bag1 = Bag() 41 | val bag2 = Bag() 42 | assertEquals(bag1, bag2) 43 | } 44 | 45 | @Test 46 | fun `equal Bags should have the equal hashcodes`() { 47 | assertEquals(Bag().hashCode(), Bag().hashCode()) 48 | assertEquals(bagOf(1).hashCode(), bagOf(1).hashCode()) 49 | assertEquals(bagOf(1, 2, 3).hashCode(), bagOf(3, 2, 1).hashCode()) 50 | } 51 | 52 | @Test 53 | fun `emptyBag() should return a singleton object`() { 54 | val bag1 = emptyBag() 55 | val bag2 = emptyBag() 56 | assertSame(bag1, bag2) 57 | } 58 | 59 | @Test 60 | fun `bagOf() with no elements should return the singleton emptyBag object`() { 61 | assertSame(emptyBag(), bagOf()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/IonSchemaWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer 5 | 6 | import com.amazon.ionschema.ION 7 | import com.amazon.ionschema.IonSchemaVersion 8 | import com.amazon.ionschema.assertEqualIon 9 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 10 | import com.amazon.ionschema.model.SchemaDocument 11 | import org.junit.jupiter.api.Test 12 | import org.junit.jupiter.api.assertThrows 13 | 14 | @OptIn(ExperimentalIonSchemaModel::class) 15 | class IonSchemaWriterTest { 16 | // The purpose of these tests is just to check that it delegates to the correct writer implementation. 17 | // Testing the actual serialization is done in WriterTests.kt 18 | 19 | @Test 20 | fun `IonSchemaWriter throw UnsupportedOperationException for ISL 1 0`() { 21 | val writer = ION.newTextWriter(StringBuilder()) 22 | val schema = SchemaDocument("schema.isl", IonSchemaVersion.v1_0, emptyList()) 23 | assertThrows { 24 | IonSchemaWriter.writeSchema(writer, schema) 25 | } 26 | } 27 | 28 | @Test 29 | fun `IonSchemaWriter writes a schema document for ISL 2 0`() { 30 | val schema = SchemaDocument("schema.isl", IonSchemaVersion.v2_0, emptyList()) 31 | // Since there's no content added to the schema, we expect just a version marker 32 | assertEqualIon("\$ion_schema_2_0 ") { 33 | IonSchemaWriter.writeSchema(it, schema) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/FooterWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal 5 | 6 | import com.amazon.ion.system.IonSystemBuilder 7 | import com.amazon.ionschema.assertEqualIon 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.SchemaFooter 10 | import com.amazon.ionschema.util.bagOf 11 | import org.junit.jupiter.api.Test 12 | 13 | @OptIn(ExperimentalIonSchemaModel::class) 14 | class FooterWriterTest { 15 | 16 | val ION = IonSystemBuilder.standard().build() 17 | private val footerWriter = FooterWriter 18 | 19 | @Test 20 | fun `writeFooter can write an empty footer`() { 21 | assertEqualIon("schema_footer::{}") { w -> footerWriter.writeFooter(w, SchemaFooter()) } 22 | } 23 | 24 | @Test 25 | fun `writeFooter can write a footer with open content`() { 26 | val footer = SchemaFooter( 27 | openContent = bagOf( 28 | "foo" to ION.newInt(1), 29 | "bar" to ION.newInt(2), 30 | ) 31 | ) 32 | assertEqualIon("schema_footer::{foo:1,bar:2}") { w -> footerWriter.writeFooter(w, footer) } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/AnnotationsV2WriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint.AnnotationsV2 7 | import com.amazon.ionschema.model.Constraint.AnnotationsV2.Modifier 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.TypeArgument 10 | 11 | @OptIn(ExperimentalIonSchemaModel::class) 12 | class AnnotationsV2WriterTest : ConstraintTestBase( 13 | writer = AnnotationsV2Writer(stubTypeWriterWithRefs("foo_type")), 14 | expectedConstraints = setOf( 15 | AnnotationsV2.Simplified::class, 16 | AnnotationsV2.Standard::class, 17 | ), 18 | writeTestCases = listOf( 19 | AnnotationsV2.Standard(TypeArgument.Reference("foo_type")) to "annotations: foo_type", 20 | AnnotationsV2.Simplified(Modifier.Closed, setOf("a")) to "annotations: closed::[a]", 21 | AnnotationsV2.Simplified(Modifier.Required, setOf("b")) to "annotations: required::[b]", 22 | AnnotationsV2.Simplified(Modifier.Exact, setOf("c")) to "annotations: closed::required::[c]", 23 | ) 24 | ) 25 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/ContainsWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint.Contains 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | 9 | @OptIn(ExperimentalIonSchemaModel::class) 10 | class ContainsWriterTest : ConstraintTestBase( 11 | writer = ContainsWriter, 12 | expectedConstraints = setOf(Contains::class), 13 | writeTestCases = listOf( 14 | Contains(emptySet()) to "contains: []", 15 | Contains(setOf(ion("[foo, true]"), ion("bar"))) to "contains: [[foo, true], bar]", 16 | Contains(setOf(ion("me::2"), ion("null.timestamp"), ion("{a:b}"))) to "contains: [me::2, null.timestamp, {a:b}]", 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/ElementWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.IonSchemaException 7 | import com.amazon.ionschema.IonSchemaVersion 8 | import com.amazon.ionschema.model.Constraint 9 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 10 | import com.amazon.ionschema.model.TypeArgument 11 | import io.mockk.mockk 12 | import org.junit.jupiter.api.Test 13 | import org.junit.jupiter.api.assertThrows 14 | 15 | @OptIn(ExperimentalIonSchemaModel::class) 16 | class ElementWriterTest : ConstraintTestBase( 17 | writer = ElementWriter(stubTypeWriterWithRefs("foo_type"), IonSchemaVersion.v2_0), 18 | expectedConstraints = setOf(Constraint.Element::class), 19 | writeTestCases = listOf( 20 | Constraint.Element(TypeArgument.Reference("foo_type")) to "element: foo_type", 21 | Constraint.Element(TypeArgument.Reference("foo_type"), distinct = true) to "element: distinct::foo_type", 22 | ) 23 | ) { 24 | @Test 25 | fun `writer should throw exception when distinct = true and version = v1_0`() { 26 | val writer = ElementWriter(stubTypeWriterWithRefs("foo_type"), IonSchemaVersion.v1_0) 27 | val constraint = Constraint.Element(TypeArgument.Reference("foo_type"), distinct = true) 28 | assertThrows { 29 | writer.writeTo(mockk(relaxed = true), constraint) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/ExponentWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.DiscreteIntRange 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class ExponentWriterTest : ConstraintTestBase( 12 | writer = ExponentWriter, 13 | expectedConstraints = setOf(Constraint.Exponent::class), 14 | writeTestCases = listOf( 15 | Constraint.Exponent(DiscreteIntRange(2, 5)) to "exponent: range::[2, 5]", 16 | Constraint.Exponent(DiscreteIntRange(null, 23)) to "exponent: range::[min, 23]", 17 | Constraint.Exponent(DiscreteIntRange(7, null)) to "exponent: range::[7, max]", 18 | Constraint.Exponent(DiscreteIntRange(3, 3)) to "exponent: 3", 19 | ), 20 | ) 21 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/FieldNamesWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.TypeArgument 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class FieldNamesWriterTest : ConstraintTestBase( 12 | writer = FieldNamesWriter(stubTypeWriterWithRefs("foo_type")), 13 | expectedConstraints = setOf(Constraint.FieldNames::class), 14 | writeTestCases = listOf( 15 | Constraint.FieldNames(TypeArgument.Reference("foo_type")) to "field_names: foo_type", 16 | Constraint.FieldNames(TypeArgument.Reference("foo_type"), distinct = true) to "field_names: distinct::foo_type", 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/FieldsWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.IonSchemaVersion 7 | import com.amazon.ionschema.model.Constraint 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.TypeArgument 10 | import com.amazon.ionschema.model.occurs 11 | import com.amazon.ionschema.model.optional 12 | import org.junit.jupiter.api.Test 13 | 14 | @OptIn(ExperimentalIonSchemaModel::class) 15 | class FieldsWriterTest : ConstraintTestBase( 16 | writer = FieldsWriter(stubTypeWriterWithRefs("foo_type", "bar_type"), IonSchemaVersion.v2_0), 17 | expectedConstraints = setOf(Constraint.Fields::class), 18 | writeTestCases = listOf( 19 | Constraint.Fields(fieldsMap, closed = true) to "fields: closed::{ a: foo_type, b: bar_type }", 20 | Constraint.Fields(fieldsMap, closed = false) to "fields: { a: foo_type, b: bar_type }", 21 | ) 22 | ) { 23 | companion object { 24 | private val fieldsMap = mapOf( 25 | "a" to TypeArgument.Reference("foo_type").optional(), 26 | "b" to TypeArgument.Reference("bar_type").occurs(0, 1), 27 | ) 28 | } 29 | 30 | @Test 31 | fun `writer should write content closed for v1_0`() { 32 | val writer = FieldsWriter(stubTypeWriterWithRefs("foo_type", "bar_type"), IonSchemaVersion.v1_0) 33 | runWriteCase( 34 | writer, 35 | Constraint.Fields(fieldsMap, closed = true) to "content: closed, fields: { a: foo_type, b: bar_type }" 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/Ieee754FloatWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.Ieee754InterchangeFormat 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class Ieee754FloatWriterTest : ConstraintTestBase( 12 | writer = Ieee754FloatWriter, 13 | expectedConstraints = setOf(Constraint.Ieee754Float::class), 14 | writeTestCases = listOf( 15 | Constraint.Ieee754Float(Ieee754InterchangeFormat.Binary16) to "ieee754_float: binary16", 16 | Constraint.Ieee754Float(Ieee754InterchangeFormat.Binary32) to "ieee754_float: binary32", 17 | Constraint.Ieee754Float(Ieee754InterchangeFormat.Binary64) to "ieee754_float: binary64", 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.TypeArgument.Reference 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class LogicConstraintsWriterTest : ConstraintTestBase( 12 | writer = LogicConstraintsWriter(stubTypeWriterWithRefs("foo", "bar")), 13 | expectedConstraints = setOf( 14 | Constraint.AllOf::class, 15 | Constraint.AnyOf::class, 16 | Constraint.OneOf::class, 17 | Constraint.Not::class, 18 | Constraint.Type::class, 19 | ), 20 | writeTestCases = listOf( 21 | Constraint.AllOf(emptySet()) to "all_of: []", 22 | Constraint.AllOf(Reference("foo")) to "all_of: [foo]", 23 | Constraint.AllOf(Reference("foo"), Reference("bar")) to "all_of: [foo, bar]", 24 | Constraint.AnyOf(emptySet()) to "any_of: []", 25 | Constraint.AnyOf(Reference("foo")) to "any_of: [foo]", 26 | Constraint.AnyOf(Reference("foo"), Reference("bar")) to "any_of: [foo, bar]", 27 | Constraint.OneOf(emptySet()) to "one_of: []", 28 | Constraint.OneOf(Reference("foo")) to "one_of: [foo]", 29 | Constraint.OneOf(Reference("foo"), Reference("bar")) to "one_of: [foo, bar]", 30 | Constraint.Not(Reference("foo")) to "not: foo", 31 | Constraint.Type(Reference("foo")) to "type: foo", 32 | ) 33 | ) 34 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.TypeArgument.Reference 9 | import com.amazon.ionschema.model.required 10 | 11 | @OptIn(ExperimentalIonSchemaModel::class) 12 | class OrderedElementsWriterTest : ConstraintTestBase( 13 | writer = OrderedElementsWriter(stubTypeWriterWithRefs("foo", "bar")), 14 | expectedConstraints = setOf(Constraint.OrderedElements::class), 15 | writeTestCases = listOf( 16 | Constraint.OrderedElements(emptyList()) to "ordered_elements: []", 17 | Constraint.OrderedElements(Reference("foo").required()) to "ordered_elements: [foo]", 18 | Constraint.OrderedElements( 19 | Reference("foo").required(), 20 | Reference("bar").required(), 21 | Reference("foo").required(), 22 | ) to "ordered_elements: [foo, bar, foo]", 23 | ) 24 | ) 25 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/PrecisionWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.DiscreteIntRange 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class PrecisionWriterTest : ConstraintTestBase( 12 | writer = PrecisionWriter, 13 | expectedConstraints = setOf(Constraint.Precision::class), 14 | writeTestCases = listOf( 15 | Constraint.Precision(DiscreteIntRange(2, 5)) to "precision: range::[2, 5]", 16 | Constraint.Precision(DiscreteIntRange(null, 23)) to "precision: range::[min, 23]", 17 | Constraint.Precision(DiscreteIntRange(7, null)) to "precision: range::[7, max]", 18 | Constraint.Precision(DiscreteIntRange(3, 3)) to "precision: 3", 19 | ) 20 | ) 21 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/RegexWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | 9 | @OptIn(ExperimentalIonSchemaModel::class) 10 | class RegexWriterTest : ConstraintTestBase( 11 | writer = RegexWriter, 12 | expectedConstraints = setOf(Constraint.Regex::class), 13 | writeTestCases = listOf( 14 | Constraint.Regex("abc") to """ regex: "abc" """, 15 | Constraint.Regex("abc", multiline = true) to """ regex: m::"abc" """, 16 | Constraint.Regex("abc", caseInsensitive = true) to """ regex: i::"abc" """, 17 | Constraint.Regex("abc", true, true) to """ regex: i::m::"abc" """, 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/TimestampOffsetWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint.TimestampOffset 7 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 8 | import com.amazon.ionschema.model.TimestampOffsetValue.Companion.parse 9 | 10 | @OptIn(ExperimentalIonSchemaModel::class) 11 | class TimestampOffsetWriterTest : ConstraintTestBase( 12 | writer = TimestampOffsetWriter, 13 | expectedConstraints = setOf(TimestampOffset::class), 14 | writeTestCases = listOf( 15 | TimestampOffset(emptySet()) to "timestamp_offset: []", 16 | TimestampOffset(setOf(parse("+01:23"))) to """ timestamp_offset: ["+01:23"] """, 17 | TimestampOffset(setOf(parse("+01:23"), parse("-04:56"))) to """ timestamp_offset: ["+01:23", "-04:56"] """, 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/TimestampPrecisionWriterTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ionschema.model.Constraint 7 | import com.amazon.ionschema.model.ContinuousRange.Limit.Closed 8 | import com.amazon.ionschema.model.ContinuousRange.Limit.Open 9 | import com.amazon.ionschema.model.ContinuousRange.Limit.Unbounded 10 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 11 | import com.amazon.ionschema.model.TimestampPrecisionRange 12 | import com.amazon.ionschema.model.TimestampPrecisionValue 13 | 14 | @OptIn(ExperimentalIonSchemaModel::class) 15 | class TimestampPrecisionWriterTest : ConstraintTestBase( 16 | writer = TimestampPrecisionWriter, 17 | expectedConstraints = setOf(Constraint.TimestampPrecision::class), 18 | writeTestCases = listOf( 19 | Constraint.TimestampPrecision(TimestampPrecisionRange(TimestampPrecisionValue.Second)) to "timestamp_precision: second", 20 | Constraint.TimestampPrecision(TimestampPrecisionRange(Unbounded, Closed(TimestampPrecisionValue.Day))) to "timestamp_precision: range::[min, day]", 21 | Constraint.TimestampPrecision(TimestampPrecisionRange(Open(TimestampPrecisionValue.Year), Closed(TimestampPrecisionValue.Day))) to "timestamp_precision: range::[exclusive::year, day]", 22 | Constraint.TimestampPrecision(TimestampPrecisionRange(Closed(TimestampPrecisionValue.Day), Unbounded)) to "timestamp_precision: range::[day, max]", 23 | ), 24 | ) 25 | -------------------------------------------------------------------------------- /ion-schema/src/test/kotlin/com/amazon/ionschema/writer/internal/constraints/util.kt: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package com.amazon.ionschema.writer.internal.constraints 5 | 6 | import com.amazon.ion.IonWriter 7 | import com.amazon.ion.system.IonSystemBuilder 8 | import com.amazon.ionschema.model.ExperimentalIonSchemaModel 9 | import com.amazon.ionschema.model.TypeArgument 10 | import com.amazon.ionschema.model.optional 11 | import com.amazon.ionschema.model.required 12 | import com.amazon.ionschema.writer.internal.TypeWriter 13 | import io.mockk.every 14 | import io.mockk.mockk 15 | 16 | private val ION = IonSystemBuilder.standard().build() 17 | 18 | /** Helper fun for creating IonValue instances */ 19 | fun ion(text: String) = ION.singleValue(text) 20 | 21 | /** 22 | * Creates a mock TypeWriter that can write the given type names. 23 | * This is a mock, though, and it's functionality is not complete. Does not write nullability annotations. 24 | * Does not respect the "occurs" value of any [VariablyOccurringTypeArgument]s. 25 | */ 26 | @OptIn(ExperimentalIonSchemaModel::class) 27 | internal fun stubTypeWriterWithRefs(vararg refs: String) = mockk { 28 | refs.forEach { ref -> 29 | every { writeTypeArg(any(), TypeArgument.Reference(ref)) } answers { 30 | (it.invocation.args[0] as IonWriter).writeSymbol(ref) 31 | } 32 | every { writeVariablyOccurringTypeArg(any(), TypeArgument.Reference(ref).optional(), any()) } answers { 33 | (it.invocation.args[0] as IonWriter).writeSymbol(ref) 34 | } 35 | every { writeVariablyOccurringTypeArg(any(), TypeArgument.Reference(ref).required(), any()) } answers { 36 | (it.invocation.args[0] as IonWriter).writeSymbol(ref) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ion-schema-kotlin' 2 | include 'ion-schema' 3 | include 'cli' 4 | --------------------------------------------------------------------------------