diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml
index 787c91f..92b702b 100644
--- a/ballerina/Ballerina.toml
+++ b/ballerina/Ballerina.toml
@@ -19,4 +19,4 @@ path = "../native/build/libs/data.xmldata-native-1.1.0-SNAPSHOT.jar"
groupId = "io.ballerina.stdlib"
artifactId = "constraint-native"
version = "1.6.0"
-path = "./lib/constraint-native-1.6.0-20241121-170800-002e6d6.jar"
+path = "./lib/constraint-native-1.6.0-20241122-133100-98689e2.jar"
diff --git a/ballerina/tests/test_finite_types.bal b/ballerina/tests/test_finite_types.bal
new file mode 100644
index 0000000..62bf7e4
--- /dev/null
+++ b/ballerina/tests/test_finite_types.bal
@@ -0,0 +1,214 @@
+// 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'");
+}
+
+@test:Config
+function testRecordFieldWithSingleFiniteType4() returns error? {
+ record {|
+ 200.1d|100f a;
+ 200.1d|100d b;
+ 100d|100f|200.1f c;
+ 200.1f|200.1d|100f d;
+ 200.1d|200.1f|100f e;
+ 100d|100d|200.1d f;
+ 100d|200.1d|200.1d g;
+ 200.1f|200.1d|200.1d 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});
+}
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 57d3a2f..914e96c 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
@@ -22,17 +22,20 @@
import io.ballerina.lib.data.xmldata.utils.DiagnosticLog;
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.PredefinedTypes;
import io.ballerina.runtime.api.types.ReferenceType;
import io.ballerina.runtime.api.types.Type;
import io.ballerina.runtime.api.types.TypeTags;
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 5615006..efca262 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
@@ -375,7 +375,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 19e186c..43e9ec8 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
@@ -448,7 +448,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 -> {