Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement enum singleton support #56

2 changes: 1 addition & 1 deletion ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
214 changes: 214 additions & 0 deletions ballerina/tests/test_finite_types.bal
Original file line number Diff line number Diff line change
@@ -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;

SasinduDilshara marked this conversation as resolved.
Show resolved Hide resolved
@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 `<Root><a>A</a><b>B</b><nested><c>B</c><d>B</d></nested></Root>`);
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 `<Root><a>C2</a><b>1</b><nested><c>2</c><d>A</d><e>true</e></nested></Root>`);
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 `<Root><a>A</a><a>A</a><b>B</b><b>B</b><nested><c>B</c><c>B</c><d>B</d><d>B</d></nested></Root>`);
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 `<Root><a>C2</a><a>C2</a><b>1</b><b>1</b><nested><c>2</c><c>2</c><d>A</d><d>A</d><e>true</e><e>true</e></nested></Root>`);
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 `<Root><a>A</a><b>B</b><nested><c>B</c><d>B</d></nested></Root>`);
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 `<Root><a>C2</a><b>1</b><nested><c>2</c><d>A</d><e>true</e></nested></Root>`);
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 `<Root><a>A</a><a>A</a><b>B</b><b>B</b><nested><c>B</c><c>B</c><d>B</d><d>B</d></nested></Root>`);
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 `<Root><a>C2</a><a>C2</a><b>1</b><b>1</b><nested><c>2</c><c>2</c><d>A</d><d>A</d><e>true</e><e>true</e></nested></Root>`);
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 `<Root><a>C2</a><b>1</b><nested><c2>2</c2><d2>A</d2><e>true</e></nested></Root>`);
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 `<Root><a>A</a><b>A</b><nested><c>100.0</c><d>B</d></nested></Root>`);
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 `<Root><a>100</a><b>200.1</b><c>100</c><d>200.1</d><e>100.0</e><f>200.1</f><g>100.0</g><h>200.1</h></Root>`);
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 `<Root><a>100.01</a></Root>`);
test:assertTrue(r is Error);
test:assertEquals((<Error>r).message(), "'string' value '100.01' cannot be converted to '100.0f'");

record {|
100d a;
|}|Error r2 = parseAsType(xml `<Root><a>100.01</a></Root>`);
test:assertTrue(r2 is Error);
test:assertEquals((<Error>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 `<Root><a>100</a><b>200.1</b><c>100</c><d>200.1</d><e>100.0</e><f>200.1</f><g>100.0</g><h>200.1</h></Root>`);
test:assertEquals(r, {a: 100f, b: 200.1d, c: 100d, d: 200.1f, e: 100f, f: 200.1d, g: 100d, h: 200.1f});
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
Expand All @@ -104,6 +109,38 @@ public static Object fromStringWithType(BString string, Type expType) {
}
}

SasinduDilshara marked this conversation as resolved.
Show resolved Hide resolved
private static Object stringToFiniteType(String value, FiniteType finiteType) {
return finiteType.getValueSpace().stream()
SasinduDilshara marked this conversation as resolved.
Show resolved Hide resolved
.filter(finiteValue -> !(convertToSingletonValue(value, finiteValue) instanceof BError))
SasinduDilshara marked this conversation as resolved.
Show resolved Hide resolved
.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);
}
SasinduDilshara marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
Expand Down
Loading