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

Add enum and finite type support for 1.0.x #58

Merged
merged 9 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-timestamped-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion .github/workflows/build-with-bal-test-graalvm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ on:
pull_request:
branches:
- main
- 1.0.x
types: [ opened, synchronize, reopened, labeled, unlabeled ]

concurrency:
Expand All @@ -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 }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/central-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
2 changes: 1 addition & 1 deletion .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .github/workflows/trivy-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 3 additions & 3 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
199 changes: 199 additions & 0 deletions ballerina/tests/test_finite_types.bal
Original file line number Diff line number Diff line change
@@ -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 `<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'");
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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) {
}
}

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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
Expand Down
Loading