diff --git a/.github/workflows/build-timestamped-master.yml b/.github/workflows/build-timestamped-master.yml index 72d594a2..9204231b 100644 --- a/.github/workflows/build-timestamped-master.yml +++ b/.github/workflows/build-timestamped-master.yml @@ -14,5 +14,5 @@ jobs: call_workflow: name: Run Build Workflow if: ${{ github.repository_owner == 'ballerina-platform' }} - uses: ballerina-platform/ballerina-library/.github/workflows/build-timestamp-master-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/build-timestamp-master-template.yml@2201.10.x secrets: inherit diff --git a/.github/workflows/build-with-bal-test-graalvm.yml b/.github/workflows/build-with-bal-test-graalvm.yml index 291054d4..acbc5eab 100644 --- a/.github/workflows/build-with-bal-test-graalvm.yml +++ b/.github/workflows/build-with-bal-test-graalvm.yml @@ -20,6 +20,7 @@ on: pull_request: branches: - main + - 1.0.x types: [ opened, synchronize, reopened, labeled, unlabeled ] concurrency: @@ -30,7 +31,7 @@ jobs: call_stdlib_workflow: name: Run StdLib Workflow if: ${{ github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository_owner == 'ballerina-platform') }} - uses: ballerina-platform/ballerina-library/.github/workflows/build-with-bal-test-graalvm-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/build-with-bal-test-graalvm-template.yml@2201.10.x with: lang_tag: ${{ inputs.lang_tag }} lang_version: ${{ inputs.lang_version }} diff --git a/.github/workflows/central-publish.yml b/.github/workflows/central-publish.yml index 11922b55..74c48557 100644 --- a/.github/workflows/central-publish.yml +++ b/.github/workflows/central-publish.yml @@ -15,7 +15,7 @@ jobs: call_workflow: name: Run Central Publish Workflow if: ${{ github.repository_owner == 'ballerina-platform' }} - uses: ballerina-platform/ballerina-library/.github/workflows/central-publish-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/central-publish-template.yml@2201.10.x secrets: inherit with: environment: ${{ github.event.inputs.environment }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index c6044e4c..4488b8fd 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -9,7 +9,7 @@ jobs: call_workflow: name: Run Release Workflow if: ${{ github.repository_owner == 'ballerina-platform' }} - uses: ballerina-platform/ballerina-library/.github/workflows/release-package-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/release-package-template.yml@2201.10.x secrets: inherit with: package-name: data.xmldata diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 06f4f569..7b455672 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -10,5 +10,5 @@ jobs: call_workflow: name: Run PR Build Workflow if: ${{ github.repository_owner == 'ballerina-platform' }} - uses: ballerina-platform/ballerina-library/.github/workflows/pull-request-build-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/pull-request-build-template.yml@2201.10.x secrets: inherit diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index c02c8ff4..c29d8f2a 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -9,5 +9,5 @@ jobs: call_workflow: name: Run Trivy Scan Workflow if: ${{ github.repository_owner == 'ballerina-platform' }} - uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@main + uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@2201.10.x secrets: inherit diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 65c23573..55e32b53 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "data.xmldata" -version = "1.0.0" +version = "1.1.0" authors = ["Ballerina"] keywords = ["xml"] repository = "https://github.com/ballerina-platform/module-ballerina-data-xmldata" @@ -12,8 +12,8 @@ export = ["data.xmldata"] [[platform.java17.dependency]] groupId = "io.ballerina.lib" artifactId = "data-native" -version = "1.0.0" -path = "../native/build/libs/data.xmldata-native-1.0.0.jar" +version = "1.1.0" +path = "../native/build/libs/data.xmldata-native-1.1.0-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 1669a303..2eb00ce5 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "constraint-compiler-plugin" class = "io.ballerina.lib.data.xmldata.compiler.XmldataCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/data.xmldata-compiler-plugin-1.0.0.jar" +path = "../compiler-plugin/build/libs/data.xmldata-compiler-plugin-1.1.0-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 0180731c..b3e73938 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -22,7 +22,7 @@ modules = [ [[package]] org = "ballerina" name = "data.xmldata" -version = "1.0.0" +version = "1.1.0" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "io"}, diff --git a/ballerina/tests/test_finite_types.bal b/ballerina/tests/test_finite_types.bal new file mode 100644 index 00000000..6b6a0707 --- /dev/null +++ b/ballerina/tests/test_finite_types.bal @@ -0,0 +1,199 @@ +// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; + +enum EnumA { + A = "A", + B, + C = "C2" +} + +type FiniteType true|"A"|1|2; + +@test:Config +function testFiniteTypes() returns error? { + record {| + EnumA a; + "A"|"B"|"C" b; + record {| + "A"|"B"|"C" c; + EnumA d; + |} nested; + |} r = check parseAsType(xml `ABBB`); + test:assertEquals(r, {a: "A", b: "B", nested: {c: "B", d: "B"}}); +} + +@test:Config +function testFiniteTypesWithNestedRecords() returns error? { + record {| + EnumA a; + FiniteType b; + record {| + FiniteType c; + FiniteType e; + EnumA d; + |} nested; + |} r = check parseAsType(xml `C212Atrue`); + test:assertEquals(r, {a: "C2", b: 1, nested: {c: 2, d: "A", e: true}}); +} + +@test:Config +function testFiniteTypeArrays() returns error? { + record {| + EnumA[] a; + ("A"|"B"|"C")[] b; + record {| + ("A"|"B"|"C")[] c; + EnumA[] d; + |} nested; + |} r = check parseAsType(xml `AABBBBBB`); + test:assertEquals(r, {a: ["A", "A"], b: ["B", "B"], nested: {c: ["B", "B"], d: ["B", "B"]}}); +} + +@test:Config +function testFiniteTypeArrays2() returns error? { + record {| + EnumA[] a; + FiniteType[] b; + record {| + FiniteType[] c; + FiniteType[] e; + EnumA[] d; + |} nested; + |} r = check parseAsType(xml `C2C21122AAtruetrue`); + test:assertEquals(r, {a: ["C2", "C2"], b: [1, 1], nested: {c: [2, 2], d: ["A", "A"], e: [true, true]}}); +} + +@test:Config +function testFiniteTypesWithXmlString() returns error? { + record {| + EnumA a; + "A"|"B"|"C" b; + record {| + "A"|"B"|"C" c; + EnumA d; + |} nested; + |} r = check parseString(string `ABBB`); + test:assertEquals(r, {a: "A", b: "B", nested: {c: "B", d: "B"}}); +} + +@test:Config +function testFiniteTypesWithNestedRecordsWithXmlString() returns error? { + record {| + EnumA a; + FiniteType b; + record {| + FiniteType c; + FiniteType e; + EnumA d; + |} nested; + |} r = check parseString(string `C212Atrue`); + test:assertEquals(r, {a: "C2", b: 1, nested: {c: 2, d: "A", e: true}}); +} + +@test:Config +function testFiniteTypeArraysWithXmlString() returns error? { + record {| + EnumA[] a; + ("A"|"B"|"C")[] b; + record {| + ("A"|"B"|"C")[] c; + EnumA[] d; + |} nested; + |} r = check parseString(string `AABBBBBB`); + test:assertEquals(r, {a: ["A", "A"], b: ["B", "B"], nested: {c: ["B", "B"], d: ["B", "B"]}}); +} + +@test:Config +function testFiniteTypeArraysWithXmlString2() returns error? { + record {| + EnumA[] a; + FiniteType[] b; + record {| + FiniteType[] c; + FiniteType[] e; + EnumA[] d; + |} nested; + |} r = check parseString(string `C2C21122AAtruetrue`); + test:assertEquals(r, {a: ["C2", "C2"], b: [1, 1], nested: {c: [2, 2], d: ["A", "A"], e: [true, true]}}); +} +type NestedRec record {| + @Name { + value: "c2" + } + FiniteType c; + FiniteType e; + @Name { + value: "d2" + } + EnumA d; +|}; + +@test:Config +function testFiniteTypesWithNameAnnotations() returns error? { + record {| + EnumA a; + FiniteType b; + NestedRec nested; + |} r = check parseAsType(xml `C212Atrue`); + test:assertEquals(r, {a: "C2", b: 1, nested: {c: 2, d: "A", e: true}}); +} + +type FiniteValue 100f; + +@test:Config +function testRecordFieldWithSingleFiniteType() returns error? { + record {| + EnumA a; + "A" b; + record {| + FiniteValue c; + EnumA d; + |} nested; + |} r = check parseAsType(xml `AA100.0B`); + test:assertEquals(r, {a: "A", b: "A", nested: {c: 100f, d: "B"}}); +} + +@test:Config +function testRecordFieldWithSingleFiniteType2() returns error? { + record {| + 100f a; + 200.1d b; + 100d c; + 200.1f d; + 100f e; + 200.1d f; + 100d g; + 200.1f h; + |} r = check parseAsType(xml `100200.1100200.1100.0200.1100.0200.1`); + test:assertEquals(r, {a: 100f, b: 200.1d, c: 100d, d: 200.1f, e: 100f, f: 200.1d, g: 100d, h: 200.1f}); +} + +@test:Config +function testRecordFieldWithSingleFiniteType3() returns error? { + record {| + 100f a; + |}|Error r = parseAsType(xml `100.01`); + test:assertTrue(r is Error); + test:assertEquals((r).message(), "'string' value '100.01' cannot be converted to '100.0f'"); + + record {| + 100d a; + |}|Error r2 = parseAsType(xml `100.01`); + test:assertTrue(r2 is Error); + test:assertEquals((r2).message(), "'string' value '100.01' cannot be converted to '100d'"); +} diff --git a/gradle.properties b/gradle.properties index 34a6b716..e66aae1f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.caching=true group=io.ballerina.lib -version=1.0.0 +version=1.1.0-SNAPSHOT ballerinaLangVersion=2201.10.0 checkstyleToolVersion=10.12.0 diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/FromString.java b/native/src/main/java/io/ballerina/lib/data/xmldata/FromString.java index d48b9425..22d9a862 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/FromString.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/FromString.java @@ -24,15 +24,18 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BTypedesc; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -96,6 +99,8 @@ public static Object fromStringWithType(BString string, Type expType) { return stringToUnion(string, JSON_TYPE_WITH_BASIC_TYPES); case TypeTags.TYPE_REFERENCED_TYPE_TAG: return fromStringWithType(string, ((ReferenceType) expType).getReferredType()); + case TypeTags.FINITE_TYPE_TAG: + return stringToFiniteType(value, (FiniteType) expType); default: return returnError(value, expType.toString()); } @@ -104,6 +109,38 @@ public static Object fromStringWithType(BString string, Type expType) { } } + private static Object stringToFiniteType(String value, FiniteType finiteType) { + return finiteType.getValueSpace().stream() + .filter(finiteValue -> !(convertToSingletonValue(value, finiteValue) instanceof BError)) + .findFirst() + .orElseGet(() -> returnError(value, finiteType.toString())); + } + + private static Object convertToSingletonValue(String str, Object singletonValue) { + String singletonStr = String.valueOf(singletonValue); + Type type = TypeUtils.getType(singletonValue); + + if (singletonValue instanceof BDecimal decimalValue) { + BigDecimal bigDecimal = decimalValue.decimalValue(); + if (bigDecimal.compareTo(new BigDecimal(str)) == 0) { + return fromStringWithType(StringUtils.fromString(str), type); + } + return returnError(str, singletonStr); + } + + if (singletonValue instanceof Double doubleValue) { + if (doubleValue.compareTo(Double.valueOf(str)) == 0) { + return fromStringWithType(StringUtils.fromString(str), type); + } + return returnError(str, singletonStr); + } + + if (str.equals(singletonStr)) { + return fromStringWithType(StringUtils.fromString(str), type); + } + return returnError(str, singletonStr); + } + private static Long stringToInt(String value) throws NumberFormatException { return Long.parseLong(value); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java index 5e59ab97..1f7f8e68 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java @@ -316,7 +316,7 @@ public static boolean isSupportedType(Type type) { switch (type.getTag()) { case TypeTags.NULL_TAG, TypeTags.INT_TAG, TypeTags.BYTE_TAG, TypeTags.FLOAT_TAG, TypeTags.DECIMAL_TAG, TypeTags.BOOLEAN_TAG, TypeTags.STRING_TAG, TypeTags.RECORD_TYPE_TAG, TypeTags.MAP_TAG, - TypeTags.JSON_TAG, TypeTags.ANYDATA_TAG -> { + TypeTags.JSON_TAG, TypeTags.ANYDATA_TAG, TypeTags.FINITE_TYPE_TAG -> { return true; } case TypeTags.ARRAY_TAG -> { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java index 8f36c4fa..71a33e00 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java @@ -428,7 +428,7 @@ private Object convertStringToRestExpType(BString value, Type expType) { return convertStringToRestExpType(value, ((ArrayType) expType).getElementType()); } case TypeTags.INT_TAG, TypeTags.FLOAT_TAG, TypeTags.DECIMAL_TAG, TypeTags.STRING_TAG, - TypeTags.BOOLEAN_TAG, TypeTags.UNION_TAG -> { + TypeTags.BOOLEAN_TAG, TypeTags.UNION_TAG, TypeTags.FINITE_TYPE_TAG -> { return convertStringToExpType(value, expType); } case TypeTags.ANYDATA_TAG, TypeTags.JSON_TAG -> {