From d95e3f68d5430c55c7baa3f422c8e84cfa9022d3 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 26 Oct 2024 22:09:27 +0530 Subject: [PATCH 01/58] Add xsd validation for xsd file paths --- .../resources/xsd_tests/schemas/schema_1.xsd | 3 + .../resources/xsd_tests/schemas/schema_2.xsd | 16 ++++ .../resources/xsd_tests/schemas/schema_3.xsd | 11 +++ .../resources/xsd_tests/schemas/schema_4.xsd | 10 +++ .../resources/xsd_tests/schemas/schema_5.xsd | 10 +++ .../xml_values/schema_1_invalid_xml.xml | 1 + .../xml_values/schema_1_valid_xml.xml | 1 + .../xml_values/schema_2_invalid_xml.xml | 5 ++ .../xml_values/schema_2_valid_xml.xml | 6 ++ .../xml_values/schema_3_invalid_xml.xml | 4 + .../xml_values/schema_3_valid_xml.xml | 4 + .../xml_values/schema_4_invalid_xml.xml | 4 + .../xml_values/schema_4_valid_xml.xml | 3 + .../xml_values/schema_5_invalid_xml.xml | 3 + .../xml_values/schema_5_valid_xml.xml | 4 + .../xsd_validation_with_file_path_tests.bal | 86 +++++++++++++++++++ ballerina/xml_api.bal | 25 ++++++ .../lib/data/xmldata/xml/Native.java | 8 ++ .../lib/data/xmldata/xml/XSDValidator.java | 68 +++++++++++++++ 19 files changed, 272 insertions(+) create mode 100644 ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd create mode 100644 ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd create mode 100644 ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd create mode 100644 ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd create mode 100644 ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml create mode 100644 ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml create mode 100644 ballerina/tests/xsd_validation_with_file_path_tests.bal create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd new file mode 100644 index 00000000..ef37fa2e --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd new file mode 100644 index 00000000..7d48ea11 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd new file mode 100644 index 00000000..85d1ffe1 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd new file mode 100644 index 00000000..eb70b67c --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd new file mode 100644 index 00000000..bed75459 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml new file mode 100644 index 00000000..a83247ff --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml @@ -0,0 +1 @@ +Sample \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml new file mode 100644 index 00000000..22ea32bc --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml @@ -0,0 +1 @@ +Sample \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml new file mode 100644 index 00000000..2093bc22 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml @@ -0,0 +1,5 @@ + + + Sample Title + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml new file mode 100644 index 00000000..af52a961 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml @@ -0,0 +1,6 @@ + + + Sample Title + Sample Author + + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml new file mode 100644 index 00000000..cf0bbd0c --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml @@ -0,0 +1,4 @@ + + John Doe + 30 + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml new file mode 100644 index 00000000..eeeb6cda --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml @@ -0,0 +1,4 @@ + + John Doe + 30 + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml new file mode 100644 index 00000000..5dfb6647 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml @@ -0,0 +1,4 @@ + + Toyota + Yamaha + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml new file mode 100644 index 00000000..ff16f172 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml @@ -0,0 +1,3 @@ + + Toyota + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml new file mode 100644 index 00000000..86a091b5 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml @@ -0,0 +1,3 @@ + + 12345 + \ No newline at end of file diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml new file mode 100644 index 00000000..8310ec08 --- /dev/null +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml @@ -0,0 +1,4 @@ + + 12345 + Laptop + \ No newline at end of file diff --git a/ballerina/tests/xsd_validation_with_file_path_tests.bal b/ballerina/tests/xsd_validation_with_file_path_tests.bal new file mode 100644 index 00000000..259533d6 --- /dev/null +++ b/ballerina/tests/xsd_validation_with_file_path_tests.bal @@ -0,0 +1,86 @@ +import ballerina/test; +import ballerina/io; + +function readXmlFile(string path) returns xml|error { + return io:fileReadXml(path); +} + +@test:Config {groups: ["xsd"]} +function testValidateSchema1() returns error? { + string xsdPath = "tests/resources/xsd_tests/schemas/schema_1.xsd"; + string validXmlPath = "tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml"; + string invalidXmlPath = "tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml"; + + xml validXml = check readXmlFile(validXmlPath); + xml invalidXml = check readXmlFile(invalidXmlPath); + + boolean isValid = validate(xsdPath, validXml); + test:assertTrue(isValid, msg = "Valid XML should pass validation"); + + boolean isInvalid = validate(xsdPath, invalidXml); + test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); +} + +@test:Config {groups: ["xsd"]} +function testValidateSchema2() returns error? { + string xsdPath = "tests/resources/xsd_tests/schemas/schema_2.xsd"; + string validXmlPath = "tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml"; + string invalidXmlPath = "tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml"; + + xml validXml = check readXmlFile(validXmlPath); + xml invalidXml = check readXmlFile(invalidXmlPath); + + boolean isValid = validate(xsdPath, validXml); + test:assertTrue(isValid, msg = "Valid XML should pass validation"); + + boolean isInvalid = validate(xsdPath, invalidXml); + test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); +} + +@test:Config {groups: ["xsd"]} +function testValidateSchema3() returns error? { + string xsdPath = "tests/resources/xsd_tests/schemas/schema_3.xsd"; + string validXmlPath = "tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml"; + string invalidXmlPath = "tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml"; + + xml validXml = check readXmlFile(validXmlPath); + xml invalidXml = check readXmlFile(invalidXmlPath); + + boolean isValid = validate(xsdPath, validXml); + test:assertTrue(isValid, msg = "Valid XML should pass validation"); + + boolean isInvalid = validate(xsdPath, invalidXml); + test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); +} + +@test:Config {groups: ["xsd"]} +function testValidateSchema4() returns error? { + string xsdPath = "tests/resources/xsd_tests/schemas/schema_4.xsd"; + string validXmlPath = "tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml"; + string invalidXmlPath = "tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml"; + + xml validXml = check readXmlFile(validXmlPath); + xml invalidXml = check readXmlFile(invalidXmlPath); + + boolean isValid = validate(xsdPath, validXml); + test:assertTrue(isValid, msg = "Valid XML should pass validation"); + + boolean isInvalid = validate(xsdPath, invalidXml); + test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); +} + +@test:Config {groups: ["xsd"]} +function testValidateSchema5() returns error? { + string xsdPath = "tests/resources/xsd_tests/schemas/schema_5.xsd"; + string validXmlPath = "tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml"; + string invalidXmlPath = "tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml"; + + xml validXml = check readXmlFile(validXmlPath); + xml invalidXml = check readXmlFile(invalidXmlPath); + + boolean isValid = validate(xsdPath, validXml); + test:assertTrue(isValid, msg = "Valid XML should pass validation"); + + boolean isInvalid = validate(xsdPath, invalidXml); + test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); +} diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index da4a4a35..46369757 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -428,3 +428,28 @@ isolated function addNamespaces(map allNamespaces, map namespace allNamespaces['key] = namespace; } } + +# Validates an XML document against a provided XML schema. +# +# The schema can either be a content of a XSD (as a `string`) or a Ballerina record type that represents +# the XSD structure. The function checks if the `xmlValue` conforms to the provided schema. +# +# + schema - A `string` representing the XSD content or a Ballerina record type representing the XSD. +# + xmlValue - The XML document that needs to be validated against the schema. +# + return - Returns `true` if the XML is valid according to the schema, otherwise returns `false`. +# +# # Examples +# +# ```ballerina +# string xsdContent = string ` +# +# `; +# xml bookXml = xml `Sample`; +# boolean isValid = validate(xsdContent, bookXml); +# +# // Using Ballerina record to represent XSD +# type xsdRecord record {string name;}; +# boolean isValid = validate(xsdRecord, bookXml); +# ``` +public function validate(string|typedesc schema, xml xmlValue) + returns boolean = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java index 8eda9f97..8e1f3ee7 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java @@ -81,4 +81,12 @@ public static Object parseStream(Environment env, BStream xml, BMap) xsd); + } + + private static boolean validateXmlFromXsdFile(String xsdFilePath, BXml xml) { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + dbFactory.setIgnoringComments(true); // Ignore comments in the XML + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document document = dBuilder.parse(new InputSource(new StringReader(StringUtils.getStringValue(xml)))); + + DOMSource source = new DOMSource(document); + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = factory.newSchema(new File(xsdFilePath)); + Validator validator = schema.newValidator(); + validator.validate(source); + return true; + } catch (Exception e) { + return false; + } + } + + private static boolean validateXsdFromXsdRecord(BMap xsdRecord) { + return false; + } +} From 43b97119061d5be18043a7d9a9b2871b21b9208d Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 6 Nov 2024 16:09:50 +0530 Subject: [PATCH 02/58] Add initial sequence --- ballerina/tests/xsd_sequence_tests.bal | 30 ++++ ballerina/xml_api.bal | 36 +++++ .../lib/data/xmldata/utils/Constants.java | 6 + .../lib/data/xmldata/xml/XmlParser.java | 143 ++++++++++++++++++ .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 75 +++++++++ .../lib/data/xmldata/xml/xsd/ElementInfo.java | 41 +++++ .../data/xmldata/xml/xsd/ModelGroupInfo.java | 12 ++ .../data/xmldata/xml/xsd/SequenceInfo.java | 94 ++++++++++++ 8 files changed, 437 insertions(+) create mode 100644 ballerina/tests/xsd_sequence_tests.bal create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal new file mode 100644 index 00000000..8aeb7c57 --- /dev/null +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -0,0 +1,30 @@ +import ballerina/test; + +type XSDSequenceRecord record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord seq_XSDSequenceRecord; +|}; + +type Seq_XSDSequenceRecord record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config +function testXsdSequence() returns error? { + string xmlStr = string `1311.1`; + XSDSequenceRecord v = check parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecord: {age: 13, salary: 11.1}}); + test:assertEquals(v.seq_XSDSequenceRecord.age, 13); + test:assertEquals(v.seq_XSDSequenceRecord.salary, 11.1); +} diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 46369757..456bf308 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -22,6 +22,42 @@ const ATTRIBUTE_PREFIX = "attribute_"; const XMLNS = "xmlns"; const EMPTY_STRING = ""; +public type ParticleOccurrence record {| + # Specifies the minimum number of occurrences. + int:Unsigned32 minOccurs?; + # Specifies the maximum number of occurrences. + int:Unsigned32 maxOccurs?; +|}; + +# Defines the configuration for an XML element in the XML schema (XSD). +public type ElementConfig record {| + *ParticleOccurrence; + # Represents the `form` attribute in the XML schema (XSD) `element` definition. + "qualified"|"unqulified" form = "qualified"; + # Specifies the id of the substitution group for the element. + string substitutionGroupId?; + # Indicates whether the element is abstract. + boolean 'abstract = false; + # Restricts how the element can be extended or restricted. + "extension"|"restriction"|"all"|"substitution" block?; + # Controls whether the element can be further extended or restricted. + "extension"|"restriction"|"all" 'final?; +|}; +# Annotation to define schema rules for an XML element in Ballerina. +public const annotation ElementConfig Element on type, record field; + +public type SequenceConfig record {| + *ParticleOccurrence; +|}; + +public const annotation SequenceConfig Sequence on type, record field; + +public type SequenceOrderConfig record {| + int value; +|}; + +public const annotation SequenceOrderConfig Order on type, record field; + # Defines the name of the XML element. public type NameConfig record {| # The name of the XML element diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java index 146accc8..1d1209d8 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java @@ -66,4 +66,10 @@ private Constants() {} public static final MapType JSON_MAP_TYPE = TypeCreator.createMapType(PredefinedTypes.TYPE_JSON); public static final ArrayType JSON_ARRAY_TYPE = TypeCreator.createArrayType(PredefinedTypes.TYPE_JSON); + + public static final String ELEMENT = "Element"; + public static final String SEQUENCE = "Sequence"; + public static final String CHOICE = "Choice"; + public static final BString MIN_OCCURS = StringUtils.fromString("minOccurs"); + public static final BString MAX_OCCURS = StringUtils.fromString("maxOccurs"); } 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 0ed318e9..00386cdb 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 @@ -23,6 +23,12 @@ import io.ballerina.lib.data.xmldata.utils.DataUtils; import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; +import io.ballerina.lib.data.xmldata.xml.xsd.ChoiceInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; +import io.ballerina.runtime.api.PredefinedTypes; +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.flags.SymbolFlags; @@ -259,6 +265,7 @@ private void parseRootElement(XMLStreamReader xmlStreamReader, // Keep track of fields and attributes xmlParserData.recordTypeStack.push(rootRecord); updateExpectedTypeStacks(rootRecord, xmlParserData); + initializeXsdInformation(rootRecord, xmlParserData); handleAttributes(xmlStreamReader, xmlParserData); xmlParserData.arrayIndexes.push(new HashMap<>()); } @@ -461,6 +468,16 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); QualifiedNameMap siblings = xmlParserData.siblings; Stack> parents = xmlParserData.parents; + + if (!xmlParserData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + if (modelGroup.isCompleted()) { + modelGroup.validate(); + xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); + xmlParserData.modelGroupStack.pop(); + } + } + if (siblings.contains(elemQName) && !siblings.get(elemQName)) { siblings.put(elemQName, true); } @@ -502,6 +519,22 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse } xmlParserData.currentField = currentField; if (currentField == null) { + // TODO: In here assume that if XSD is present then no rest type can be there. + HashMap choiceInfo = null; + HashMap sequenceInfo = null; + if (!xmlParserData.xsdChoiceInfo.isEmpty()) { + choiceInfo = xmlParserData.xsdChoiceInfo.peek(); + } + if (!xmlParserData.xsdSequenceInfo.isEmpty()) { + sequenceInfo = xmlParserData.xsdSequenceInfo.peek(); + } + + if ((sequenceInfo != null && !sequenceInfo.isEmpty()) || (choiceInfo != null && !choiceInfo.isEmpty())) { + validateElementInXsdSequenceOrElement(elemQName, choiceInfo, sequenceInfo, + xmlStreamReader, xmlParserData, xmlParserData.visitedFieldHierarchy.peek(), fieldMap); + return; + } + String elemName = elemQName.getLocalPart(); if (xmlParserData.restTypes.peek() != null) { if (fieldMap.contains(elemName)) { @@ -539,9 +572,68 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse throw DiagnosticLog.error(DiagnosticErrorCode.UNSUPPORTED_TYPE); } + if (!xmlParserData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + if (modelGroup.isElementContains(elemQName.getLocalPart())) { + modelGroup.visit(elemQName.getLocalPart()); + } + } initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); } + private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, + HashMap sequenceInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, + QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { + if (sequenceInfo != null) { + sequenceInfo.forEach((key, sequence) -> { + // TODO: Validate Namespaces + if (sequence.isElementContains(elemQName.getLocalPart())) { + xmlParserData.modelGroupStack.push(sequence); + Field field = xmlParserData.rootRecord.getFields().get(key); + // TODO: Test on arrays + fieldMap.remove(QualifiedNameFactory.createQualifiedName( + "", key, "", xmlParserData.useSemanticEquality)); + + // TODO: temp != null for arrays and currentNode + // TODO: Check for arrays as well + Type referredType = TypeUtils.getReferredType(field.getFieldType()); + updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); + updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); + readElement(xmlStreamReader, xmlParserData); + int a = 1; + return; + } + }); + + choiceInfo.forEach((key, choice) -> { + // TODO: Validate Namespaces + if (choice.isElementContains(elemQName.getLocalPart())) { + xmlParserData.modelGroupStack.push(choice); + Field field = xmlParserData.rootRecord.getFields().get(key); + // TODO: Test on arrays + fieldMap.remove(QualifiedNameFactory.createQualifiedName( + "", key, "", xmlParserData.useSemanticEquality)); + + // TODO: temp != null for arrays and currentNode + // TODO: Check for arrays as well + Type referredType = TypeUtils.getReferredType(field.getFieldType()); + updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); + updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); + readElement(xmlStreamReader, xmlParserData); + int a = 1; + return; + } + }); + } + } + + private void updateNextRecordForXsd(XmlParserData xmlParserData, String fieldName, + Type fieldType, RecordType recordType) { + xmlParserData.recordTypeStack.push(xmlParserData.rootRecord); + xmlParserData.rootRecord = recordType; + xmlParserData.currentNode = updateNextValue(recordType, fieldName, fieldType, xmlParserData); + } + private void initializeNextValueBasedOnExpectedType(String fieldName, Type fieldType, Object temp, BMap currentNode, XmlParserData xmlParserData) { @@ -657,6 +749,7 @@ private BMap updateNextValue(RecordType rootRecord, String fiel XmlParserData xmlParserData) { BMap nextValue = ValueCreator.createRecordValue(rootRecord.getPackage(), rootRecord.getName()); updateExpectedTypeStacks(rootRecord, xmlParserData); + initializeXsdInformation(rootRecord, xmlParserData); BMap currentNode = xmlParserData.currentNode; Object temp = currentNode.get(StringUtils.fromString(fieldName)); if (temp instanceof BArray) { @@ -681,6 +774,8 @@ private void updateExpectedTypeStacks(RecordType recordType, XmlParserData xmlPa xmlParserData.fieldHierarchy.push(new QualifiedNameMap<>(getAllFieldsInRecordType(recordType, xmlParserData))); xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.restTypes.push(recordType.getRestFieldType()); + xmlParserData.xsdChoiceInfo.push(new HashMap<>()); + xmlParserData.xsdSequenceInfo.push(new HashMap<>()); } private void popExpectedTypeStacks(XmlParserData xmlParserData) { @@ -1133,6 +1228,50 @@ private QualifiedName getElementName(XMLStreamReader xmlStreamReader, boolean us qName.getPrefix(), ELEMENT, useSemanticEquality); } + private void initializeXsdInformation(RecordType recordType, XmlParserData parserData) { + BMap annotations = recordType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey: fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + BMap fieldAnnotationValue = null; + if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { + fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + parserData.xmlElementInfo.peek().put(fieldName, + new ElementInfo(fieldName, fieldAnnotationValue)); + } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = TypeUtils.getReferredType(recordType.getFields() + .get(fieldName).getFieldType()); + if (fieldType instanceof RecordType recType) { + parserData.xsdSequenceInfo.peek().put(fieldName, + new SequenceInfo(fieldName, fieldAnnotationValue, recType)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = recordType.getFields() + .get(StringUtils.fromString(fieldName)).getFieldType(); + if (fieldType instanceof RecordType recType) { + parserData.xsdChoiceInfo.peek().put(fieldName, + new ChoiceInfo(fieldName, fieldAnnotationValue, recType)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } + } + } + } + } + } + /** * Represents the content of an XML element. * @@ -1167,5 +1306,9 @@ public static class XmlParserData { private String textFieldName; private boolean allowDataProjection; private boolean useSemanticEquality; + Stack> xmlElementInfo = new Stack<>(); + Stack> xsdSequenceInfo = new Stack<>(); + Stack> xsdChoiceInfo = new Stack<>(); + Stack modelGroupStack = new Stack<>(); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java new file mode 100644 index 00000000..caedbb82 --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -0,0 +1,75 @@ +package io.ballerina.lib.data.xmldata.xml.xsd; + +import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.lib.data.xmldata.xml.QualifiedName; +import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import java.util.Map; + +public class ChoiceInfo implements ModelGroupInfo { + public String fieldName; + public long minOccurs; + public long maxOccurs; + public int occurrences = 0; + + // TODO: Update to a hashset + public Map elements; + + + public ChoiceInfo(String fieldName, BMap element, Type fieldType) { + this.fieldName = fieldName; + if (element.containsKey(Constants.MIN_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); + } else { + this.minOccurs = 1; + } + + if (element.containsKey(Constants.MAX_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + } else { + this.maxOccurs = Math.max(this.minOccurs, 1); + } + this.occurrences = 1; + } + + public void updateOccurrences() { + this.occurrences++; + if (this.occurrences > this.maxOccurs) { + throw new RuntimeException(fieldName + " Element occurs more than the max allowed times"); + } + } + + public void validateMinOccurrences() { + if (this.occurrences < this.minOccurs) { + throw new RuntimeException(fieldName + " Element occurs less than the min required times"); + } + } + + @Override + public void validate() { + + } + + @Override + public void reset() { + + } + + @Override + public void visit(String element) { + + } + + @Override + public boolean isCompleted() { + return false; + } + + @Override + public boolean isElementContains(String elementName) { + return false; + } +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java new file mode 100644 index 00000000..f3a6e986 --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -0,0 +1,41 @@ +package io.ballerina.lib.data.xmldata.xml.xsd; + +import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +public class ElementInfo { + String name; + public long minOccurs; + public long maxOccurs; + public int occurrences = 0; + + public ElementInfo(String name, BMap element) { + this.name = name; + if (element.containsKey(Constants.MIN_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); + } else { + this.minOccurs = 1; + } + + if (element.containsKey(Constants.MAX_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + } else { + this.maxOccurs = Math.max(this.minOccurs, 1); + } + this.occurrences = 1; + } + + public void updateOccurrences() { + this.occurrences++; + if (this.occurrences > this.maxOccurs) { + throw new RuntimeException(name + " Element occurs more than the max allowed times"); + } + } + + public void validateMinOccurrences() { + if (this.occurrences < this.minOccurs) { + throw new RuntimeException(name + " Element occurs less than the min required times"); + } + } +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java new file mode 100644 index 00000000..d7d10c1c --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -0,0 +1,12 @@ +package io.ballerina.lib.data.xmldata.xml.xsd; + +public interface ModelGroupInfo { + public void updateOccurrences(); + public void validateMinOccurrences(); + public void validate(); + public void reset(); + + public void visit(String element); + public boolean isCompleted(); + public boolean isElementContains(String elementName); +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java new file mode 100644 index 00000000..6cf701d1 --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -0,0 +1,94 @@ +package io.ballerina.lib.data.xmldata.xml.xsd; + +import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import java.util.HashSet; +import java.util.Set; + +public class SequenceInfo implements ModelGroupInfo { + public String fieldName; + public long minOccurs; + public long maxOccurs; + public int occurrences; + + // TODO: Update to a hashset + public Set unvisitedElements = new HashSet<>(); + public Set visitedElements = new HashSet<>(); + public Set allElements = new HashSet<>(); + + + public SequenceInfo(String fieldName, BMap element, RecordType fieldType) { + this.fieldName = fieldName; + if (element.containsKey(Constants.MIN_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); + } else { + this.minOccurs = 1; + } + + if (element.containsKey(Constants.MAX_OCCURS)) { + this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + } else { + this.maxOccurs = Math.max(this.minOccurs, 1); + } + this.occurrences = 0; + + // TODO: Name Annotation not encountered + this.allElements.addAll(fieldType.getFields().keySet()); + this.unvisitedElements.addAll(fieldType.getFields().keySet()); + } + + public void updateOccurrences() { + this.occurrences++; + if (this.occurrences > this.maxOccurs) { + throw new RuntimeException(fieldName + " Element occurs more than the max allowed times"); + } + } + + public void validateMinOccurrences() { + if (this.occurrences < this.minOccurs) { + throw new RuntimeException(fieldName + " Element occurs less than the min required times"); + } + } + + @Override + public void validate() { + if (unvisitedElements.size() > 0) { + throw new RuntimeException("Element " + unvisitedElements.iterator().next() + " not found in " + fieldName); + } + } + + @Override + public void reset() { + this.unvisitedElements.addAll(allElements); + this.visitedElements.clear(); + } + + @Override + public void visit(String element) { + if (this.unvisitedElements.contains(element)) { + this.unvisitedElements.remove(element); + this.visitedElements.add(element); + } else { + if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { + reset(); + updateOccurrences(); + visitedElements.add(element); + return; + } + throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); + } + } + + @Override + public boolean isCompleted() { + return this.unvisitedElements.isEmpty(); + } + + @Override + public boolean isElementContains(String elementName) { + return this.allElements.contains(elementName); + } +} From 4dd8b45a12f1e0f326b5a8f36830091c5228466e Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 11:28:35 +0530 Subject: [PATCH 03/58] Add implementation for single sequences without order alidation --- ballerina/tests/xsd_choice_test.bal | 29 +++ ballerina/tests/xsd_sequence_tests.bal | 213 +++++++++++++++++- ballerina/xml_api.bal | 6 + .../lib/data/xmldata/xml/XmlParser.java | 106 ++++++--- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 25 +- .../data/xmldata/xml/xsd/SequenceInfo.java | 32 ++- 6 files changed, 351 insertions(+), 60 deletions(-) create mode 100644 ballerina/tests/xsd_choice_test.bal diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal new file mode 100644 index 00000000..000e7ac0 --- /dev/null +++ b/ballerina/tests/xsd_choice_test.bal @@ -0,0 +1,29 @@ +// import ballerina/test; + +// type XSDChoiceRecord record {| +// @Choice { +// minOccurs: 1, +// maxOccurs: 1 +// } +// Seq_XSDChoiceRecord seq_XSDChoiceRecord; +// |}; + +// type Seq_XSDChoiceRecord record {| +// int age?; +// float salary?; +// |}; + +// @test:Config +// function testXsdChoice() returns error? { +// string xmlStr = string `13`; +// XSDChoiceRecord v = parseString(xmlStr); +// test:assertEquals(v, {seq_XSDChoiceRecord: {age: 13}}); + +// xmlStr = string `13.5`; +// v = parseString(xmlStr); +// test:assertEquals(v, {seq_XSDChoiceRecord: {salary: 13.5}}); + +// xmlStr = string `1311.1`; +// v = parseString(xmlStr); +// test:assertEquals(v, {seq_XSDChoiceRecord: {age: 13, salary: 11.1}}); +// } \ No newline at end of file diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 8aeb7c57..531c56ca 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -20,11 +20,216 @@ type Seq_XSDSequenceRecord record {| float salary; |}; -@test:Config +@test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdSequence() returns error? { string xmlStr = string `1311.1`; - XSDSequenceRecord v = check parseString(xmlStr); + XSDSequenceRecord|Error v = parseString(xmlStr); test:assertEquals(v, {seq_XSDSequenceRecord: {age: 13, salary: 11.1}}); - test:assertEquals(v.seq_XSDSequenceRecord.age, 13); - test:assertEquals(v.seq_XSDSequenceRecord.salary, 11.1); + test:assertEquals((check v).seq_XSDSequenceRecord.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecord.salary, 11.1); + + xmlStr = string `11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + // TODO: Change error messageas + // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); + test:assertTrue((v).message().includes("required field 'age' not present in XML"), msg = (v).message()); + + xmlStr = string `13`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + // test:assertTrue((v).message().includes("Element salary not found in"), msg = (v).message()); + test:assertTrue((v).message().includes("required field 'salary' not present in XML"), msg = (v).message()); + + xmlStr = string ``; + v = parseString(xmlStr); + test:assertTrue(v is Error); + + // TODO: Change the error message in validate Fields function + test:assertTrue((v).message().includes("required field 'seq_XSDSequenceRecord' not present in XML"), msg = (v).message()); +} + +// TODO: Test with open records. +type XSDSequenceRecord2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord2 seq_XSDSequenceRecord2; + + // TODO: After adding XSD validation for traverse, check union fields as well + int num; +|}; + +type Seq_XSDSequenceRecord2 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence2() returns error? { + string xmlStr = string `31311.1`; + XSDSequenceRecord2|Error v = parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecord2: {age: 13, salary: 11.1}, num: 3}); + test:assertEquals((check v).seq_XSDSequenceRecord2.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecord2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlStr = string `1311.13`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecord2: {age: 13, salary: 11.1}, num: 3}); + test:assertEquals((check v).seq_XSDSequenceRecord2.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecord2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlStr = string `13311.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary not found in")); +} + +type XSDSequenceRecord3 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord3 seq_XSDSequenceRecord3; + + // TODO: After adding XSD validation for traverse, check union fields as well + record{int n;} num; +|}; + +type Seq_XSDSequenceRecord3 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence3() returns error? { + string xmlStr = string `31311.1`; + XSDSequenceRecord3|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord3: {age: 13, salary: 11.1}, num: {n: 3}}); + test:assertEquals((check v2).seq_XSDSequenceRecord3.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord3.salary, 11.1); + test:assertEquals((check v2).num, {n: 3}); + + xmlStr = string `1311.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord3: {age: 13, salary: 11.1}, num: {n: 3}}); + test:assertEquals((check v2).seq_XSDSequenceRecord3.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord3.salary, 11.1); + test:assertEquals((check v2).num, {n: 3}); + + xmlStr = string `13311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in")); +} + +type XSDSequenceRecord4 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord4 seq_XSDSequenceRecord4; +|}; + +type Seq_XSDSequenceRecord4 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence4() returns error? { + string xmlStr = string `31311.1`; + XSDSequenceRecord4|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord4: {age: 13, salary: 11.1}, num: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecord4.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord4.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `1311.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord4: {age: 13, salary: 11.1}, num: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecord4.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord4.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `13311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in")); +} + +type XSDSequenceRecord5 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord5 seq_XSDSequenceRecord5; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecord5 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence5() returns error? { + string xmlStr = string `331311.1`; + XSDSequenceRecord5|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `31311.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `1311.133`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `133311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 456bf308..7fe2f1be 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -52,6 +52,12 @@ public type SequenceConfig record {| public const annotation SequenceConfig Sequence on type, record field; +public type ChoiceConfig record {| + *ParticleOccurrence; +|}; + +public const annotation ChoiceConfig Choice on type, record field; + public type SequenceOrderConfig record {| int value; |}; 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 00386cdb..472ee9a5 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 @@ -57,6 +57,7 @@ import java.util.Map; import java.util.Optional; import java.util.Stack; +import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; @@ -469,14 +470,8 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser QualifiedNameMap siblings = xmlParserData.siblings; Stack> parents = xmlParserData.parents; - if (!xmlParserData.modelGroupStack.isEmpty()) { - ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - if (modelGroup.isCompleted()) { - modelGroup.validate(); - xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); - xmlParserData.modelGroupStack.pop(); - } - } + // TODO: Check the conflict with L482 + finalizedModelStack(xmlParserData, elemQName); if (siblings.contains(elemQName) && !siblings.get(elemQName)) { siblings.put(elemQName, true); @@ -492,6 +487,8 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser } private void validateRequiredFields(XmlParserData xmlParserData) { + // TODO: Validate Sequence and Choice Fields + Map remainingFields = xmlParserData.fieldHierarchy.peek().getMembers(); for (Field field : remainingFields.values()) { if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.REQUIRED)) { @@ -509,6 +506,8 @@ private void validateRequiredFields(XmlParserData xmlParserData) { private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData) { QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); + validateModelGroupStack(xmlParserData, elemQName); + QualifiedNameMap fieldMap = xmlParserData.fieldHierarchy.peek(); Field currentField = null; if (xmlParserData.visitedFieldHierarchy.peek().contains(elemQName)) { @@ -517,6 +516,7 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse elemQName = fieldMap.getMatchedQualifiedName(elemQName); currentField = fieldMap.remove(elemQName); } + xmlParserData.currentField = currentField; if (currentField == null) { // TODO: In here assume that if XSD is present then no rest type can be there. @@ -571,23 +571,50 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse if (DataUtils.isRegExpType(fieldType)) { throw DiagnosticLog.error(DiagnosticErrorCode.UNSUPPORTED_TYPE); } + initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); + } + private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName) { if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); if (modelGroup.isElementContains(elemQName.getLocalPart())) { modelGroup.visit(elemQName.getLocalPart()); } + + if (!modelGroup.isElementContains(elemQName.getLocalPart())) { + modelGroup.validate(); + } } - initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); + } + + private void finalizedModelStack(XmlParserData xmlParserData, QualifiedName elemQName) { + if (!xmlParserData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + if (modelGroup.isCompleted()) { + validateModelGroup(modelGroup, xmlParserData); + } + } + } + + private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData) { + modelGroup.validate(); + xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); + xmlParserData.modelGroupStack.pop(); + xmlParserData.recordTypeStack.pop(); + xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); + popExpectedTypeStacks(xmlParserData); } private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, HashMap sequenceInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { + AtomicBoolean hasElementFound = new AtomicBoolean(false); if (sequenceInfo != null) { + // TODO: Optimize this with stream.findFirst() sequenceInfo.forEach((key, sequence) -> { // TODO: Validate Namespaces if (sequence.isElementContains(elemQName.getLocalPart())) { + hasElementFound.set(true); xmlParserData.modelGroupStack.push(sequence); Field field = xmlParserData.rootRecord.getFields().get(key); // TODO: Test on arrays @@ -598,32 +625,33 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, Hash // TODO: Check for arrays as well Type referredType = TypeUtils.getReferredType(field.getFieldType()); updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); - updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); +// updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); readElement(xmlStreamReader, xmlParserData); int a = 1; - return; } }); - choiceInfo.forEach((key, choice) -> { - // TODO: Validate Namespaces - if (choice.isElementContains(elemQName.getLocalPart())) { - xmlParserData.modelGroupStack.push(choice); - Field field = xmlParserData.rootRecord.getFields().get(key); - // TODO: Test on arrays - fieldMap.remove(QualifiedNameFactory.createQualifiedName( - "", key, "", xmlParserData.useSemanticEquality)); - - // TODO: temp != null for arrays and currentNode - // TODO: Check for arrays as well - Type referredType = TypeUtils.getReferredType(field.getFieldType()); - updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); - updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); - readElement(xmlStreamReader, xmlParserData); - int a = 1; - return; - } - }); + if (!hasElementFound.get() && choiceInfo != null) { + choiceInfo.forEach((key, choice) -> { + // TODO: Validate Namespaces + if (choice.isElementContains(elemQName.getLocalPart())) { + xmlParserData.modelGroupStack.push(choice); + Field field = xmlParserData.rootRecord.getFields().get(key); + // TODO: Test on arrays + fieldMap.remove(QualifiedNameFactory.createQualifiedName( + "", key, "", xmlParserData.useSemanticEquality)); + + // TODO: temp != null for arrays and currentNode + // TODO: Check for arrays as well + Type referredType = TypeUtils.getReferredType(field.getFieldType()); + updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); + updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); + readElement(xmlStreamReader, xmlParserData); + int a = 1; + return; + } + }); + } } } @@ -706,6 +734,8 @@ private BMap updateNextMappingValue(XmlParserData xmlParserData xmlParserData.fieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.recordTypeStack.push(null); + xmlParserData.xsdSequenceInfo.push(new HashMap<>()); + xmlParserData.xsdChoiceInfo.push(new HashMap<>()); BMap currentNode = xmlParserData.currentNode; Object temp = currentNode.get(StringUtils.fromString(fieldName)); if (temp instanceof BArray) { @@ -782,6 +812,12 @@ private void popExpectedTypeStacks(XmlParserData xmlParserData) { popMappingTypeStacks(xmlParserData); xmlParserData.attributeHierarchy.pop(); xmlParserData.arrayIndexes.pop(); + popXsdValidationStacks(xmlParserData); + } + + private void popXsdValidationStacks(XmlParserData xmlParserData) { + xmlParserData.xsdSequenceInfo.pop(); + xmlParserData.xsdChoiceInfo.pop(); } private void popMappingTypeStacks(XmlParserData xmlParserData) { @@ -855,6 +891,8 @@ private void updateExpectedTypeStacksOfRestType(Type restType, XmlParserData xml xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.restTypes.push(restType); xmlParserData.arrayIndexes.push(new HashMap<>()); + xmlParserData.xsdSequenceInfo.push(new HashMap<>()); + xmlParserData.xsdChoiceInfo.push(new HashMap<>()); } } @@ -1245,8 +1283,8 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse new ElementInfo(fieldName, fieldAnnotationValue)); } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = TypeUtils.getReferredType(recordType.getFields() - .get(fieldName).getFieldType()); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); if (fieldType instanceof RecordType recType) { parserData.xsdSequenceInfo.peek().put(fieldName, new SequenceInfo(fieldName, fieldAnnotationValue, recType)); @@ -1256,8 +1294,8 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse } } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = recordType.getFields() - .get(StringUtils.fromString(fieldName)).getFieldType(); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); if (fieldType instanceof RecordType recType) { parserData.xsdChoiceInfo.peek().put(fieldName, new ChoiceInfo(fieldName, fieldAnnotationValue, recType)); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index caedbb82..e0e1d46d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -1,25 +1,23 @@ package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; -import io.ballerina.lib.data.xmldata.xml.QualifiedName; -import io.ballerina.runtime.api.types.Field; -import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; public class ChoiceInfo implements ModelGroupInfo { public String fieldName; public long minOccurs; public long maxOccurs; public int occurrences = 0; + public Set allElements = new HashSet<>(); + public Set visitedElements = new HashSet<>(); - // TODO: Update to a hashset - public Map elements; - - public ChoiceInfo(String fieldName, BMap element, Type fieldType) { + public ChoiceInfo(String fieldName, BMap element, RecordType fieldType) { this.fieldName = fieldName; if (element.containsKey(Constants.MIN_OCCURS)) { this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); @@ -33,6 +31,7 @@ public ChoiceInfo(String fieldName, BMap element, Type fieldTyp this.maxOccurs = Math.max(this.minOccurs, 1); } this.occurrences = 1; + this.allElements.addAll(fieldType.getFields().keySet()); } public void updateOccurrences() { @@ -50,17 +49,21 @@ public void validateMinOccurrences() { @Override public void validate() { - + validateMinOccurrences(); } @Override public void reset() { - + this.visitedElements.clear(); } @Override public void visit(String element) { - + this.visitedElements.add(element); + if (this.visitedElements.containsAll(this.allElements)) { + updateOccurrences(); + visitedElements.clear(); + } } @Override diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 6cf701d1..26ddab24 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -18,6 +18,7 @@ public class SequenceInfo implements ModelGroupInfo { public Set unvisitedElements = new HashSet<>(); public Set visitedElements = new HashSet<>(); public Set allElements = new HashSet<>(); + public boolean isCompleted = false; public SequenceInfo(String fieldName, BMap element, RecordType fieldType) { @@ -29,7 +30,7 @@ public SequenceInfo(String fieldName, BMap element, RecordType } if (element.containsKey(Constants.MAX_OCCURS)) { - this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + this.maxOccurs = element.getIntValue(Constants.MAX_OCCURS); } else { this.maxOccurs = Math.max(this.minOccurs, 1); } @@ -55,9 +56,10 @@ public void validateMinOccurrences() { @Override public void validate() { - if (unvisitedElements.size() > 0) { + if (!isCompleted) { throw new RuntimeException("Element " + unvisitedElements.iterator().next() + " not found in " + fieldName); } + validateMinOccurrences(); } @Override @@ -69,26 +71,34 @@ public void reset() { @Override public void visit(String element) { if (this.unvisitedElements.contains(element)) { + isCompleted = false; this.unvisitedElements.remove(element); this.visitedElements.add(element); - } else { - if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { - reset(); - updateOccurrences(); - visitedElements.add(element); - return; - } - throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); + isCompletedSequences(element, false); + return; } + throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); } @Override public boolean isCompleted() { - return this.unvisitedElements.isEmpty(); + return this.isCompleted; } @Override public boolean isElementContains(String elementName) { return this.allElements.contains(elementName); } + + private void isCompletedSequences(String element, boolean needsUpdate) { + if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { + isCompleted = true; + reset(); + updateOccurrences(); + if (needsUpdate) { + visitedElements.add(element); + unvisitedElements.remove(element); + } + } + } } From ca7997bb390b811aa0aa341f448bd93dcc207921 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 12:54:58 +0530 Subject: [PATCH 04/58] Implement same level sequence without order --- ballerina/tests/xsd_sequence_tests.bal | 141 ++++++++++++++++++ .../lib/data/xmldata/xml/XmlParser.java | 1 - 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 531c56ca..de6cc701 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -213,6 +213,7 @@ function testXsdSequence5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); xmlStr = string `31311.13`; v2 = parseString(xmlStr); @@ -220,6 +221,7 @@ function testXsdSequence5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); xmlStr = string `1311.133`; v2 = parseString(xmlStr); @@ -227,9 +229,148 @@ function testXsdSequence5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); xmlStr = string `133311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } + +type XSDSequenceRecord6 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord6_1 seq_XSDSequenceRecord6_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord6_2 seq_XSDSequenceRecord6_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecord6_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord6_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence6() returns error? { + string xmlStr = string `3SDsuccess31311.1`; + XSDSequenceRecord6|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + // xmlStr = string `31311.13`; + // v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + // test:assertEquals((check v2).num, {n: {n: 3}}); + + // xmlStr = string `1311.133`; + // v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + // test:assertEquals((check v2).num, {n: {n: 3}}); + + // xmlStr = string `133311.1`; + // v2 = parseString(xmlStr); + // test:assertTrue(v2 is Error); + // test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecord7 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord7_1 seq_XSDSequenceRecord7_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord7_2 seq_XSDSequenceRecord7_2; +|}; + +type Seq_XSDSequenceRecord7_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord7_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence7() returns error? { + string xmlStr = string `SDsuccess1311.1`; + XSDSequenceRecord7|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord7_1: {age: 13, salary: 11.1}, seq_XSDSequenceRecord7_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecord7_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord7_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecord7_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecord7_2.status, "success"); + + // xmlStr = string `31311.13`; + // v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + // test:assertEquals((check v2).num, {n: {n: 3}}); + + // xmlStr = string `1311.133`; + // v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); + // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); + // test:assertEquals((check v2).num, {n: {n: 3}}); + + // xmlStr = string `133311.1`; + // v2 = parseString(xmlStr); + // test:assertTrue(v2 is Error); + // test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} 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 472ee9a5..f3de786f 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 @@ -600,7 +600,6 @@ private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlPars modelGroup.validate(); xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); xmlParserData.modelGroupStack.pop(); - xmlParserData.recordTypeStack.pop(); xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); popExpectedTypeStacks(xmlParserData); } From 66255b4d2a51ecbe9f60d4f40d75a40ab63cf602 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 14:05:52 +0530 Subject: [PATCH 05/58] Add tests for simple sequence in the nested scope --- ballerina/tests/xsd_sequence_tests.bal | 326 ++++++++++++++++-- .../lib/data/xmldata/xml/XmlParser.java | 1 + 2 files changed, 291 insertions(+), 36 deletions(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index de6cc701..1076116a 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -288,24 +288,45 @@ function testXsdSequence6() returns error? { test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); - // xmlStr = string `31311.13`; - // v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); - // test:assertEquals((check v2).num, {n: {n: 3}}); - - // xmlStr = string `1311.133`; - // v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); - // test:assertEquals((check v2).num, {n: {n: 3}}); - - // xmlStr = string `133311.1`; - // v2 = parseString(xmlStr); - // test:assertTrue(v2 is Error); - // test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + xmlStr = string `SDsuccess331311.1`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlStr = string `33SDsuccess1311.1`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlStr = string `SDsuccess1311.133`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecord6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlStr = string `SD13success11.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlStr = string `13success11.1SD`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } type XSDSequenceRecord7 record {| @@ -354,23 +375,256 @@ function testXsdSequence7() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecord7_1.salary, 11.1); test:assertEquals((check v2).seq_XSDSequenceRecord7_2.name, "SD"); test:assertEquals((check v2).seq_XSDSequenceRecord7_2.status, "success"); +} - // xmlStr = string `31311.13`; - // v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); - // test:assertEquals((check v2).num, {n: {n: 3}}); - - // xmlStr = string `1311.133`; - // v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XSDSequenceRecord5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.age, 13); - // test:assertEquals((check v2).seq_XSDSequenceRecord5.salary, 11.1); - // test:assertEquals((check v2).num, {n: {n: 3}}); - - // xmlStr = string `133311.1`; - // v2 = parseString(xmlStr); - // test:assertTrue(v2 is Error); - // test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +type XSDSequenceRecord8 record {| + XSDSequenceRecord8P2 test; + int 'check; +|}; + +type XSDSequenceRecord8P2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord8_1 seq_XSDSequenceRecord8_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord8_2 seq_XSDSequenceRecord8_2; +|}; + +type Seq_XSDSequenceRecord8_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord8_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence8() returns error? { + string xmlStr = string `SDsuccess1311.12`; + XSDSequenceRecord8|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {'check: 2, test: {seq_XSDSequenceRecord8_1: {age: 13, salary: 11.1}, seq_XSDSequenceRecord8_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord8_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord8_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord8_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord8_2.status, "success"); +} + +type XSDSequenceRecord9 record {| + XSDSequenceRecord9P test; + int a; +|}; + +type XSDSequenceRecord9P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord9_1 seq_XSDSequenceRecord9_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord9_2 seq_XSDSequenceRecord9_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecord9_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord9_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence9() returns error? { + string xmlStr = string `3SDsuccess31311.12`; + XSDSequenceRecord9|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SDsuccess331311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `33SDsuccess1311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SDsuccess1311.1332`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SD13success11.12`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlStr = string `13success11.1SD2`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecord10 record {| + XSDSequenceRecord10P test; + int a; +|}; + +type XSDSequenceRecord10P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord10_1 seq_XSDSequenceRecord10_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord10_2 seq_XSDSequenceRecord10_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecord10_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord10_2 record {| + @Order { + value: 1 + } + Rec10 name; + + @Order { + value: 2 + } + Rec10 status; +|}; + +type Rec10 record {| + string value1; + string value2; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence10() returns error? { + string xmlStr = string `3SDABSuccessFail31311.12`; + XSDSequenceRecord10|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SDABSuccessFail331311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `33SDABSuccessFail1311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SDABSuccessFail1311.1332`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SDAB13SuccessFail11.12`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlStr = string `13SuccessFail11.1SDAB2`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } 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 f3de786f..fc2d60f2 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 @@ -582,6 +582,7 @@ private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName } if (!modelGroup.isElementContains(elemQName.getLocalPart())) { + // TODO: Validate All model groups at the end modelGroup.validate(); } } From 0ff1274e580d54a274c2332884e7e9f0ffceef9a Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 14:47:53 +0530 Subject: [PATCH 06/58] Add implementation for sequences with xml child elements --- ballerina/tests/xsd_sequence_tests.bal | 16 ++++++++-------- .../lib/data/xmldata/xml/XmlParser.java | 7 +++++-- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 7 ++++++- .../lib/data/xmldata/xml/xsd/ModelGroupInfo.java | 3 ++- .../lib/data/xmldata/xml/xsd/SequenceInfo.java | 15 ++++++++++++++- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 1076116a..c2b9463e 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -583,8 +583,8 @@ function testXsdSequence10() returns error? { test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); @@ -593,8 +593,8 @@ function testXsdSequence10() returns error? { test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); @@ -603,8 +603,8 @@ function testXsdSequence10() returns error? { test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); @@ -613,8 +613,8 @@ function testXsdSequence10() returns error? { test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.age, 13); test:assertEquals((check v2).test.seq_XSDSequenceRecord10_1.salary, 11.1); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, "SD"); - test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, "success"); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); 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 fc2d60f2..3e8e8ae1 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 @@ -578,10 +578,10 @@ private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); if (modelGroup.isElementContains(elemQName.getLocalPart())) { - modelGroup.visit(elemQName.getLocalPart()); + modelGroup.visit(elemQName.getLocalPart(), true); } - if (!modelGroup.isElementContains(elemQName.getLocalPart())) { + if (!modelGroup.isElementContains(elemQName.getLocalPart()) && !modelGroup.isMiddleOfModelGroup()) { // TODO: Validate All model groups at the end modelGroup.validate(); } @@ -591,6 +591,9 @@ private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName private void finalizedModelStack(XmlParserData xmlParserData, QualifiedName elemQName) { if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + if (modelGroup.isElementContains(elemQName.getLocalPart())) { + modelGroup.visit(elemQName.getLocalPart(), false); + } if (modelGroup.isCompleted()) { validateModelGroup(modelGroup, xmlParserData); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index e0e1d46d..0d08d4ff 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -58,7 +58,7 @@ public void reset() { } @Override - public void visit(String element) { + public void visit(String element, boolean isStartElement) { this.visitedElements.add(element); if (this.visitedElements.containsAll(this.allElements)) { updateOccurrences(); @@ -75,4 +75,9 @@ public boolean isCompleted() { public boolean isElementContains(String elementName) { return false; } + + @Override + public boolean isMiddleOfModelGroup() { + return false; + } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index d7d10c1c..4f395db1 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -6,7 +6,8 @@ public interface ModelGroupInfo { public void validate(); public void reset(); - public void visit(String element); + public void visit(String element, boolean isStartElement); public boolean isCompleted(); public boolean isElementContains(String elementName); + public boolean isMiddleOfModelGroup(); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 26ddab24..a757da4e 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -19,6 +19,7 @@ public class SequenceInfo implements ModelGroupInfo { public Set visitedElements = new HashSet<>(); public Set allElements = new HashSet<>(); public boolean isCompleted = false; + public boolean isMiddleOfElement = false; public SequenceInfo(String fieldName, BMap element, RecordType fieldType) { @@ -69,8 +70,15 @@ public void reset() { } @Override - public void visit(String element) { + public void visit(String element, boolean isStartElement) { + isMiddleOfElement = isStartElement; + if (isStartElement) { + isCompleted = false; + return; + } + if (this.unvisitedElements.contains(element)) { + isMiddleOfElement = false; isCompleted = false; this.unvisitedElements.remove(element); this.visitedElements.add(element); @@ -90,6 +98,11 @@ public boolean isElementContains(String elementName) { return this.allElements.contains(elementName); } + @Override + public boolean isMiddleOfModelGroup() { + return isMiddleOfElement; + } + private void isCompletedSequences(String element, boolean needsUpdate) { if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { isCompleted = true; From d3ab720d20e58bb342ad876e80f8e234c6972015 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 20:52:30 +0530 Subject: [PATCH 07/58] Add implementation for medium complex sequences without order hierachy --- ballerina/tests/xsd_sequence_tests.bal | 288 ++++++++++++++++++ .../lib/data/xmldata/xml/XmlParser.java | 47 ++- .../data/xmldata/xml/xsd/SequenceInfo.java | 1 + 3 files changed, 320 insertions(+), 16 deletions(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index c2b9463e..db1df291 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -628,3 +628,291 @@ function testXsdSequence10() returns error? { test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } + +type XSDSequenceRecord11 record {| + XSDSequenceRecord11P test; + int a; + XSDSequenceRecord11P2 test2; +|}; + +type XSDSequenceRecord11P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord11_1 seq_XSDSequenceRecord11_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord11_2 seq_XSDSequenceRecord11_2; + record{record {int n;} n;} num2; +|}; + +type XSDSequenceRecord11P2 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord11_1 seq_XSDSequenceRecord11_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord11_2 seq_XSDSequenceRecord11_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecord11_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecord11_2 record {| + @Order { + value: 1 + } + Rec11 name; + + @Order { + value: 2 + } + Rec11 status; +|}; + +type Rec11 record {| + string value1; + string value2; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence11() returns error? { + string xmlStr = string `3SDABSuccessFail31311.123SDABSuccessFail31311.1`; + XSDSequenceRecord11|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `3SDABSuccessFail31311.1SDABSuccessFail331311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `33SDABSuccessFail1311.13SDABSuccessFail31311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecord11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecord11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecord11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `3SDABSuccessFail31311.113SuccessFail11.1SDAB2`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecord12 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord12_1 seq_XSDSequenceRecord12_1; +}; + +type Seq_XSDSequenceRecord12_1 record { + @Order {value: 1} + Seq_A field1; + + @Order {value: 2} + Seq_B field2; + + @Order {value: 3} + Seq_C field3; +}; + +type Seq_A record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq value1; +}; + +type Seq_B record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq value2; +}; + +type Seq_C record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq value3; +}; + +type Seq record { + @Order {value: 1} + string a; + + @Order {value: 2} + string b; + + @Order {value: 3} + string c; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence12() returns error? { + string xmlStr = string `123123123`; + XSDSequenceRecord12|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord12_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {a: "1", b: "2", c: "3"}}, field3: {value3: {a: "1", b: "2", c: "3"}}}}); +} + +type XSDSequenceRecord13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord13_1 seq_XSDSequenceRecord13_1; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord13_2 Seq_XSDSequenceRecord13_2; +}; + +type Seq_XSDSequenceRecord13_1 record { + @Order {value: 1} + Seq_A_13 field1; + + @Order {value: 2} + Seq_B_13 field2; + + @Order {value: 3} + Seq_C_13 field3; +}; + +type Seq_XSDSequenceRecord13_2 record { + @Order {value: 1} + Seq_D_13 field4; + + @Order {value: 2} + Seq_E_13 field5; + + @Order {value: 3} + Seq_F_13 field6; +}; + +type Seq_A_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_13 value1; +}; + +type Seq_B_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq2_13 value2; +}; + +type Seq_C_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq3_13 value3; +}; + +type Seq_D_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_13 value1; +}; + +type Seq_E_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq2_13 value2; +}; + +type Seq_F_13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq3_13 value3; +}; + +type Seq_13 record { + @Order {value: 1} + string a; + + @Order {value: 2} + string b; + + @Order {value: 3} + string c; +}; + +type Seq2_13 record { + @Order {value: 1} + string d; + + @Order {value: 2} + string e; + + @Order {value: 3} + string f; +}; + +type Seq3_13 record { + @Order {value: 1} + string g; + + @Order {value: 2} + string h; + + @Order {value: 3} + string i; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequence13() returns error? { + string xmlStr = string `123123123123123123`; + XSDSequenceRecord13|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); +} 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 3e8e8ae1..083e9100 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 @@ -57,7 +57,6 @@ import java.util.Map; import java.util.Optional; import java.util.Stack; -import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; @@ -471,16 +470,25 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser Stack> parents = xmlParserData.parents; // TODO: Check the conflict with L482 - finalizedModelStack(xmlParserData, elemQName); +// boolean isModelGroupCompleted = finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); if (siblings.contains(elemQName) && !siblings.get(elemQName)) { siblings.put(elemQName, true); } if (parents.isEmpty() || !parents.peek().contains(elemQName)) { + finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); return; } validateRequiredFields(xmlParserData); + + finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); + + //TODO: Uncomment this +// if (!isModelGroupCompleted) { +// xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); +// } + xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); updateSiblingAndRootRecord(xmlParserData); @@ -588,7 +596,7 @@ private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName } } - private void finalizedModelStack(XmlParserData xmlParserData, QualifiedName elemQName) { + private boolean finalizedModelStackAndReturnTrueIfCompleted(XmlParserData xmlParserData, QualifiedName elemQName) { if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); if (modelGroup.isElementContains(elemQName.getLocalPart())) { @@ -596,8 +604,10 @@ private void finalizedModelStack(XmlParserData xmlParserData, QualifiedName elem } if (modelGroup.isCompleted()) { validateModelGroup(modelGroup, xmlParserData); + return true; } } + return false; } private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData) { @@ -611,31 +621,37 @@ private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlPars private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, HashMap sequenceInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { - AtomicBoolean hasElementFound = new AtomicBoolean(false); + boolean hasElementFound = false; if (sequenceInfo != null) { + // TODO: Optimize this with stream.findFirst() - sequenceInfo.forEach((key, sequence) -> { + for (Map.Entry entry : sequenceInfo.entrySet()) { + String k = entry.getKey(); + SequenceInfo sequence = entry.getValue(); + // TODO: Validate Namespaces if (sequence.isElementContains(elemQName.getLocalPart())) { - hasElementFound.set(true); xmlParserData.modelGroupStack.push(sequence); - Field field = xmlParserData.rootRecord.getFields().get(key); + Field field = xmlParserData.rootRecord.getFields().get(k); + // TODO: Test on arrays fieldMap.remove(QualifiedNameFactory.createQualifiedName( - "", key, "", xmlParserData.useSemanticEquality)); + "", k, "", xmlParserData.useSemanticEquality)); // TODO: temp != null for arrays and currentNode // TODO: Check for arrays as well Type referredType = TypeUtils.getReferredType(field.getFieldType()); - updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); -// updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); + updateNextRecordForXsd(xmlParserData, k, referredType, (RecordType) referredType); readElement(xmlStreamReader, xmlParserData); - int a = 1; + return; } - }); + } + + if (!hasElementFound && choiceInfo != null) { + for (Map.Entry entry : choiceInfo.entrySet()) { + String key = entry.getKey(); + ChoiceInfo choice = entry.getValue(); - if (!hasElementFound.get() && choiceInfo != null) { - choiceInfo.forEach((key, choice) -> { // TODO: Validate Namespaces if (choice.isElementContains(elemQName.getLocalPart())) { xmlParserData.modelGroupStack.push(choice); @@ -650,10 +666,9 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, Hash updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); readElement(xmlStreamReader, xmlParserData); - int a = 1; return; } - }); + } } } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index a757da4e..48cdec72 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -71,6 +71,7 @@ public void reset() { @Override public void visit(String element, boolean isStartElement) { + // TODO: Validate Sequence Order isMiddleOfElement = isStartElement; if (isStartElement) { isCompleted = false; From a6d91f06f4070461c6dc13cdeb8559031e828d8f Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 21:15:06 +0530 Subject: [PATCH 08/58] Update sequence tests --- ballerina/tests/xsd_sequence_tests.bal | 4 ++-- .../io/ballerina/lib/data/xmldata/xml/XmlParser.java | 10 ---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index db1df291..c1e2d3bb 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -804,7 +804,7 @@ type XSDSequenceRecord13 record { minOccurs: 1, maxOccurs: 1 } - Seq_XSDSequenceRecord13_2 Seq_XSDSequenceRecord13_2; + Seq_XSDSequenceRecord13_2 seq_XSDSequenceRecord13_2; }; type Seq_XSDSequenceRecord13_1 record { @@ -914,5 +914,5 @@ type Seq3_13 record { function testXsdSequence13() returns error? { string xmlStr = string `123123123123123123`; XSDSequenceRecord13|Error v2 = parseString(xmlStr); - test:assertEquals(v2, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertEquals(v2, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); } 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 083e9100..8ea6cbff 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 @@ -469,9 +469,6 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser QualifiedNameMap siblings = xmlParserData.siblings; Stack> parents = xmlParserData.parents; - // TODO: Check the conflict with L482 -// boolean isModelGroupCompleted = finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); - if (siblings.contains(elemQName) && !siblings.get(elemQName)) { siblings.put(elemQName, true); } @@ -481,14 +478,7 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser } validateRequiredFields(xmlParserData); - finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); - - //TODO: Uncomment this -// if (!isModelGroupCompleted) { -// xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); -// } - xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); updateSiblingAndRootRecord(xmlParserData); From 1f89eed4adbe0bbf58e8d163942477f41d3b5c4c Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 7 Nov 2024 22:12:48 +0530 Subject: [PATCH 09/58] Add negative tests for medium complex sequences --- ballerina/tests/xsd_sequence_tests.bal | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index c1e2d3bb..5eb660bc 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -915,4 +915,14 @@ function testXsdSequence13() returns error? { string xmlStr = string `123123123123123123`; XSDSequenceRecord13|Error v2 = parseString(xmlStr); test:assertEquals(v2, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + + xmlStr = string `123123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + + xmlStr = string `123123123123121233123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); } From c6161d14c54fc3a8a8438a17803f759ee08e4ee3 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 8 Nov 2024 00:03:24 +0530 Subject: [PATCH 10/58] Implement sequence order validation --- ballerina/tests/xsd_sequence_tests.bal | 42 ++++++++++++- .../lib/data/xmldata/utils/Constants.java | 1 + .../data/xmldata/xml/xsd/SequenceInfo.java | 63 +++++++++++++++---- 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 5eb660bc..595aab6f 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -38,7 +38,6 @@ function testXsdSequence() returns error? { xmlStr = string `13`; v = parseString(xmlStr); test:assertTrue(v is Error); - // test:assertTrue((v).message().includes("Element salary not found in"), msg = (v).message()); test:assertTrue((v).message().includes("required field 'salary' not present in XML"), msg = (v).message()); xmlStr = string ``; @@ -47,6 +46,17 @@ function testXsdSequence() returns error? { // TODO: Change the error message in validate Fields function test:assertTrue((v).message().includes("required field 'seq_XSDSequenceRecord' not present in XML"), msg = (v).message()); + + xmlStr = string `11.113`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); + + // TODO: Create an issue + // xmlStr = string `13`; + // v = parseString(xmlStr); + // test:assertTrue(v is Error); + // test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); } // TODO: Test with open records. @@ -327,6 +337,21 @@ function testXsdSequence6() returns error? { v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + + xmlStr = string `successSD1311.133`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element name is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `SDsuccess11.11333`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element age is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `SDsuccess11.13133`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element age not found in"), (v2).message()); } type XSDSequenceRecord7 record {| @@ -925,4 +950,19 @@ function testXsdSequence13() returns error? { v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + + xmlStr = string `123123123123123132`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element h is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `132123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element b is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field4 is not in the correct order in"), msg = (v2).message()); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java index 1d1209d8..2d5737ef 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java @@ -72,4 +72,5 @@ private Constants() {} public static final String CHOICE = "Choice"; public static final BString MIN_OCCURS = StringUtils.fromString("minOccurs"); public static final BString MAX_OCCURS = StringUtils.fromString("maxOccurs"); + public static final String ORDER = "Order"; } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 48cdec72..ce77ae7d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -5,7 +5,9 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; public class SequenceInfo implements ModelGroupInfo { @@ -15,11 +17,13 @@ public class SequenceInfo implements ModelGroupInfo { public int occurrences; // TODO: Update to a hashset - public Set unvisitedElements = new HashSet<>(); - public Set visitedElements = new HashSet<>(); - public Set allElements = new HashSet<>(); - public boolean isCompleted = false; - public boolean isMiddleOfElement = false; + private Set unvisitedElements = new HashSet<>(); + private Set visitedElements = new HashSet<>(); + private Set allElements = new HashSet<>(); + private Map elementPriorityOrder = new HashMap<>(); + private boolean isCompleted = false; + private boolean isMiddleOfElement = false; + private long currentPriority = -1L; public SequenceInfo(String fieldName, BMap element, RecordType fieldType) { @@ -40,6 +44,28 @@ public SequenceInfo(String fieldName, BMap element, RecordType // TODO: Name Annotation not encountered this.allElements.addAll(fieldType.getFields().keySet()); this.unvisitedElements.addAll(fieldType.getFields().keySet()); + updatePriorityOrder(fieldType); + } + + private void updatePriorityOrder(RecordType fieldType) { + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + BMap fieldAnnotationValue = null; + if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { + fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + this.elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); + } + } + } + } + } } public void updateOccurrences() { @@ -71,8 +97,13 @@ public void reset() { @Override public void visit(String element, boolean isStartElement) { - // TODO: Validate Sequence Order + if (isMiddleOfElement && isStartElement) { + return; + } + isMiddleOfElement = isStartElement; + compareSequencePriorityOrder(element); + if (isStartElement) { isCompleted = false; return; @@ -81,14 +112,26 @@ public void visit(String element, boolean isStartElement) { if (this.unvisitedElements.contains(element)) { isMiddleOfElement = false; isCompleted = false; + + //TODO: Remove unvisitedElements variable and get it using set substraction this.unvisitedElements.remove(element); this.visitedElements.add(element); - isCompletedSequences(element, false); + isCompletedSequences(element); return; } throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); } + private void compareSequencePriorityOrder(String element) { + Long elementPriority = elementPriorityOrder.get(element); + if (elementPriority != null) { + if (elementPriority < currentPriority) { + throw new RuntimeException("Element " + element + " is not in the correct order in " + fieldName); + } + currentPriority = elementPriority; + } + } + @Override public boolean isCompleted() { return this.isCompleted; @@ -104,15 +147,11 @@ public boolean isMiddleOfModelGroup() { return isMiddleOfElement; } - private void isCompletedSequences(String element, boolean needsUpdate) { + private void isCompletedSequences(String element) { if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { isCompleted = true; reset(); updateOccurrences(); - if (needsUpdate) { - visitedElements.add(element); - unvisitedElements.remove(element); - } } } } From 2ab553ecef7e303edf045aba12685054df42bf76 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 8 Nov 2024 12:43:17 +0530 Subject: [PATCH 11/58] Add element annotation details for xsd validation --- ballerina/tests/xsd_element_test.bal | 206 ++++++++++++++++++ .../lib/data/xmldata/xml/XmlParser.java | 31 +++ .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 4 +- .../lib/data/xmldata/xml/xsd/ElementInfo.java | 12 +- .../data/xmldata/xml/xsd/SequenceInfo.java | 6 +- 5 files changed, 250 insertions(+), 9 deletions(-) create mode 100644 ballerina/tests/xsd_element_test.bal diff --git a/ballerina/tests/xsd_element_test.bal b/ballerina/tests/xsd_element_test.bal new file mode 100644 index 00000000..b4b039a0 --- /dev/null +++ b/ballerina/tests/xsd_element_test.bal @@ -0,0 +1,206 @@ +import ballerina/test; + +type ElementRecord record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + string name?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + int age?; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElement() returns error? { + string xmlStr = "John25"; + ElementRecord|Error rec = parseString(xmlStr); + test:assertEquals(rec, {name: "John", age: 25}); + + xmlStr = "John"; + rec = parseString(xmlStr); + test:assertEquals(rec, {name: "John"}); +} + +type ElementRecord2 record { + @Element { + maxOccurs: 10, + minOccurs: 0 + } + string[] name?; + + @Element { + maxOccurs: 3, + minOccurs: 1 + } + int[] age?; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElement2() returns error? { + string xmlStr = "John25"; + ElementRecord2|Error rec = parseString(xmlStr); + test:assertEquals(rec, {name: ["John"], age: [25]}); + + xmlStr = "John"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs less than the min required times")); + + xmlStr = "25"; + rec = parseString(xmlStr); + test:assertEquals(rec, {age: [25]}); + + xmlStr = "1112131415"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = "11Abc12131415"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = "AbcAbcAbcAbcAbc1112131415"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = "11AbcAbc12Abc13Abc1415"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); +} + +type ElementRecord3 record { + record { + @Element { + maxOccurs: 10, + minOccurs: 0 + } + string[] name?; + + @Element { + maxOccurs: 3, + minOccurs: 1 + } + int[] age?; + + @Element { + maxOccurs: 3, + minOccurs: 2 + } + int[] id?; + } user; + + @Element { + maxOccurs: 1, + minOccurs: 1 + } + int status; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElement3() returns error? { + string xmlStr; + ElementRecord3|Error rec; + + xmlStr = "John12353"; + rec = parseString(xmlStr); + test:assertEquals(rec, {user: {name: ["John"], age: [35], id: [1, 2]}, status: 3}); + + xmlStr = "John123"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs less than the min required times"), (rec).message()); + + xmlStr = "12353"; + rec = parseString(xmlStr); + test:assertEquals(rec, {user: {age: [35], id: [1, 2]}, status: 3}); + + xmlStr = "1211131314153"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = "1211Abc131314153"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + + xmlStr = "12AbcAbcAbcAbcAbc11131314153"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + + xmlStr = "1211AbcAbc13Abc13Abc14153"; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); +} + +type ElementRecord4 record { + @Element { + maxOccurs: 3, + minOccurs: 2 + } + record { + @Element { + maxOccurs: 4, + minOccurs: 3 + } + string[] firstName; + + @Element { + maxOccurs: 3, + minOccurs: 3 + } + string[] lastName; + }[] name; + + @Element { + maxOccurs: 4, + minOccurs: 3 + } + int[] status; + + @Element { + maxOccurs: 3, + minOccurs: 3 + } + int[] age; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElement4() returns error? { + string xmlStr = string `JohnJaneJimDoeSmithBrownJohnJaneJimDoeSmithBrown123202530`; + ElementRecord4|Error rec = parseString(xmlStr); + test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3], age: [20, 25, 30]}); + + xmlStr = string `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseString(xmlStr); + test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + + xmlStr = string `JohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("name Element occurs less than the min required times")); + + xmlStr = string `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("name Element occurs more than the max allowed times")); + + xmlStr = string `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("lastName Element occurs more than the max allowed times")); + + xmlStr = string `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; + rec = parseString(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("firstName Element occurs more than the max allowed times"), (rec).message()); +} \ No newline at end of file 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 8ea6cbff..2f649153 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 @@ -191,6 +191,8 @@ public Object parse(XmlParserData xmlParserData) { handleXMLStreamException(e); } + validateModelGroupInfoStack(xmlParserData); + validateElementInfoStack(xmlParserData); return xmlParserData.currentNode; } @@ -228,6 +230,7 @@ public void parseRecordRest(String startElementName, XmlParserData xmlParserData QName endElement = xmlStreamReader.getName(); if (endElement.getLocalPart().equals(startElementName)) { validateRequiredFields(xmlParserData); + validateElementInfoStack(xmlParserData); popExpectedTypeStacks(xmlParserData); break; } @@ -460,6 +463,7 @@ private void buildDocument(XmlParserData xmlParserData) { return; } validateRequiredFields(xmlParserData); + validateElementInfoStack(xmlParserData); } @SuppressWarnings("unchecked") @@ -478,6 +482,7 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser } validateRequiredFields(xmlParserData); + validateElementInfoStack(xmlParserData); finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); @@ -505,6 +510,7 @@ private void validateRequiredFields(XmlParserData xmlParserData) { private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData) { QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); validateModelGroupStack(xmlParserData, elemQName); + validateElementOccurrence(xmlParserData, elemQName); QualifiedNameMap fieldMap = xmlParserData.fieldHierarchy.peek(); Field currentField = null; @@ -572,6 +578,15 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); } + private void validateElementOccurrence(XmlParserData xmlParserData, QualifiedName elemQName) { + if (!xmlParserData.xmlElementInfo.isEmpty()) { + HashMap elementInfo = xmlParserData.xmlElementInfo.peek(); + if (elementInfo.containsKey(elemQName.getLocalPart())) { + elementInfo.get(elemQName.getLocalPart()).updateOccurrences(); + } + } + } + private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName) { if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); @@ -744,6 +759,7 @@ private BMap updateNextMappingValue(XmlParserData xmlParserData xmlParserData.recordTypeStack.push(null); xmlParserData.xsdSequenceInfo.push(new HashMap<>()); xmlParserData.xsdChoiceInfo.push(new HashMap<>()); + xmlParserData.xmlElementInfo.push(new HashMap<>()); BMap currentNode = xmlParserData.currentNode; Object temp = currentNode.get(StringUtils.fromString(fieldName)); if (temp instanceof BArray) { @@ -814,6 +830,7 @@ private void updateExpectedTypeStacks(RecordType recordType, XmlParserData xmlPa xmlParserData.restTypes.push(recordType.getRestFieldType()); xmlParserData.xsdChoiceInfo.push(new HashMap<>()); xmlParserData.xsdSequenceInfo.push(new HashMap<>()); + xmlParserData.xmlElementInfo.push(new HashMap<>()); } private void popExpectedTypeStacks(XmlParserData xmlParserData) { @@ -826,6 +843,7 @@ private void popExpectedTypeStacks(XmlParserData xmlParserData) { private void popXsdValidationStacks(XmlParserData xmlParserData) { xmlParserData.xsdSequenceInfo.pop(); xmlParserData.xsdChoiceInfo.pop(); + xmlParserData.xmlElementInfo.pop(); } private void popMappingTypeStacks(XmlParserData xmlParserData) { @@ -901,6 +919,7 @@ private void updateExpectedTypeStacksOfRestType(Type restType, XmlParserData xml xmlParserData.arrayIndexes.push(new HashMap<>()); xmlParserData.xsdSequenceInfo.push(new HashMap<>()); xmlParserData.xsdChoiceInfo.push(new HashMap<>()); + xmlParserData.xmlElementInfo.push(new HashMap<>()); } } @@ -1318,6 +1337,18 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse } } + private void validateElementInfoStack(XmlParserData xmlParserData) { + if (!xmlParserData.xmlElementInfo.isEmpty()) { + xmlParserData.xmlElementInfo.peek().forEach((key, value) -> value.validate()); + } + } + + private void validateModelGroupInfoStack(XmlParserData xmlParserData) { + if (!xmlParserData.modelGroupStack.isEmpty()) { + xmlParserData.modelGroupStack.peek().validate(); + } + } + /** * Represents the content of an XML element. * diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 0d08d4ff..d0f2a19d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -12,7 +12,7 @@ public class ChoiceInfo implements ModelGroupInfo { public String fieldName; public long minOccurs; public long maxOccurs; - public int occurrences = 0; + public int occurrences; public Set allElements = new HashSet<>(); public Set visitedElements = new HashSet<>(); @@ -26,7 +26,7 @@ public ChoiceInfo(String fieldName, BMap element, RecordType fi } if (element.containsKey(Constants.MAX_OCCURS)) { - this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + this.maxOccurs = element.getIntValue(Constants.MAX_OCCURS); } else { this.maxOccurs = Math.max(this.minOccurs, 1); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index f3a6e986..e01da77d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -8,7 +8,7 @@ public class ElementInfo { String name; public long minOccurs; public long maxOccurs; - public int occurrences = 0; + public int occurrences; public ElementInfo(String name, BMap element) { this.name = name; @@ -19,11 +19,11 @@ public ElementInfo(String name, BMap element) { } if (element.containsKey(Constants.MAX_OCCURS)) { - this.minOccurs = element.getIntValue(Constants.MAX_OCCURS); + this.maxOccurs = element.getIntValue(Constants.MAX_OCCURS); } else { this.maxOccurs = Math.max(this.minOccurs, 1); } - this.occurrences = 1; + this.occurrences = 0; } public void updateOccurrences() { @@ -33,7 +33,11 @@ public void updateOccurrences() { } } - public void validateMinOccurrences() { + public void validate() { + validateMinOccurrences(); + } + + private void validateMinOccurrences() { if (this.occurrences < this.minOccurs) { throw new RuntimeException(name + " Element occurs less than the min required times"); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index ce77ae7d..fb5ad9ec 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -116,7 +116,7 @@ public void visit(String element, boolean isStartElement) { //TODO: Remove unvisitedElements variable and get it using set substraction this.unvisitedElements.remove(element); this.visitedElements.add(element); - isCompletedSequences(element); + validateCompletedSequences(); return; } throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); @@ -147,8 +147,8 @@ public boolean isMiddleOfModelGroup() { return isMiddleOfElement; } - private void isCompletedSequences(String element) { - if (unvisitedElements.isEmpty() && visitedElements.contains(element)) { + private void validateCompletedSequences() { + if (unvisitedElements.isEmpty()) { isCompleted = true; reset(); updateOccurrences(); From e501cbd8969112a17031dd73c283171087630adb Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 8 Nov 2024 23:15:29 +0530 Subject: [PATCH 12/58] Improve seq implementation for validate element occurences --- ..._sequence_test_with_element_annotation.bal | 225 ++++++++++++++++++ ballerina/tests/xsd_sequence_tests.bal | 4 +- .../lib/data/xmldata/xml/XmlParser.java | 84 ++++--- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 9 +- .../data/xmldata/xml/xsd/ModelGroupInfo.java | 4 + .../data/xmldata/xml/xsd/SequenceInfo.java | 38 ++- 6 files changed, 327 insertions(+), 37 deletions(-) create mode 100644 ballerina/tests/xsd_sequence_test_with_element_annotation.bal diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal new file mode 100644 index 00000000..0c8d62c1 --- /dev/null +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -0,0 +1,225 @@ +import ballerina/test; + +type XsdSequenceWithElementAnnotation record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1 seq_EA1; +}; + +type Seq_EA1 record { + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithElementAnnotation() returns error? { + string xmlStr; + XsdSequenceWithElementAnnotation|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); + + xmlStr = string `ABCABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdSequenceWithElementAnnotation2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2 seq_EA2; +}; + +type Seq_EA2 record { + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithElementAnnotation2() returns error? { + string xmlStr; + XsdSequenceWithElementAnnotation2|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCABCD`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABCCD`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function test110() returns error? { + // string xmlStr; + // // XsdSequenceWithElementAnnotation|Error v; + + // string xmlStr; + // XSDSequenceRecord2|Error v; + + // // xmlStr = string `ABCABCABABAB`; + // // v = parseString(xmlStr); + // // test:assertEquals(v, {seq_EA1: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + // xmlStr = string `1311.13`; + // v = parseString(xmlStr); + // test:assertEquals(v, {seq_XSDSequenceRecord2: {age: 13, salary: 11.1}, num: 3}); + // test:assertEquals((check v).seq_XSDSequenceRecord2.age, 13); + // test:assertEquals((check v).seq_XSDSequenceRecord2.salary, 11.1); + // test:assertEquals((check v).num, 3); +} \ No newline at end of file diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 595aab6f..4f50e1d8 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -33,12 +33,12 @@ function testXsdSequence() returns error? { test:assertTrue(v is Error); // TODO: Change error messageas // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); - test:assertTrue((v).message().includes("required field 'age' not present in XML"), msg = (v).message()); + test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecord"), msg = (v).message()); xmlStr = string `13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("required field 'salary' not present in XML"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary not found in seq_XSDSequenceRecord"), msg = (v).message()); xmlStr = string ``; v = parseString(xmlStr); 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 2f649153..f0404672 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 @@ -191,8 +191,8 @@ public Object parse(XmlParserData xmlParserData) { handleXMLStreamException(e); } - validateModelGroupInfoStack(xmlParserData); validateElementInfoStack(xmlParserData); + validateModelGroupInfoStack(xmlParserData); return xmlParserData.currentNode; } @@ -477,13 +477,13 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser siblings.put(elemQName, true); } if (parents.isEmpty() || !parents.peek().contains(elemQName)) { - finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); + validateModelGroupStack(xmlParserData, elemQName, false); return; } validateRequiredFields(xmlParserData); validateElementInfoStack(xmlParserData); - finalizedModelStackAndReturnTrueIfCompleted(xmlParserData, elemQName); + validateModelGroupStack(xmlParserData, elemQName, false); xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); updateSiblingAndRootRecord(xmlParserData); @@ -509,8 +509,8 @@ private void validateRequiredFields(XmlParserData xmlParserData) { private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData) { QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); - validateModelGroupStack(xmlParserData, elemQName); - validateElementOccurrence(xmlParserData, elemQName); + updateElementOccurrence(xmlParserData, elemQName); + validateModelGroupStack(xmlParserData, elemQName, true); QualifiedNameMap fieldMap = xmlParserData.fieldHierarchy.peek(); Field currentField = null; @@ -578,7 +578,7 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); } - private void validateElementOccurrence(XmlParserData xmlParserData, QualifiedName elemQName) { + private void updateElementOccurrence(XmlParserData xmlParserData, QualifiedName elemQName) { if (!xmlParserData.xmlElementInfo.isEmpty()) { HashMap elementInfo = xmlParserData.xmlElementInfo.peek(); if (elementInfo.containsKey(elemQName.getLocalPart())) { @@ -587,32 +587,44 @@ private void validateElementOccurrence(XmlParserData xmlParserData, QualifiedNam } } - private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName) { - if (!xmlParserData.modelGroupStack.isEmpty()) { +// private void finalizedModelStackAndReturnTrueIfCompleted(XmlParserData xmlParserData, QualifiedName elemQName) { +// if (!xmlParserData.modelGroupStack.isEmpty()) { +// ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); +// if (modelGroup.isElementContains(elemQName.getLocalPart())) { +// modelGroup.visit(elemQName.getLocalPart(), false); +// } +// } +// } +// +// private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName) { +// while (!xmlParserData.modelGroupStack.isEmpty()) { +// ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); +// if ((!modelGroup.isElementContains(elemQName.getLocalPart()) +// && !modelGroup.isMiddleOfModelGroup())) { +// validateModelGroup(modelGroup, xmlParserData); +// continue; +// } +// if (modelGroup.isElementContains(elemQName.getLocalPart())) { +// modelGroup.visit(elemQName.getLocalPart(), true); +// } +// return; +// } +// } + + private void validateModelGroupStack(XmlParserData xmlParserData, + QualifiedName elemQName, boolean isStartElement) { + while (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - if (modelGroup.isElementContains(elemQName.getLocalPart())) { - modelGroup.visit(elemQName.getLocalPart(), true); - } - - if (!modelGroup.isElementContains(elemQName.getLocalPart()) && !modelGroup.isMiddleOfModelGroup()) { - // TODO: Validate All model groups at the end - modelGroup.validate(); + if ((!modelGroup.isElementContains(elemQName.getLocalPart()) + && !modelGroup.isMiddleOfModelGroup())) { + validateModelGroup(modelGroup, xmlParserData); + continue; } - } - } - - private boolean finalizedModelStackAndReturnTrueIfCompleted(XmlParserData xmlParserData, QualifiedName elemQName) { - if (!xmlParserData.modelGroupStack.isEmpty()) { - ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); if (modelGroup.isElementContains(elemQName.getLocalPart())) { - modelGroup.visit(elemQName.getLocalPart(), false); - } - if (modelGroup.isCompleted()) { - validateModelGroup(modelGroup, xmlParserData); - return true; + modelGroup.visit(elemQName.getLocalPart(), isStartElement); } + return; } - return false; } private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData) { @@ -620,7 +632,15 @@ private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlPars xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); xmlParserData.modelGroupStack.pop(); xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); - popExpectedTypeStacks(xmlParserData); + popElementStacksForValidatingGroup(xmlParserData); + } + + private void popElementStacksForValidatingGroup(XmlParserData xmlParserData) { + popMappingTypeStacks(xmlParserData); + xmlParserData.attributeHierarchy.pop(); + xmlParserData.arrayIndexes.pop(); + xmlParserData.xsdSequenceInfo.pop(); + xmlParserData.xsdChoiceInfo.pop(); } private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, @@ -1314,7 +1334,8 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse .getFields().get(fieldName).getFieldType()); if (fieldType instanceof RecordType recType) { parserData.xsdSequenceInfo.peek().put(fieldName, - new SequenceInfo(fieldName, fieldAnnotationValue, recType)); + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else { throw new RuntimeException("Cannot include Sequence annotation into " + fieldName + " of type " + fieldType); @@ -1344,8 +1365,9 @@ private void validateElementInfoStack(XmlParserData xmlParserData) { } private void validateModelGroupInfoStack(XmlParserData xmlParserData) { - if (!xmlParserData.modelGroupStack.isEmpty()) { - xmlParserData.modelGroupStack.peek().validate(); + while (!xmlParserData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + validateModelGroup(modelGroup, xmlParserData); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index d0f2a19d..9064dd57 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -5,8 +5,10 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.Stack; public class ChoiceInfo implements ModelGroupInfo { public String fieldName; @@ -68,7 +70,7 @@ public void visit(String element, boolean isStartElement) { @Override public boolean isCompleted() { - return false; + return occurrences <= maxOccurs && occurrences >= minOccurs; } @Override @@ -80,4 +82,9 @@ public boolean isElementContains(String elementName) { public boolean isMiddleOfModelGroup() { return false; } + + @Override + public boolean isContainsAllRemaining(Stack> elementInfoStack) { + return false; + } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index 4f395db1..d0e5e801 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -1,5 +1,8 @@ package io.ballerina.lib.data.xmldata.xml.xsd; +import java.util.HashMap; +import java.util.Stack; + public interface ModelGroupInfo { public void updateOccurrences(); public void validateMinOccurrences(); @@ -10,4 +13,5 @@ public interface ModelGroupInfo { public boolean isCompleted(); public boolean isElementContains(String elementName); public boolean isMiddleOfModelGroup(); + public boolean isContainsAllRemaining(Stack> elementInfoStack); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index fb5ad9ec..78b7f739 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -9,6 +9,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.Stack; public class SequenceInfo implements ModelGroupInfo { public String fieldName; @@ -24,9 +25,10 @@ public class SequenceInfo implements ModelGroupInfo { private boolean isCompleted = false; private boolean isMiddleOfElement = false; private long currentPriority = -1L; + private Stack> xmlElementInfo; - - public SequenceInfo(String fieldName, BMap element, RecordType fieldType) { + public SequenceInfo(String fieldName, BMap element, RecordType fieldType, + Stack> xmlElementInfo) { this.fieldName = fieldName; if (element.containsKey(Constants.MIN_OCCURS)) { this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); @@ -45,6 +47,7 @@ public SequenceInfo(String fieldName, BMap element, RecordType this.allElements.addAll(fieldType.getFields().keySet()); this.unvisitedElements.addAll(fieldType.getFields().keySet()); updatePriorityOrder(fieldType); + this.xmlElementInfo = xmlElementInfo; } private void updatePriorityOrder(RecordType fieldType) { @@ -83,12 +86,33 @@ public void validateMinOccurrences() { @Override public void validate() { - if (!isCompleted) { + if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { throw new RuntimeException("Element " + unvisitedElements.iterator().next() + " not found in " + fieldName); } validateMinOccurrences(); } + private boolean containsAllOptionalElements(Stack> elementInfoStack) { + if (elementInfoStack.isEmpty()) { + return false; + } + + HashMap elementInfo = elementInfoStack.peek(); + Set unvisitedElementsTemp = new HashSet<>(unvisitedElements); + unvisitedElementsTemp.forEach(element -> { + if (elementInfo.containsKey(element)) { + ElementInfo elem = elementInfo.get(element); + if (elem.minOccurs == 0) { + unvisitedElements.remove(element); + } + } + }); + if (unvisitedElements.isEmpty()) { + return true; + } + return false; + } + @Override public void reset() { this.unvisitedElements.addAll(allElements); @@ -119,6 +143,10 @@ public void visit(String element, boolean isStartElement) { validateCompletedSequences(); return; } + + if (this.visitedElements.contains(element)) { + return; + } throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); } @@ -154,4 +182,8 @@ private void validateCompletedSequences() { updateOccurrences(); } } + + public boolean isContainsAllRemaining(Stack> elementInfoStack) { + return containsAllOptionalElements(elementInfoStack); + } } From 16805b210fd11b047b7963aa00ad240e44e822a6 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 9 Nov 2024 00:30:01 +0530 Subject: [PATCH 13/58] Add element occurs implementation in xsd sequences --- ..._sequence_test_with_element_annotation.bal | 155 ++++++++++++++++-- .../lib/data/xmldata/xml/XmlParser.java | 42 ++--- .../data/xmldata/xml/xsd/SequenceInfo.java | 5 +- 3 files changed, 154 insertions(+), 48 deletions(-) diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index 0c8d62c1..80b4d73a 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -204,22 +204,141 @@ function testXsdSequenceWithElementAnnotation2() returns error? { test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); } +type XsdSequenceWithElementAnnotation3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XsdSequenceWithElementAnnotation3_1 seq_XsdSequenceWithElementAnnotation3_1; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XsdSequenceWithElementAnnotation3_2 seq_XsdSequenceWithElementAnnotation3_2; +}; + +type Seq_XsdSequenceWithElementAnnotation3_1 record { + @Element { + minOccurs: 1, + maxOccurs: 3 + } + @Order {value: 1} + Seq_A_3[] field1; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + @Order {value: 2} + Seq_B_3[] field2?; + + @Order {value: 3} + Seq_C_3 field3; +}; + +type Seq_XsdSequenceWithElementAnnotation3_2 record { + @Order {value: 1} + Seq_D_3 field4; + + @Order {value: 2} + Seq_E_3 field5; + + @Order {value: 3} + Seq_F_3 field6; +}; + +type Seq_A_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_3 value1; +}; + +type Seq_B_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq2_3 value2; +}; + +type Seq_C_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq3_3 value3; +}; + +type Seq_D_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_3 value1; +}; + +type Seq_E_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq2_3 value2; +}; + +type Seq_F_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq3_3 value3; +}; + +type Seq_3 record { + @Order {value: 1} + string a; + + @Order {value: 2} + string b; + + @Order {value: 3} + string c; +}; + +type Seq2_3 record { + @Order {value: 1} + string d; + + @Order {value: 2} + string e; + + @Order {value: 3} + string f; +}; + +type Seq3_3 record { + @Order {value: 1} + string g; + + @Order {value: 2} + string h; + + @Order {value: 3} + string i; +}; + @test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} -function test110() returns error? { - // string xmlStr; - // // XsdSequenceWithElementAnnotation|Error v; - - // string xmlStr; - // XSDSequenceRecord2|Error v; - - // // xmlStr = string `ABCABCABABAB`; - // // v = parseString(xmlStr); - // // test:assertEquals(v, {seq_EA1: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); - - // xmlStr = string `1311.13`; - // v = parseString(xmlStr); - // test:assertEquals(v, {seq_XSDSequenceRecord2: {age: 13, salary: 11.1}, num: 3}); - // test:assertEquals((check v).seq_XSDSequenceRecord2.age, 13); - // test:assertEquals((check v).seq_XSDSequenceRecord2.salary, 11.1); - // test:assertEquals((check v).num, 3); -} \ No newline at end of file +function testXsdSequenceWithElementAnnotation3() returns error? { + string xmlStr; + XsdSequenceWithElementAnnotation3|Error v2; + + // xmlStr = string `123123123123123123123123123`; + // v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: "1", b: "2", c: "3"}}, {value1: {a: "1", b: "2", c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + + xmlStr = string `123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: "1", b: "2", c: "3"}}, {value1: {a: "1", b: "2", c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); +} 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 f0404672..9faf602e 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 @@ -230,7 +230,7 @@ public void parseRecordRest(String startElementName, XmlParserData xmlParserData QName endElement = xmlStreamReader.getName(); if (endElement.getLocalPart().equals(startElementName)) { validateRequiredFields(xmlParserData); - validateElementInfoStack(xmlParserData); + validateCurrentElementInfo(xmlParserData); popExpectedTypeStacks(xmlParserData); break; } @@ -367,6 +367,7 @@ private void convertTextAndUpdateCurrentNode(BMap currentNode, } xmlParserData.currentNode = parent; + validateCurrentElementInfo(xmlParserData); popExpectedTypeStacks(xmlParserData); updateSiblingAndRootRecord(xmlParserData); } @@ -407,6 +408,7 @@ private void handleTruncatedCharacters(XMLStreamReader xmlStreamReader, TextValu @SuppressWarnings("unchecked") private void handleContentFieldInRecordType(RecordType recordType, BString text, XmlParserData xmlParserData) { + validateCurrentElementInfo(xmlParserData); popExpectedTypeStacks(xmlParserData); updateSiblingAndRootRecord(xmlParserData); for (String key : recordType.getFields().keySet()) { @@ -463,7 +465,7 @@ private void buildDocument(XmlParserData xmlParserData) { return; } validateRequiredFields(xmlParserData); - validateElementInfoStack(xmlParserData); + validateCurrentElementInfo(xmlParserData); } @SuppressWarnings("unchecked") @@ -482,7 +484,7 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser } validateRequiredFields(xmlParserData); - validateElementInfoStack(xmlParserData); + validateCurrentElementInfo(xmlParserData); validateModelGroupStack(xmlParserData, elemQName, false); xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); @@ -587,30 +589,6 @@ private void updateElementOccurrence(XmlParserData xmlParserData, QualifiedName } } -// private void finalizedModelStackAndReturnTrueIfCompleted(XmlParserData xmlParserData, QualifiedName elemQName) { -// if (!xmlParserData.modelGroupStack.isEmpty()) { -// ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); -// if (modelGroup.isElementContains(elemQName.getLocalPart())) { -// modelGroup.visit(elemQName.getLocalPart(), false); -// } -// } -// } -// -// private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName) { -// while (!xmlParserData.modelGroupStack.isEmpty()) { -// ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); -// if ((!modelGroup.isElementContains(elemQName.getLocalPart()) -// && !modelGroup.isMiddleOfModelGroup())) { -// validateModelGroup(modelGroup, xmlParserData); -// continue; -// } -// if (modelGroup.isElementContains(elemQName.getLocalPart())) { -// modelGroup.visit(elemQName.getLocalPart(), true); -// } -// return; -// } -// } - private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName, boolean isStartElement) { while (!xmlParserData.modelGroupStack.isEmpty()) { @@ -632,6 +610,7 @@ private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlPars xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); xmlParserData.modelGroupStack.pop(); xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); + validateCurrentElementInfo(xmlParserData); popElementStacksForValidatingGroup(xmlParserData); } @@ -641,6 +620,7 @@ private void popElementStacksForValidatingGroup(XmlParserData xmlParserData) { xmlParserData.arrayIndexes.pop(); xmlParserData.xsdSequenceInfo.pop(); xmlParserData.xsdChoiceInfo.pop(); + xmlParserData.xmlElementInfo.pop(); } private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, @@ -1358,12 +1338,18 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse } } - private void validateElementInfoStack(XmlParserData xmlParserData) { + private void validateCurrentElementInfo(XmlParserData xmlParserData) { if (!xmlParserData.xmlElementInfo.isEmpty()) { xmlParserData.xmlElementInfo.peek().forEach((key, value) -> value.validate()); } } + private void validateElementInfoStack(XmlParserData xmlParserData) { + while (!xmlParserData.xmlElementInfo.isEmpty()) { + xmlParserData.xmlElementInfo.pop().forEach((key, value) -> value.validate()); + } + } + private void validateModelGroupInfoStack(XmlParserData xmlParserData) { while (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 78b7f739..240ad3ba 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -86,10 +86,12 @@ public void validateMinOccurrences() { @Override public void validate() { + validateCompletedSequences(); if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { throw new RuntimeException("Element " + unvisitedElements.iterator().next() + " not found in " + fieldName); } validateMinOccurrences(); + reset(); } private boolean containsAllOptionalElements(Stack> elementInfoStack) { @@ -108,6 +110,7 @@ private boolean containsAllOptionalElements(Stack> } }); if (unvisitedElements.isEmpty()) { + updateOccurrences(); return true; } return false; @@ -140,7 +143,6 @@ public void visit(String element, boolean isStartElement) { //TODO: Remove unvisitedElements variable and get it using set substraction this.unvisitedElements.remove(element); this.visitedElements.add(element); - validateCompletedSequences(); return; } @@ -178,7 +180,6 @@ public boolean isMiddleOfModelGroup() { private void validateCompletedSequences() { if (unvisitedElements.isEmpty()) { isCompleted = true; - reset(); updateOccurrences(); } } From 885fb64f931e77116dd6101dd307588af00d0ac3 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 9 Nov 2024 01:22:27 +0530 Subject: [PATCH 14/58] Add complex tests with sequences with element annotation --- ..._sequence_test_with_element_annotation.bal | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index 80b4d73a..d9838afd 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -1,5 +1,6 @@ import ballerina/test; +// TODO: Add tests with attributes type XsdSequenceWithElementAnnotation record { @Sequence { minOccurs: 0, @@ -215,7 +216,7 @@ type XsdSequenceWithElementAnnotation3 record { minOccurs: 1, maxOccurs: 1 } - Seq_XsdSequenceWithElementAnnotation3_2 seq_XsdSequenceWithElementAnnotation3_2; + Seq_XsdSequenceWithElementAnnotation3_2 seq_XsdSequenceWithElementAnnotation3_2?; }; type Seq_XsdSequenceWithElementAnnotation3_1 record { @@ -233,19 +234,35 @@ type Seq_XsdSequenceWithElementAnnotation3_1 record { @Order {value: 2} Seq_B_3[] field2?; + @Element { + minOccurs: 1, + maxOccurs: 3 + } @Order {value: 3} Seq_C_3 field3; }; type Seq_XsdSequenceWithElementAnnotation3_2 record { @Order {value: 1} - Seq_D_3 field4; + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Seq_D_3[] field4?; + @Element { + minOccurs: 0, + maxOccurs: 3 + } @Order {value: 2} - Seq_E_3 field5; + Seq_E_3[] field5?; + @Element { + minOccurs: 0, + maxOccurs: 3 + } @Order {value: 3} - Seq_F_3 field6; + Seq_F_3[] field6?; }; type Seq_A_3 record { @@ -297,11 +314,19 @@ type Seq_F_3 record { }; type Seq_3 record { + @Element { + minOccurs: 0, + maxOccurs: 3 + } @Order {value: 1} - string a; + string[] a?; + @Element { + minOccurs: 0, + maxOccurs: 3 + } @Order {value: 2} - string b; + string[] b?; @Order {value: 3} string c; @@ -334,11 +359,43 @@ function testXsdSequenceWithElementAnnotation3() returns error? { string xmlStr; XsdSequenceWithElementAnnotation3|Error v2; - // xmlStr = string `123123123123123123123123123`; - // v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: "1", b: "2", c: "3"}}, {value1: {a: "1", b: "2", c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + xmlStr = string `123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}, {value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlStr = string `123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}]}}); + + xmlStr = string `123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlStr = string `2312312312323123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: [{value1: {a: ["1", "2", "3"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlStr = string `33123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {c: "3"}}, {value1: {c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}}); + + xmlStr = string `2312312323123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field3 not found in seq_XsdSequenceWithElementAnnotation3_1"), (v2).message()); + + xmlStr = string `231231231232312312`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'i' not present in XML"), (v2).message()); + + xmlStr = string `123123123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("field5 Element occurs more than the max allowed times"), (v2).message()); - xmlStr = string `123123123123123123`; + xmlStr = string `1231222223123123123123123123123`; v2 = parseString(xmlStr); - test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {a: "1", b: "2", c: "3"}}, {value1: {a: "1", b: "2", c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotation3_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("b Element occurs more than the max allowed times"), (v2).message()); } From 9f5384ccd5ca826b2738100f409b6ac62530e524 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 9 Nov 2024 20:47:14 +0530 Subject: [PATCH 15/58] Add array of sequences --- ballerina/tests/xsd_sequence_array_test.bal | 261 ++++++++++++++++++ ballerina/tests/xsd_sequence_tests.bal | 92 +++++- .../lib/data/xmldata/xml/XmlParser.java | 147 +++++++--- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 8 +- .../data/xmldata/xml/xsd/ModelGroupInfo.java | 7 +- .../data/xmldata/xml/xsd/SequenceInfo.java | 99 ++++--- 6 files changed, 517 insertions(+), 97 deletions(-) create mode 100644 ballerina/tests/xsd_sequence_array_test.bal diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal new file mode 100644 index 00000000..3b09e619 --- /dev/null +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -0,0 +1,261 @@ +import ballerina/test; + +type XsdSequenceArray record {| + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArray[] seq_XsdSequenceArray; +|}; + +type Seq_XsdSequenceArray record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArray() returns error? { + string xmlStr; + XsdSequenceArray|Error v; + + xmlStr = string `1311.11415.1`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XsdSequenceArray: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + + xmlStr = string `1311.11414.11515.1`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArray Element occurs more than the max allowed times")); +} + +type XsdSequenceArray2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArray2[] seq_XsdSequenceArray2; + + @Sequence { + minOccurs: 0, + maxOccurs: 2 + } + Seq_XsdSequenceArray2_2[] seq_XsdSequenceArray2_2 = []; +|}; + +type Seq_XsdSequenceArray2 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XsdSequenceArray2_2 record {| + @Order { + value: 1 + } + int age2; + + @Order { + value: 2 + } + float salary2; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArray2() returns error? { + string xmlStr; + XsdSequenceArray2|Error v; + + xmlStr = string `1311.11415.11311.11415.1`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XsdSequenceArray2: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}], seq_XsdSequenceArray2_2: [{age2: 13, salary2: 11.1}, {age2: 14, salary2: 15.1}]}); + + xmlStr = string `1311.11311.11415.11311.11415.1`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArray2 Element occurs more than the max allowed times")); + + xmlStr = string `1311.11415.11311.11311.11415.1`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArray2_2 Element occurs more than the max allowed times")); +} + +type XSDSequenceArrayRecord13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_XSDSequenceArrayRecord13_1[] seq_XSDSequenceArrayRecord13_1; + + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_XSDSequenceArrayRecord13_2[] seq_XSDSequenceArrayRecord13_2; +}; + +type Seq_XSDSequenceArrayRecord13_1 record { + @Order {value: 1} + Seq_Array_A_3 field1; + + @Order {value: 2} + Seq_Array_B_3 field2; + + @Order {value: 3} + Seq_Array_C_3 field3; +}; + +type Seq_XSDSequenceArrayRecord13_2 record { + @Order {value: 1} + Seq_Array_D_3 field4; + + @Order {value: 2} + Seq_Array_E_3 field5; + + @Order {value: 3} + Seq__Array_F_3 field6; +}; + +type Seq_Array_A_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_Array_3[] value1; +}; + +type Seq_Array_B_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq2_Array_3[] value2; +}; + +type Seq_Array_C_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq3_Array_3[] value3; +}; + +type Seq_Array_D_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_Array_3[] value1; +}; + +type Seq_Array_E_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq2_Array_3[] value2; +}; + +type Seq__Array_F_3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq3_Array_3[] value3; +}; + +type Seq_Array_3 record { + @Order {value: 1} + string a; + + @Order {value: 2} + string b; + + @Order {value: 3} + string c; +}; + +type Seq2_Array_3 record { + @Order {value: 1} + string d; + + @Order {value: 2} + string e; + + @Order {value: 3} + string f; +}; + +type Seq3_Array_3 record { + @Order {value: 1} + string g; + + @Order {value: 2} + string h; + + @Order {value: 3} + string i; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXSDSequenceArrayRecord4() returns error? { + string xmlStr = string `123123123123123123`; + XSDSequenceArrayRecord13|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceArrayRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + + xmlStr = string `123123123123123123123123123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceArrayRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + + xmlStr = string `123123123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceArrayRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}]}}]}); + + xmlStr = string `123123123123123123123123123123123123123123123123123123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("seq_XSDSequenceArrayRecord13_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `123123123123123123123123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value3 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `123123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + + xmlStr = string `123123123123121233123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + + xmlStr = string `123123123123123132`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element h is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `132123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element b is not in the correct order in"), msg = (v2).message()); + + xmlStr = string `123123123123123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field6, field4 not found in seq_XSDSequenceArrayRecord13_2"), msg = (v2).message()); +} diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 4f50e1d8..a5b066c0 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -50,7 +50,7 @@ function testXsdSequence() returns error? { xmlStr = string `11.113`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); + test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecord"), msg = (v).message()); // TODO: Create an issue // xmlStr = string `13`; @@ -59,6 +59,89 @@ function testXsdSequence() returns error? { // test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); } +type XSDSequenceRecordP2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordP2 seq_XSDSequenceRecordP2; +|}; + +type Seq_XSDSequenceRecordP2 record {| + @Element { + minOccurs: 1, + maxOccurs: 3 + } + @Order { + value: 1 + } + int[] age; + + @Order { + value: 2 + } + float salary; + + @Order { + value: 3 + } + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string[] name; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceP2() returns error? { + string xmlStr = string `1311.1ABC`; + XSDSequenceRecordP2|Error v = parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecordP2: {age: [13], salary: 11.1, name: ["ABC"]}}); + + xmlStr = string `13131311.1ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecordP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC"]}}); + + xmlStr = string `13ABC11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + + xmlStr = string `1313ABC11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + + xmlStr = string `1311.1ABC11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + + xmlStr = string `1313131311.1ABC11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("age Element occurs more than the max allowed times"), msg = (v).message()); + + xmlStr = string `11.1ABC13`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecordP2"), msg = (v).message()); + + xmlStr = string `13131311.1ABCABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XSDSequenceRecordP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC", "ABC"]}}); + + xmlStr = string `13131311.1ABCABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("name Element occurs more than the max allowed"), msg = (v).message()); + + xmlStr = string `131311.1ABC13`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element name, salary not found in seq_XSDSequenceRecordP2"), msg = (v).message()); +} + // TODO: Test with open records. type XSDSequenceRecord2 record {| @Sequence { @@ -341,12 +424,12 @@ function testXsdSequence6() returns error? { xmlStr = string `successSD1311.133`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element name is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element name not found in seq_XSDSequenceRecord6_2"), msg = (v2).message()); xmlStr = string `SDsuccess11.11333`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element age is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element age not found in seq_XSDSequenceRecord6_1"), msg = (v2).message()); xmlStr = string `SDsuccess11.13133`; v2 = parseString(xmlStr); @@ -964,5 +1047,6 @@ function testXsdSequence13() returns error? { xmlStr = string `123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field4 is not in the correct order in"), msg = (v2).message()); + // TODO: Fix the error message + test:assertTrue((v2).message().includes("Element field6, field4 not found in seq_XSDSequenceRecord13_2"), msg = (v2).message()); } 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 9faf602e..8e568050 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 @@ -511,6 +511,7 @@ private void validateRequiredFields(XmlParserData xmlParserData) { private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData) { QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); + // TODO: Check + 1, when seq present updateElementOccurrence(xmlParserData, elemQName); validateModelGroupStack(xmlParserData, elemQName, true); @@ -564,13 +565,20 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse Type referredFieldType = TypeUtils.getReferredType(fieldType); if (!xmlParserData.siblings.contains(elemQName)) { xmlParserData.siblings.put(elemQName, false); + } else if (DataUtils.isAnydataOrJson(referredFieldType.getTag()) && !(temp instanceof BArray)) { + BArray tempArray = DataUtils.createArrayValue(referredFieldType); + tempArray.append(temp); + currentNode.put(bFieldName, tempArray); } else { - if (DataUtils.isAnydataOrJson(referredFieldType.getTag()) && !(temp instanceof BArray)) { - BArray tempArray = DataUtils.createArrayValue(referredFieldType); - tempArray.append(temp); - currentNode.put(bFieldName, tempArray); - } else if (referredFieldType.getTag() != TypeTags.ARRAY_TAG) { - throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName); + if (!xmlParserData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); + if (referredFieldType.getTag() != TypeTags.ARRAY_TAG && modelGroup.getOccurences() == 0) { + throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName); + } + } else { + if (referredFieldType.getTag() != TypeTags.ARRAY_TAG) { + throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName); + } } } @@ -591,15 +599,22 @@ private void updateElementOccurrence(XmlParserData xmlParserData, QualifiedName private void validateModelGroupStack(XmlParserData xmlParserData, QualifiedName elemQName, boolean isStartElement) { + String localPart = elemQName.getLocalPart(); while (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - if ((!modelGroup.isElementContains(elemQName.getLocalPart()) + if ((!modelGroup.isElementContains(localPart) && !modelGroup.isMiddleOfModelGroup())) { validateModelGroup(modelGroup, xmlParserData); continue; } - if (modelGroup.isElementContains(elemQName.getLocalPart())) { - modelGroup.visit(elemQName.getLocalPart(), isStartElement); + + if (isStartElement && modelGroup.checkAndStartNewModelGroup(localPart)) { + validateModelGroup(modelGroup, xmlParserData); + return; + } + + if (modelGroup.isElementContains(localPart)) { + modelGroup.visit(localPart, isStartElement); } return; } @@ -631,58 +646,91 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, Hash // TODO: Optimize this with stream.findFirst() for (Map.Entry entry : sequenceInfo.entrySet()) { - String k = entry.getKey(); + String key = entry.getKey(); SequenceInfo sequence = entry.getValue(); + QualifiedName qualifiedName = QualifiedNameFactory.createQualifiedName( + "", key, "", xmlParserData.useSemanticEquality); // TODO: Validate Namespaces if (sequence.isElementContains(elemQName.getLocalPart())) { + Object temp = null; + Field field = xmlParserData.rootRecord.getFields().get(key); + if (visitedFields.contains(qualifiedName)) { + temp = xmlParserData.currentNode.get(StringUtils.fromString(key)); + } xmlParserData.modelGroupStack.push(sequence); - Field field = xmlParserData.rootRecord.getFields().get(k); // TODO: Test on arrays - fieldMap.remove(QualifiedNameFactory.createQualifiedName( - "", k, "", xmlParserData.useSemanticEquality)); + visitedFields.put(qualifiedName, field); + fieldMap.remove(qualifiedName); // TODO: temp != null for arrays and currentNode // TODO: Check for arrays as well Type referredType = TypeUtils.getReferredType(field.getFieldType()); - updateNextRecordForXsd(xmlParserData, k, referredType, (RecordType) referredType); + updateNextRecordForXsd(xmlParserData, key, referredType, temp, xmlParserData.currentNode); readElement(xmlStreamReader, xmlParserData); return; } } - - if (!hasElementFound && choiceInfo != null) { - for (Map.Entry entry : choiceInfo.entrySet()) { - String key = entry.getKey(); - ChoiceInfo choice = entry.getValue(); - - // TODO: Validate Namespaces - if (choice.isElementContains(elemQName.getLocalPart())) { - xmlParserData.modelGroupStack.push(choice); - Field field = xmlParserData.rootRecord.getFields().get(key); - // TODO: Test on arrays - fieldMap.remove(QualifiedNameFactory.createQualifiedName( - "", key, "", xmlParserData.useSemanticEquality)); - - // TODO: temp != null for arrays and currentNode - // TODO: Check for arrays as well - Type referredType = TypeUtils.getReferredType(field.getFieldType()); - updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType); - updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); - readElement(xmlStreamReader, xmlParserData); - return; - } - } - } } - } - private void updateNextRecordForXsd(XmlParserData xmlParserData, String fieldName, - Type fieldType, RecordType recordType) { +// if (!hasElementFound && choiceInfo != null) { +// for (Map.Entry entry : choiceInfo.entrySet()) { +// String key = entry.getKey(); +// ChoiceInfo choice = entry.getValue(); +// +// // TODO: Validate Namespaces +// if (choice.isElementContains(elemQName.getLocalPart())) { +// xmlParserData.modelGroupStack.push(choice); +// Field field = xmlParserData.rootRecord.getFields().get(key); +// // TODO: Test on arrays +// fieldMap.remove(QualifiedNameFactory.createQualifiedName( +// "", key, "", xmlParserData.useSemanticEquality)); +// Object temp = new Object(); +// +// // TODO: temp != null for arrays and currentNode +// // TODO: Check for arrays as well +// Type referredType = TypeUtils.getReferredType(field.getFieldType()); +// updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType, +// temp, xmlParserData.currentNode); +// updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); +// readElement(xmlStreamReader, xmlParserData); +// return; +// } +// } +// } + } + + private void updateNextRecordForXsd(XmlParserData xmlParserData, + String fieldName, Type fieldType, Object temp, BMap currentNode) { xmlParserData.recordTypeStack.push(xmlParserData.rootRecord); - xmlParserData.rootRecord = recordType; - xmlParserData.currentNode = updateNextValue(recordType, fieldName, fieldType, xmlParserData); + if (fieldType.getTag() == TypeTags.RECORD_TYPE_TAG) { + RecordType recType = (RecordType) fieldType; + xmlParserData.currentNode = updateNextValue(recType, fieldName, fieldType, xmlParserData); + xmlParserData.rootRecord = recType; + return; + } + + if (fieldType.getTag() == TypeTags.ARRAY_TAG) { + Type elementType = TypeUtils.getReferredType(((ArrayType) fieldType).getElementType()); + if (temp == null) { + xmlParserData.arrayIndexes.peek().put(fieldName, 0); + currentNode.put(StringUtils.fromString(fieldName), + ValueCreator.createArrayValue((ArrayType) fieldType)); + } else { + HashMap indexes = xmlParserData.arrayIndexes.peek(); + indexes.put(fieldName, indexes.get(fieldName) + 1); + } + + if (elementType.getTag() == TypeTags.RECORD_TYPE_TAG) { + RecordType recType = (RecordType) elementType; + xmlParserData.rootRecord = recType; + xmlParserData.currentNode = updateNextValue(recType, + fieldName, fieldType, xmlParserData); + return; + } + } + throw new RuntimeException("Not implemented"); } private void initializeNextValueBasedOnExpectedType(String fieldName, Type fieldType, Object temp, @@ -1303,7 +1351,7 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse for (BString fieldAnnotationKey: fieldAnnotation.keySet()) { String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - BMap fieldAnnotationValue = null; + BMap fieldAnnotationValue; if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); parserData.xmlElementInfo.peek().put(fieldName, @@ -1316,6 +1364,17 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse parserData.xsdSequenceInfo.peek().put(fieldName, new SequenceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else if (fieldType instanceof ArrayType arrayType) { + Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); + if (elementType instanceof RecordType recType) { + //TODO: Define a separate function + parserData.xsdSequenceInfo.peek().put(fieldName, + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } } else { throw new RuntimeException("Cannot include Sequence annotation into " + fieldName + " of type " + fieldType); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 9064dd57..7c958c52 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -5,10 +5,8 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; -import java.util.HashMap; import java.util.HashSet; import java.util.Set; -import java.util.Stack; public class ChoiceInfo implements ModelGroupInfo { public String fieldName; @@ -69,8 +67,8 @@ public void visit(String element, boolean isStartElement) { } @Override - public boolean isCompleted() { - return occurrences <= maxOccurs && occurrences >= minOccurs; + public int getOccurences() { + return occurrences; } @Override @@ -84,7 +82,7 @@ public boolean isMiddleOfModelGroup() { } @Override - public boolean isContainsAllRemaining(Stack> elementInfoStack) { + public boolean checkAndStartNewModelGroup(String element) { return false; } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index d0e5e801..62b9beb8 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -1,8 +1,5 @@ package io.ballerina.lib.data.xmldata.xml.xsd; -import java.util.HashMap; -import java.util.Stack; - public interface ModelGroupInfo { public void updateOccurrences(); public void validateMinOccurrences(); @@ -10,8 +7,8 @@ public interface ModelGroupInfo { public void reset(); public void visit(String element, boolean isStartElement); - public boolean isCompleted(); + public int getOccurences(); public boolean isElementContains(String elementName); public boolean isMiddleOfModelGroup(); - public boolean isContainsAllRemaining(Stack> elementInfoStack); + public boolean checkAndStartNewModelGroup(String element); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 240ad3ba..727a08b2 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -88,38 +88,19 @@ public void validateMinOccurrences() { public void validate() { validateCompletedSequences(); if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { - throw new RuntimeException("Element " + unvisitedElements.iterator().next() + " not found in " + fieldName); + throw new RuntimeException("Element " + getUnvisitedElements() + " not found in " + fieldName); } validateMinOccurrences(); reset(); } - private boolean containsAllOptionalElements(Stack> elementInfoStack) { - if (elementInfoStack.isEmpty()) { - return false; - } - - HashMap elementInfo = elementInfoStack.peek(); - Set unvisitedElementsTemp = new HashSet<>(unvisitedElements); - unvisitedElementsTemp.forEach(element -> { - if (elementInfo.containsKey(element)) { - ElementInfo elem = elementInfo.get(element); - if (elem.minOccurs == 0) { - unvisitedElements.remove(element); - } - } - }); - if (unvisitedElements.isEmpty()) { - updateOccurrences(); - return true; - } - return false; - } - @Override public void reset() { this.unvisitedElements.addAll(allElements); this.visitedElements.clear(); + this.currentPriority = -1L; + this.isCompleted = false; + this.isMiddleOfElement = false; } @Override @@ -139,8 +120,6 @@ public void visit(String element, boolean isStartElement) { if (this.unvisitedElements.contains(element)) { isMiddleOfElement = false; isCompleted = false; - - //TODO: Remove unvisitedElements variable and get it using set substraction this.unvisitedElements.remove(element); this.visitedElements.add(element); return; @@ -152,19 +131,9 @@ public void visit(String element, boolean isStartElement) { throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); } - private void compareSequencePriorityOrder(String element) { - Long elementPriority = elementPriorityOrder.get(element); - if (elementPriority != null) { - if (elementPriority < currentPriority) { - throw new RuntimeException("Element " + element + " is not in the correct order in " + fieldName); - } - currentPriority = elementPriority; - } - } - @Override - public boolean isCompleted() { - return this.isCompleted; + public int getOccurences() { + return this.occurrences; } @Override @@ -184,7 +153,59 @@ private void validateCompletedSequences() { } } - public boolean isContainsAllRemaining(Stack> elementInfoStack) { - return containsAllOptionalElements(elementInfoStack); + private void compareSequencePriorityOrder(String element) { + Long elementPriority = elementPriorityOrder.get(element); + if (elementPriority != null) { + if (elementPriority < currentPriority) { + throw new RuntimeException("Element " + element + " is not in the correct order in " + fieldName); + } + currentPriority = elementPriority; + } + } + + //TODO: Check + public boolean checkAndStartNewModelGroup(String element) { + if (!isElementContains(element)) { + return false; + } + Long elementPriority = elementPriorityOrder.get(element); + if (elementPriority != null) { + // TODO: Priority orders should be start from 1. + if (!isMiddleOfElement && elementPriority < currentPriority + && elementPriority == 1) { + return true; + } + } + return false; + } + + private boolean containsAllOptionalElements(Stack> elementInfoStack) { + if (elementInfoStack.isEmpty()) { + return false; + } + + HashMap elementInfo = elementInfoStack.peek(); + Set unvisitedElementsTemp = new HashSet<>(unvisitedElements); + unvisitedElementsTemp.forEach(element -> { + if (elementInfo.containsKey(element)) { + ElementInfo elem = elementInfo.get(element); + if (elem.minOccurs == 0) { + unvisitedElements.remove(element); + } + } + }); + if (unvisitedElements.isEmpty()) { + updateOccurrences(); + return true; + } + return false; + } + + private String getUnvisitedElements() { + StringBuilder unvisitedElementsStr = new StringBuilder(); + unvisitedElements.forEach(element -> unvisitedElementsStr.append(element).append(", ")); + String result = unvisitedElementsStr.toString(); + result = result.substring(0, result.length() - 2); + return result; } } From 64e85ad5c3312b928fb53a204a528f2227fe6875 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 9 Nov 2024 22:42:08 +0530 Subject: [PATCH 16/58] Add tests for sequences with namespaces --- ...equence_test_with_namespace_annotation.bal | 231 ++++++++++++++++++ ballerina/tests/xsd_sequence_tests.bal | 8 +- 2 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal new file mode 100644 index 00000000..cef82426 --- /dev/null +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -0,0 +1,231 @@ +import ballerina/test; + +// TODO: Add tests with attributes +type XsdSequenceWithNamespaceAnnotation record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1_NamespaceAnnotation seq_EA1_NamespaceAnnotation; +}; + +type Seq_EA1_NamespaceAnnotation record { + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Namespace { + uri: "example2.com", + prefix: "ea2" + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Namespace { + uri: "example2.com", + prefix: "ea2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Namespace { + uri: "example3.com", + prefix: "ea3" + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNamespaceAnnotation() returns error? { + string xmlStr; + XsdSequenceWithNamespaceAnnotation|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NamespaceAnnotation"), (v).message()); + + xmlStr = string `ABCABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1_NamespaceAnnotation: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotation":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotation":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotation":{EA3: ["AB", "AB"]}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotation":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdSequenceWithNamespaceAnnotation2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2_NamespaceAnnotation seq_EA2_NamespaceAnnotation; +}; + +type Seq_EA2_NamespaceAnnotation record { + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Namespace { + uri: "example1.com", + prefix: "ea1" + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Namespace { + uri: "example2.com", + prefix: "ea2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + + @Namespace { + uri: "example3.com", + prefix: "ea3" + } + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNamespaceAnnotation2() returns error? { + string xmlStr; + XsdSequenceWithNamespaceAnnotation2|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCABCD`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA2_NamespaceAnnotation: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotation": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotation": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotation": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotation": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABCCD`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index a5b066c0..9da2dd4f 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -903,10 +903,10 @@ function testXsdSequence12() returns error? { type XSDSequenceRecord13 record { @Sequence { - minOccurs: 1, + minOccurs: 0, maxOccurs: 1 } - Seq_XSDSequenceRecord13_1 seq_XSDSequenceRecord13_1; + Seq_XSDSequenceRecord13_1 seq_XSDSequenceRecord13_1?; @Sequence { minOccurs: 1, @@ -1024,6 +1024,10 @@ function testXsdSequence13() returns error? { XSDSequenceRecord13|Error v2 = parseString(xmlStr); test:assertEquals(v2, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + xmlStr = string `123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + xmlStr = string `123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); From 9574656168f4b305b3c5aba3ceac87d195d76e78 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 10 Nov 2024 12:52:03 +0530 Subject: [PATCH 17/58] Add xsd sequence iumplementation with name attribute --- ...equence_test_with_namespace_annotation.bal | 4 +- .../tests/xsd_test_with_name_annotation.bal | 213 ++++++++++++++++++ .../lib/data/xmldata/utils/DataUtils.java | 2 +- .../lib/data/xmldata/xml/XmlParser.java | 109 +++++---- .../lib/data/xmldata/xml/xsd/ElementInfo.java | 4 +- .../data/xmldata/xml/xsd/SequenceInfo.java | 68 ++++-- 6 files changed, 339 insertions(+), 61 deletions(-) create mode 100644 ballerina/tests/xsd_test_with_name_annotation.bal diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal index cef82426..30782461 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -19,8 +19,8 @@ type Seq_EA1_NamespaceAnnotation record { value: 1 } @Namespace { - uri: "example2.com", - prefix: "ea2" + uri: "example1.com", + prefix: "ea1" } string EA1?; diff --git a/ballerina/tests/xsd_test_with_name_annotation.bal b/ballerina/tests/xsd_test_with_name_annotation.bal new file mode 100644 index 00000000..29fcc491 --- /dev/null +++ b/ballerina/tests/xsd_test_with_name_annotation.bal @@ -0,0 +1,213 @@ +import ballerina/test; + +// TODO: Add tests with attributes +type XsdSequenceWithNameAnnotation record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1_NameAnnotation seq_EA1_NameAnnotation; +}; + +type Seq_EA1_NameAnnotation record { + + @Name {value: "A1"} + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Name {value: "A2"} + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Name {value: "A3"} + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNameAnnotation() returns error? { + string xmlStr; + XsdSequenceWithNameAnnotation|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NameAnnotation"), (v).message()); + + xmlStr = string `ABCABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1_NameAnnotation: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NameAnnotation":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NameAnnotation":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NameAnnotation":{EA3: ["AB", "AB"]}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1_NameAnnotation":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} + +type XsdSequenceWithNameAnnotation2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2_NameAnnotation seq_EA2_NameAnnotation; +}; + +type Seq_EA2_NameAnnotation record { + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Name {value: "A1"} + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Name {value: "A2"} + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + + @Name {value: "A3"} + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNameAnnotation2() returns error? { + string xmlStr; + XsdSequenceWithNameAnnotation2|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCABCD`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA2_NameAnnotation: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NameAnnotation": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NameAnnotation": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NameAnnotation": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2_NameAnnotation": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABCCD`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} 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 78021ebb..f22d563a 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 @@ -220,7 +220,7 @@ public static QualifiedName getFieldNameFromRecord(Map fieldAnn } @SuppressWarnings("unchecked") - private static String getModifiedName(Map fieldAnnotation, String attributeName) { + public static String getModifiedName(Map fieldAnnotation, String attributeName) { for (BString key : fieldAnnotation.keySet()) { if (isNameAnnotationKey(key.getValue())) { return ((Map) fieldAnnotation.get(key)).get(Constants.VALUE).toString(); 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 8e568050..121d30e6 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 @@ -646,8 +646,8 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, Hash // TODO: Optimize this with stream.findFirst() for (Map.Entry entry : sequenceInfo.entrySet()) { - String key = entry.getKey(); SequenceInfo sequence = entry.getValue(); + String key = entry.getKey(); QualifiedName qualifiedName = QualifiedNameFactory.createQualifiedName( "", key, "", xmlParserData.useSemanticEquality); @@ -1348,55 +1348,82 @@ private void initializeXsdInformation(RecordType recordType, XmlParserData parse if (key.contains(Constants.FIELD)) { String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); Map fieldAnnotation = (Map) annotations.get(annotationKey); + String xmlElementName = DataUtils.getModifiedName(fieldAnnotation, fieldName); for (BString fieldAnnotationKey: fieldAnnotation.keySet()) { String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - BMap fieldAnnotationValue; - if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { - fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - parserData.xmlElementInfo.peek().put(fieldName, - new ElementInfo(fieldName, fieldAnnotationValue)); - } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { - fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = TypeUtils.getReferredType(recordType - .getFields().get(fieldName).getFieldType()); - if (fieldType instanceof RecordType recType) { - parserData.xsdSequenceInfo.peek().put(fieldName, - new SequenceInfo(fieldName, - fieldAnnotationValue, recType, parserData.xmlElementInfo)); - } else if (fieldType instanceof ArrayType arrayType) { - Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); - if (elementType instanceof RecordType recType) { - //TODO: Define a separate function - parserData.xsdSequenceInfo.peek().put(fieldName, - new SequenceInfo(fieldName, - fieldAnnotationValue, recType, parserData.xmlElementInfo)); - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = TypeUtils.getReferredType(recordType - .getFields().get(fieldName).getFieldType()); - if (fieldType instanceof RecordType recType) { - parserData.xsdChoiceInfo.peek().put(fieldName, - new ChoiceInfo(fieldName, fieldAnnotationValue, recType)); - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } + handleModuleXsdAnnotations(fieldAnnotation, fieldAnnotationKeyStr, fieldName, + recordType, fieldAnnotationKey, xmlElementName, parserData); } } } } } + private void handleModuleXsdAnnotations(Map fieldAnnotation, String fieldAnnotationKeyStr, + String fieldName, RecordType recordType, BString fieldAnnotationKey, + String xmlElementName, XmlParserData parserData) { + if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { + handleModuleXsdElementAnnotation(fieldAnnotation, fieldAnnotationKey, + fieldName, xmlElementName, parserData); + } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + handleModuleXsdSequenceAnnotations(fieldAnnotation, fieldName, recordType, + fieldAnnotationKey, xmlElementName, parserData); + } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + handleModuleChoiceSequenceAnnotations(fieldAnnotation, fieldName, recordType, + fieldAnnotationKey, xmlElementName, parserData); + } + } + + private void handleModuleXsdElementAnnotation(Map fieldAnnotation, BString fieldAnnotationKey, + String fieldName, String xmlElementName, XmlParserData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + parserData.xmlElementInfo.peek().put(xmlElementName, + new ElementInfo(xmlElementName, fieldName, fieldAnnotationValue)); + } + + private void handleModuleXsdSequenceAnnotations(Map fieldAnnotation, String fieldName, + RecordType recordType, BString fieldAnnotationKey, + String xmlElementName, XmlParserData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); + if (fieldType instanceof RecordType recType) { + parserData.xsdSequenceInfo.peek().put(fieldName, + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else if (fieldType instanceof ArrayType arrayType) { + Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); + if (elementType instanceof RecordType recType) { + //TODO: Define a separate function + parserData.xsdSequenceInfo.peek().put(fieldName, + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } + + private void handleModuleChoiceSequenceAnnotations(Map fieldAnnotation, String fieldName, + RecordType recordType, BString fieldAnnotationKey, + String xmlElementName, XmlParserData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); + if (fieldType instanceof RecordType recType) { + parserData.xsdChoiceInfo.peek().put(fieldName, + new ChoiceInfo(fieldName, fieldAnnotationValue, recType)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } + private void validateCurrentElementInfo(XmlParserData xmlParserData) { if (!xmlParserData.xmlElementInfo.isEmpty()) { xmlParserData.xmlElementInfo.peek().forEach((key, value) -> value.validate()); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index e01da77d..32dfe0ea 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -6,12 +6,14 @@ public class ElementInfo { String name; + String fieldName; public long minOccurs; public long maxOccurs; public int occurrences; - public ElementInfo(String name, BMap element) { + public ElementInfo(String name, String fieldName, BMap element) { this.name = name; + this.fieldName = fieldName; if (element.containsKey(Constants.MIN_OCCURS)) { this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); } else { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 727a08b2..e54953d1 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -2,9 +2,11 @@ import io.ballerina.lib.data.xmldata.utils.Constants; import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -18,14 +20,15 @@ public class SequenceInfo implements ModelGroupInfo { public int occurrences; // TODO: Update to a hashset - private Set unvisitedElements = new HashSet<>(); - private Set visitedElements = new HashSet<>(); - private Set allElements = new HashSet<>(); - private Map elementPriorityOrder = new HashMap<>(); + private final Set unvisitedElements = new HashSet<>(); + private final Set visitedElements = new HashSet<>(); + private final Set allElements = new HashSet<>(); + private final Map elementPriorityOrder = new HashMap<>(); private boolean isCompleted = false; private boolean isMiddleOfElement = false; private long currentPriority = -1L; - private Stack> xmlElementInfo; + private final Stack> xmlElementInfo; + private final HashMap xmlElementnameMap = new HashMap<>(); public SequenceInfo(String fieldName, BMap element, RecordType fieldType, Stack> xmlElementInfo) { @@ -44,12 +47,46 @@ public SequenceInfo(String fieldName, BMap element, RecordType this.occurrences = 0; // TODO: Name Annotation not encountered - this.allElements.addAll(fieldType.getFields().keySet()); - this.unvisitedElements.addAll(fieldType.getFields().keySet()); + this.allElements.addAll(getXmlElementNames(fieldType)); + this.unvisitedElements.addAll(this.allElements); updatePriorityOrder(fieldType); this.xmlElementInfo = xmlElementInfo; } + private Collection getXmlElementNames(RecordType fieldType) { + HashSet elementNames = new HashSet<>(fieldType.getFields().keySet()); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + updateFieldSetWithName(fieldAnnotation, elementNames, fieldAnnotationKey, fieldName); + } + } + } + return elementNames; + } + + private void updateFieldSetWithName(Map fieldAnnotation, Set elementNames, + BString fieldAnnotationKey, String fieldName) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue + .getStringValue(Constants.VALUE)); + elementNames.remove(fieldName); + elementNames.add(xmlElementName); + xmlElementnameMap.put(xmlElementName, fieldName); + return; + } + xmlElementnameMap.put(fieldName, fieldName); + } + } + private void updatePriorityOrder(RecordType fieldType) { BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { @@ -60,9 +97,9 @@ private void updatePriorityOrder(RecordType fieldType) { for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - BMap fieldAnnotationValue = null; if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { - fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); this.elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); } } @@ -128,7 +165,7 @@ public void visit(String element, boolean isStartElement) { if (this.visitedElements.contains(element)) { return; } - throw new RuntimeException("Unexpected element " + element + " found in " + fieldName); + throw new RuntimeException("Unexpected element " + xmlElementnameMap.get(element) + " found in " + fieldName); } @Override @@ -157,7 +194,8 @@ private void compareSequencePriorityOrder(String element) { Long elementPriority = elementPriorityOrder.get(element); if (elementPriority != null) { if (elementPriority < currentPriority) { - throw new RuntimeException("Element " + element + " is not in the correct order in " + fieldName); + throw new RuntimeException("Element " + xmlElementnameMap.get(element) + + " is not in the correct order in " + fieldName); } currentPriority = elementPriority; } @@ -171,10 +209,8 @@ public boolean checkAndStartNewModelGroup(String element) { Long elementPriority = elementPriorityOrder.get(element); if (elementPriority != null) { // TODO: Priority orders should be start from 1. - if (!isMiddleOfElement && elementPriority < currentPriority - && elementPriority == 1) { - return true; - } + return !isMiddleOfElement && elementPriority < currentPriority + && elementPriority == 1; } return false; } @@ -203,7 +239,7 @@ private boolean containsAllOptionalElements(Stack> private String getUnvisitedElements() { StringBuilder unvisitedElementsStr = new StringBuilder(); - unvisitedElements.forEach(element -> unvisitedElementsStr.append(element).append(", ")); + unvisitedElements.forEach(element -> unvisitedElementsStr.append(xmlElementnameMap.get(element)).append(", ")); String result = unvisitedElementsStr.toString(); result = result.substring(0, result.length() - 2); return result; From 2b079864fbe213afa354c54507d4eb6dd65fef89 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 10 Nov 2024 21:53:46 +0530 Subject: [PATCH 18/58] Add initial implementation for XSD choice tags --- ballerina/tests/xsd_choice_test.bal | 114 ++++++++++++---- ballerina/tests/xsd_sequence_array_test.bal | 124 ++++++++++++++++++ ..._sequence_test_with_element_annotation.bal | 3 +- .../lib/data/xmldata/utils/DataUtils.java | 41 ++++++ .../lib/data/xmldata/xml/XmlParser.java | 124 +++++++----------- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 86 +++++++++--- .../lib/data/xmldata/xml/xsd/ElementInfo.java | 4 +- .../data/xmldata/xml/xsd/ModelGroupInfo.java | 18 +-- .../data/xmldata/xml/xsd/SequenceInfo.java | 99 +++++--------- 9 files changed, 407 insertions(+), 206 deletions(-) diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 000e7ac0..3482d940 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -1,29 +1,85 @@ -// import ballerina/test; - -// type XSDChoiceRecord record {| -// @Choice { -// minOccurs: 1, -// maxOccurs: 1 -// } -// Seq_XSDChoiceRecord seq_XSDChoiceRecord; -// |}; - -// type Seq_XSDChoiceRecord record {| -// int age?; -// float salary?; -// |}; - -// @test:Config -// function testXsdChoice() returns error? { -// string xmlStr = string `13`; -// XSDChoiceRecord v = parseString(xmlStr); -// test:assertEquals(v, {seq_XSDChoiceRecord: {age: 13}}); - -// xmlStr = string `13.5`; -// v = parseString(xmlStr); -// test:assertEquals(v, {seq_XSDChoiceRecord: {salary: 13.5}}); - -// xmlStr = string `1311.1`; -// v = parseString(xmlStr); -// test:assertEquals(v, {seq_XSDChoiceRecord: {age: 13, salary: 11.1}}); -// } \ No newline at end of file +import ballerina/test; + +type XSDChoiceRecord record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord choice_XSDChoiceRecord?; +|}; + +type Choice_XSDChoiceRecord record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoice() returns error? { + string xmlStr; + XSDChoiceRecord|Error v; + + xmlStr = string `13`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecord: {age: 13}}); + + xmlStr = string `13.5`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecord: {salary: 13.5}}); + + xmlStr = string `1311.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string ``; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs less than the min required times"), (v).message()); +} + +type XSDChoiceRecordP2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecordP2 choice_XSDChoiceRecordP2; +|}; + +type Choice_XSDChoiceRecordP2 record {| + @Element { + minOccurs: 1, + maxOccurs: 3 + } + int[] age?; + float salary?; + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string[] name?; +|}; + +@test:Config {groups: ["xsd", "xsd_Choice"]} +function testXsdChoiceP2() returns error? { + string xmlStr = string `13`; + XSDChoiceRecordP2|Error v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecordP2: {age: [13]}}); + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecordP2: {name: ["ABC"]}}); + + xmlStr = string `11.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecordP2: {salary: 11.1}}); + + xmlStr = string `13131311.1ABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlStr = string `13ABC11.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); +} diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 3b09e619..477d76b3 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -259,3 +259,127 @@ function testXSDSequenceArrayRecord4() returns error? { test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("Element field6, field4 not found in seq_XSDSequenceArrayRecord13_2"), msg = (v2).message()); } + +type XsdSequenceArray5 record {| + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XsdSequenceArray5[] seq_XsdSequenceArray5; +|}; + +type Seq_XsdSequenceArray5 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArray5() returns error? { + string xmlStr; + XsdSequenceArray5|Error v; + + xmlStr = string `1311.11415.1`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XsdSequenceArray5: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + + xmlStr = string `1311.11414.11515.1`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_XsdSequenceArray5: [{age: 13, salary: 11.1}, {age: 14, salary: 14.1}, {age: 15, salary: 15.1}]}); + + xmlStr = string `1311.11414.11515.11515.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_XsdSequenceArray5 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlStr = string `1311.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_XsdSequenceArray5 Element occurs less than the min required times"), msg = (v).message()); +} + +type XSDSequenceArrayRecord6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XSDSequenceArrayRecord6_1[] seq_XSDSequenceArrayRecord6_1; + + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XSDSequenceArrayRecord6_2[] seq_XSDSequenceArrayRecord6_2; +}; + +type Seq_XSDSequenceArrayRecord6_1 record { + @Order {value: 1} + Seq_Array_A_6 field1; + + @Order {value: 2} + Seq_Array_B_6 field2; +}; + +type Seq_XSDSequenceArrayRecord6_2 record { + @Order {value: 1} + Seq_Array_D_6 field4; + + @Order {value: 2} + Seq_Array_E_6 field5; +}; + +type Seq_Array_A_6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_Array_6[] value1; +}; + +type Seq_Array_B_6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq2_Array_6[] value2; +}; + +type Seq_Array_D_6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_Array_6[] value1; +}; + +type Seq_Array_E_6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq2_Array_6[] value2; +}; + +type Seq_Array_6 record { + @Order {value: 1} + string a; +}; + +type Seq2_Array_6 record { + @Order {value: 1} + string d; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXSDSequenceArrayRecord6() returns error? { + // TODO: Refactor sequence elements into a array + // string xmlStr = string `1111111111111111`; + // XSDSequenceArrayRecord6|Error v2 = parseString(xmlStr); + // test:assertEquals(v2, {seq_XSDSequenceArrayRecord6_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayRecord6_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); +} diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index d9838afd..74d868e9 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -377,7 +377,8 @@ function testXsdSequenceWithElementAnnotation3() returns error? { xmlStr = string `33123`; v2 = parseString(xmlStr); - test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotation3_1: {field1: [{value1: {c: "3"}}, {value1: {c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("seq_XsdSequenceWithElementAnnotation3_2 Element occurs less than the min required times"), (v2).message()); xmlStr = string `2312312323123123`; v2 = parseString(xmlStr); 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 f22d563a..878d8942 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 @@ -49,11 +49,14 @@ import java.io.IOException; import java.io.Reader; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.Stack; import javax.xml.namespace.QName; @@ -1013,6 +1016,44 @@ public static boolean isContainsUnionType(Type expType) { return false; } + + public static Collection getXmlElementNames(RecordType fieldType, + HashMap xmlElementNameMap) { + HashSet elementNames = new HashSet<>(fieldType.getFields().keySet()); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + updateFieldSetWithName(fieldAnnotation, elementNames, + fieldAnnotationKey, fieldName, xmlElementNameMap); + } + } + } + return elementNames; + } + + private static void updateFieldSetWithName(Map fieldAnnotation, Set elementNames, + BString fieldAnnotationKey, String fieldName, + HashMap xmlElementNameMap) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue + .getStringValue(Constants.VALUE)); + elementNames.remove(fieldName); + elementNames.add(xmlElementName); + xmlElementNameMap.put(xmlElementName, fieldName); + return; + } + xmlElementNameMap.put(fieldName, fieldName); + } + } + /** * Holds data required for the traversing. * 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 121d30e6..be556cb5 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 @@ -260,8 +260,7 @@ private void parseRootElement(XMLStreamReader xmlStreamReader, xmlParserData.currentNode = ValueCreator.createRecordValue(rootRecord.getPackage(), rootRecord.getName()); boolean useSemanticEquality = xmlParserData.useSemanticEquality; QualifiedName elementQName = getElementName(xmlStreamReader, useSemanticEquality); - xmlParserData.rootElement = - DataUtils.validateAndGetXmlNameFromRecordAnnotation(rootRecord, rootRecord.getName(), elementQName, + DataUtils.validateAndGetXmlNameFromRecordAnnotation(rootRecord, rootRecord.getName(), elementQName, useSemanticEquality); DataUtils.validateTypeNamespace(elementQName.getPrefix(), elementQName.getNamespaceURI(), rootRecord); @@ -527,17 +526,13 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse xmlParserData.currentField = currentField; if (currentField == null) { // TODO: In here assume that if XSD is present then no rest type can be there. - HashMap choiceInfo = null; - HashMap sequenceInfo = null; - if (!xmlParserData.xsdChoiceInfo.isEmpty()) { - choiceInfo = xmlParserData.xsdChoiceInfo.peek(); - } - if (!xmlParserData.xsdSequenceInfo.isEmpty()) { - sequenceInfo = xmlParserData.xsdSequenceInfo.peek(); + HashMap modelGroupInfo = null; + if (!xmlParserData.xsdModelGroupInfo.isEmpty()) { + modelGroupInfo = xmlParserData.xsdModelGroupInfo.peek(); } - if ((sequenceInfo != null && !sequenceInfo.isEmpty()) || (choiceInfo != null && !choiceInfo.isEmpty())) { - validateElementInXsdSequenceOrElement(elemQName, choiceInfo, sequenceInfo, + if (modelGroupInfo != null && !modelGroupInfo.isEmpty()) { + validateElementInXsdSequenceOrElement(elemQName, modelGroupInfo, xmlStreamReader, xmlParserData, xmlParserData.visitedFieldHierarchy.peek(), fieldMap); return; } @@ -572,7 +567,9 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse } else { if (!xmlParserData.modelGroupStack.isEmpty()) { ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - if (referredFieldType.getTag() != TypeTags.ARRAY_TAG && modelGroup.getOccurences() == 0) { + + // Check whether this is a model group array + if (referredFieldType.getTag() != TypeTags.ARRAY_TAG && modelGroup == null) { throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName); } } else { @@ -608,8 +605,8 @@ private void validateModelGroupStack(XmlParserData xmlParserData, continue; } - if (isStartElement && modelGroup.checkAndStartNewModelGroup(localPart)) { - validateModelGroup(modelGroup, xmlParserData); + if (isStartElement && modelGroup.predictStartNewModelGroup(localPart)) { + validateModelGroup(modelGroup, xmlParserData, false); return; } @@ -621,7 +618,14 @@ private void validateModelGroupStack(XmlParserData xmlParserData, } private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData) { + validateModelGroup(modelGroup, xmlParserData, true); + } + + private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData, boolean isTerminated) { modelGroup.validate(); + if (isTerminated) { + modelGroup.validateMinOccurrences(); + } xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); xmlParserData.modelGroupStack.pop(); xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); @@ -633,32 +637,29 @@ private void popElementStacksForValidatingGroup(XmlParserData xmlParserData) { popMappingTypeStacks(xmlParserData); xmlParserData.attributeHierarchy.pop(); xmlParserData.arrayIndexes.pop(); - xmlParserData.xsdSequenceInfo.pop(); - xmlParserData.xsdChoiceInfo.pop(); + xmlParserData.xsdModelGroupInfo.pop(); xmlParserData.xmlElementInfo.pop(); } - private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap choiceInfo, - HashMap sequenceInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, - QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { - boolean hasElementFound = false; - if (sequenceInfo != null) { - + private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, + HashMap modelGroupInfo, XMLStreamReader xmlStreamReader, + XmlParserData xmlParserData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { + if (modelGroupInfo != null) { // TODO: Optimize this with stream.findFirst() - for (Map.Entry entry : sequenceInfo.entrySet()) { - SequenceInfo sequence = entry.getValue(); + for (Map.Entry entry : modelGroupInfo.entrySet()) { + ModelGroupInfo modelGroupValue = entry.getValue(); String key = entry.getKey(); - QualifiedName qualifiedName = QualifiedNameFactory.createQualifiedName( - "", key, "", xmlParserData.useSemanticEquality); + QualifiedName qualifiedName = QualifiedNameFactory + .createQualifiedName("", key, "", xmlParserData.useSemanticEquality); // TODO: Validate Namespaces - if (sequence.isElementContains(elemQName.getLocalPart())) { + if (modelGroupValue.isElementContains(elemQName.getLocalPart())) { Object temp = null; Field field = xmlParserData.rootRecord.getFields().get(key); if (visitedFields.contains(qualifiedName)) { temp = xmlParserData.currentNode.get(StringUtils.fromString(key)); } - xmlParserData.modelGroupStack.push(sequence); + xmlParserData.modelGroupStack.push(modelGroupValue); // TODO: Test on arrays visitedFields.put(qualifiedName, field); @@ -673,32 +674,6 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, Hash } } } - -// if (!hasElementFound && choiceInfo != null) { -// for (Map.Entry entry : choiceInfo.entrySet()) { -// String key = entry.getKey(); -// ChoiceInfo choice = entry.getValue(); -// -// // TODO: Validate Namespaces -// if (choice.isElementContains(elemQName.getLocalPart())) { -// xmlParserData.modelGroupStack.push(choice); -// Field field = xmlParserData.rootRecord.getFields().get(key); -// // TODO: Test on arrays -// fieldMap.remove(QualifiedNameFactory.createQualifiedName( -// "", key, "", xmlParserData.useSemanticEquality)); -// Object temp = new Object(); -// -// // TODO: temp != null for arrays and currentNode -// // TODO: Check for arrays as well -// Type referredType = TypeUtils.getReferredType(field.getFieldType()); -// updateNextRecordForXsd(xmlParserData, key, referredType, (RecordType) referredType, -// temp, xmlParserData.currentNode); -// updateExpectedTypeStacks(xmlParserData.rootRecord, xmlParserData); -// readElement(xmlStreamReader, xmlParserData); -// return; -// } -// } -// } } private void updateNextRecordForXsd(XmlParserData xmlParserData, @@ -805,8 +780,7 @@ private BMap updateNextMappingValue(XmlParserData xmlParserData xmlParserData.fieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.recordTypeStack.push(null); - xmlParserData.xsdSequenceInfo.push(new HashMap<>()); - xmlParserData.xsdChoiceInfo.push(new HashMap<>()); + xmlParserData.xsdModelGroupInfo.push(new HashMap<>()); xmlParserData.xmlElementInfo.push(new HashMap<>()); BMap currentNode = xmlParserData.currentNode; Object temp = currentNode.get(StringUtils.fromString(fieldName)); @@ -876,8 +850,7 @@ private void updateExpectedTypeStacks(RecordType recordType, XmlParserData xmlPa xmlParserData.fieldHierarchy.push(new QualifiedNameMap<>(getAllFieldsInRecordType(recordType, xmlParserData))); xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.restTypes.push(recordType.getRestFieldType()); - xmlParserData.xsdChoiceInfo.push(new HashMap<>()); - xmlParserData.xsdSequenceInfo.push(new HashMap<>()); + xmlParserData.xsdModelGroupInfo.push(new HashMap<>()); xmlParserData.xmlElementInfo.push(new HashMap<>()); } @@ -889,8 +862,7 @@ private void popExpectedTypeStacks(XmlParserData xmlParserData) { } private void popXsdValidationStacks(XmlParserData xmlParserData) { - xmlParserData.xsdSequenceInfo.pop(); - xmlParserData.xsdChoiceInfo.pop(); + xmlParserData.xsdModelGroupInfo.pop(); xmlParserData.xmlElementInfo.pop(); } @@ -965,8 +937,7 @@ private void updateExpectedTypeStacksOfRestType(Type restType, XmlParserData xml xmlParserData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); xmlParserData.restTypes.push(restType); xmlParserData.arrayIndexes.push(new HashMap<>()); - xmlParserData.xsdSequenceInfo.push(new HashMap<>()); - xmlParserData.xsdChoiceInfo.push(new HashMap<>()); + xmlParserData.xsdModelGroupInfo.push(new HashMap<>()); xmlParserData.xmlElementInfo.push(new HashMap<>()); } } @@ -1368,10 +1339,10 @@ private void handleModuleXsdAnnotations(Map fieldAnnotation, St fieldName, xmlElementName, parserData); } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { handleModuleXsdSequenceAnnotations(fieldAnnotation, fieldName, recordType, - fieldAnnotationKey, xmlElementName, parserData); + fieldAnnotationKey, parserData); } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { handleModuleChoiceSequenceAnnotations(fieldAnnotation, fieldName, recordType, - fieldAnnotationKey, xmlElementName, parserData); + fieldAnnotationKey, parserData); } } @@ -1383,20 +1354,19 @@ private void handleModuleXsdElementAnnotation(Map fieldAnnotati } private void handleModuleXsdSequenceAnnotations(Map fieldAnnotation, String fieldName, - RecordType recordType, BString fieldAnnotationKey, - String xmlElementName, XmlParserData parserData) { + RecordType recordType, BString fieldAnnotationKey, XmlParserData parserData) { BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); Type fieldType = TypeUtils.getReferredType(recordType .getFields().get(fieldName).getFieldType()); if (fieldType instanceof RecordType recType) { - parserData.xsdSequenceInfo.peek().put(fieldName, + parserData.xsdModelGroupInfo.peek().put(fieldName, new SequenceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else if (fieldType instanceof ArrayType arrayType) { Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); if (elementType instanceof RecordType recType) { //TODO: Define a separate function - parserData.xsdSequenceInfo.peek().put(fieldName, + parserData.xsdModelGroupInfo.peek().put(fieldName, new SequenceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else { @@ -1410,14 +1380,13 @@ private void handleModuleXsdSequenceAnnotations(Map fieldAnnota } private void handleModuleChoiceSequenceAnnotations(Map fieldAnnotation, String fieldName, - RecordType recordType, BString fieldAnnotationKey, - String xmlElementName, XmlParserData parserData) { + RecordType recordType, BString fieldAnnotationKey, XmlParserData parserData) { BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); Type fieldType = TypeUtils.getReferredType(recordType .getFields().get(fieldName).getFieldType()); if (fieldType instanceof RecordType recType) { - parserData.xsdChoiceInfo.peek().put(fieldName, - new ChoiceInfo(fieldName, fieldAnnotationValue, recType)); + parserData.xsdModelGroupInfo.peek().put(fieldName, + new ChoiceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else { throw new RuntimeException("Cannot include Sequence annotation into " + fieldName + " of type " + fieldType); @@ -1437,9 +1406,10 @@ private void validateElementInfoStack(XmlParserData xmlParserData) { } private void validateModelGroupInfoStack(XmlParserData xmlParserData) { - while (!xmlParserData.modelGroupStack.isEmpty()) { - ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - validateModelGroup(modelGroup, xmlParserData); + while (!xmlParserData.xsdModelGroupInfo.isEmpty()) { + xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { + value.validateMinOccurrences(); + }); } } @@ -1469,7 +1439,6 @@ public static class XmlParserData { Stack> arrayIndexes = new Stack<>(); private RecordType rootRecord; private Field currentField; - private QualifiedName rootElement; private final Stack> parents = new Stack<>(); private QualifiedNameMap siblings = new QualifiedNameMap<>(new LinkedHashMap<>()); private BMap currentNode; @@ -1478,8 +1447,7 @@ public static class XmlParserData { private boolean allowDataProjection; private boolean useSemanticEquality; Stack> xmlElementInfo = new Stack<>(); - Stack> xsdSequenceInfo = new Stack<>(); - Stack> xsdChoiceInfo = new Stack<>(); + Stack> xsdModelGroupInfo = new Stack<>(); Stack modelGroupStack = new Stack<>(); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 7c958c52..3e0a094a 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -1,23 +1,31 @@ package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.lib.data.xmldata.utils.DataUtils; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.Stack; public class ChoiceInfo implements ModelGroupInfo { + + private final Stack> xmlElementInfo; public String fieldName; public long minOccurs; public long maxOccurs; public int occurrences; - public Set allElements = new HashSet<>(); - public Set visitedElements = new HashSet<>(); + public final Set allElements = new HashSet<>(); + public final Set visitedElements = new HashSet<>(); + private final HashMap xmlElementNameMap = new HashMap<>(); + private boolean isMiddleOfElement = false; - public ChoiceInfo(String fieldName, BMap element, RecordType fieldType) { + public ChoiceInfo(String fieldName, BMap element, RecordType fieldType, + Stack> xmlElementInfo) { this.fieldName = fieldName; if (element.containsKey(Constants.MIN_OCCURS)) { this.minOccurs = element.getIntValue(Constants.MIN_OCCURS); @@ -30,40 +38,63 @@ public ChoiceInfo(String fieldName, BMap element, RecordType fi } else { this.maxOccurs = Math.max(this.minOccurs, 1); } - this.occurrences = 1; - this.allElements.addAll(fieldType.getFields().keySet()); + this.occurrences = 0; + this.allElements.addAll(DataUtils.getXmlElementNames(fieldType, xmlElementNameMap)); + this.xmlElementInfo = xmlElementInfo; } public void updateOccurrences() { this.occurrences++; - if (this.occurrences > this.maxOccurs) { + if (occurrences > maxOccurs) { throw new RuntimeException(fieldName + " Element occurs more than the max allowed times"); } } - public void validateMinOccurrences() { - if (this.occurrences < this.minOccurs) { - throw new RuntimeException(fieldName + " Element occurs less than the min required times"); - } - } - @Override public void validate() { - validateMinOccurrences(); + validateCompletedChoice(); + markOtherElementsAsOptional(); + reset(); + } + + private void markOtherElementsAsOptional() { + if (!xmlElementInfo.isEmpty()) { + HashMap elementInfo = xmlElementInfo.peek(); + for (String element : allElements) { + if (!visitedElements.contains(element)) { + ElementInfo eleInfo = elementInfo.get(element); + if (eleInfo != null) { + eleInfo.isInsideChoice = true; + } + } + } + } } @Override public void reset() { this.visitedElements.clear(); + isMiddleOfElement = false; } @Override public void visit(String element, boolean isStartElement) { - this.visitedElements.add(element); - if (this.visitedElements.containsAll(this.allElements)) { - updateOccurrences(); - visitedElements.clear(); + if (isMiddleOfElement && isStartElement) { + return; + } + + isMiddleOfElement = isStartElement; + + if (isStartElement) { + return; } + + if (allElements.contains(element)) { + this.visitedElements.add(element); + return; + } + + throw new RuntimeException("Unexpected element " + xmlElementNameMap.get(element) + " found in " + fieldName); } @Override @@ -73,16 +104,29 @@ public int getOccurences() { @Override public boolean isElementContains(String elementName) { - return false; + return allElements.contains(elementName); } @Override public boolean isMiddleOfModelGroup() { - return false; + return isMiddleOfElement; } @Override - public boolean checkAndStartNewModelGroup(String element) { - return false; + public boolean predictStartNewModelGroup(String element) { + return !visitedElements.isEmpty() && !isMiddleOfElement; + } + + private void validateCompletedChoice() { + if (visitedElements.size() != 1) { + throw new RuntimeException("Only one element in " + fieldName + " should be present"); + } + updateOccurrences(); + } + + public void validateMinOccurrences() { + if (this.occurrences < this.minOccurs) { + throw new RuntimeException(fieldName + " Element occurs less than the min required times"); + } } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index 32dfe0ea..cd648b76 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -11,6 +11,8 @@ public class ElementInfo { public long maxOccurs; public int occurrences; + public boolean isInsideChoice = false; + public ElementInfo(String name, String fieldName, BMap element) { this.name = name; this.fieldName = fieldName; @@ -40,7 +42,7 @@ public void validate() { } private void validateMinOccurrences() { - if (this.occurrences < this.minOccurs) { + if (!isInsideChoice && this.occurrences < this.minOccurs) { throw new RuntimeException(name + " Element occurs less than the min required times"); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index 62b9beb8..862c69b5 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -1,14 +1,14 @@ package io.ballerina.lib.data.xmldata.xml.xsd; public interface ModelGroupInfo { - public void updateOccurrences(); - public void validateMinOccurrences(); - public void validate(); - public void reset(); + void updateOccurrences(); + void validate(); + void reset(); - public void visit(String element, boolean isStartElement); - public int getOccurences(); - public boolean isElementContains(String elementName); - public boolean isMiddleOfModelGroup(); - public boolean checkAndStartNewModelGroup(String element); + void visit(String element, boolean isStartElement); + int getOccurences(); + boolean isElementContains(String elementName); + boolean isMiddleOfModelGroup(); + boolean predictStartNewModelGroup(String element); + void validateMinOccurrences(); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index e54953d1..a3485914 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -1,12 +1,11 @@ package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.lib.data.xmldata.utils.DataUtils; import io.ballerina.runtime.api.types.RecordType; -import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -28,7 +27,7 @@ public class SequenceInfo implements ModelGroupInfo { private boolean isMiddleOfElement = false; private long currentPriority = -1L; private final Stack> xmlElementInfo; - private final HashMap xmlElementnameMap = new HashMap<>(); + private final HashMap xmlElementNameMap = new HashMap<>(); public SequenceInfo(String fieldName, BMap element, RecordType fieldType, Stack> xmlElementInfo) { @@ -47,67 +46,12 @@ public SequenceInfo(String fieldName, BMap element, RecordType this.occurrences = 0; // TODO: Name Annotation not encountered - this.allElements.addAll(getXmlElementNames(fieldType)); + this.allElements.addAll(DataUtils.getXmlElementNames(fieldType, xmlElementNameMap)); this.unvisitedElements.addAll(this.allElements); updatePriorityOrder(fieldType); this.xmlElementInfo = xmlElementInfo; } - private Collection getXmlElementNames(RecordType fieldType) { - HashSet elementNames = new HashSet<>(fieldType.getFields().keySet()); - BMap annotations = fieldType.getAnnotations(); - for (BString annotationKey : annotations.getKeys()) { - String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - updateFieldSetWithName(fieldAnnotation, elementNames, fieldAnnotationKey, fieldName); - } - } - } - return elementNames; - } - - private void updateFieldSetWithName(Map fieldAnnotation, Set elementNames, - BString fieldAnnotationKey, String fieldName) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { - BMap fieldAnnotationValue = - (BMap) fieldAnnotation.get(fieldAnnotationKey); - String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue - .getStringValue(Constants.VALUE)); - elementNames.remove(fieldName); - elementNames.add(xmlElementName); - xmlElementnameMap.put(xmlElementName, fieldName); - return; - } - xmlElementnameMap.put(fieldName, fieldName); - } - } - - private void updatePriorityOrder(RecordType fieldType) { - BMap annotations = fieldType.getAnnotations(); - for (BString annotationKey : annotations.getKeys()) { - String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { - BMap fieldAnnotationValue = - (BMap) fieldAnnotation.get(fieldAnnotationKey); - this.elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); - } - } - } - } - } - } - public void updateOccurrences() { this.occurrences++; if (this.occurrences > this.maxOccurs) { @@ -124,10 +68,6 @@ public void validateMinOccurrences() { @Override public void validate() { validateCompletedSequences(); - if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { - throw new RuntimeException("Element " + getUnvisitedElements() + " not found in " + fieldName); - } - validateMinOccurrences(); reset(); } @@ -165,7 +105,7 @@ public void visit(String element, boolean isStartElement) { if (this.visitedElements.contains(element)) { return; } - throw new RuntimeException("Unexpected element " + xmlElementnameMap.get(element) + " found in " + fieldName); + throw new RuntimeException("Unexpected element " + xmlElementNameMap.get(element) + " found in " + fieldName); } @Override @@ -188,13 +128,17 @@ private void validateCompletedSequences() { isCompleted = true; updateOccurrences(); } + + if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { + throw new RuntimeException("Element " + getUnvisitedElements() + " not found in " + fieldName); + } } private void compareSequencePriorityOrder(String element) { Long elementPriority = elementPriorityOrder.get(element); if (elementPriority != null) { if (elementPriority < currentPriority) { - throw new RuntimeException("Element " + xmlElementnameMap.get(element) + + throw new RuntimeException("Element " + xmlElementNameMap.get(element) + " is not in the correct order in " + fieldName); } currentPriority = elementPriority; @@ -202,7 +146,7 @@ private void compareSequencePriorityOrder(String element) { } //TODO: Check - public boolean checkAndStartNewModelGroup(String element) { + public boolean predictStartNewModelGroup(String element) { if (!isElementContains(element)) { return false; } @@ -239,9 +183,30 @@ private boolean containsAllOptionalElements(Stack> private String getUnvisitedElements() { StringBuilder unvisitedElementsStr = new StringBuilder(); - unvisitedElements.forEach(element -> unvisitedElementsStr.append(xmlElementnameMap.get(element)).append(", ")); + unvisitedElements.forEach(element -> unvisitedElementsStr.append(xmlElementNameMap.get(element)).append(", ")); String result = unvisitedElementsStr.toString(); result = result.substring(0, result.length() - 2); return result; } + + private void updatePriorityOrder(RecordType fieldType) { + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + this.elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); + } + } + } + } + } + } } From abceef5c96a2d5ca8a17ba2572c3419a8e969189 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 02:05:33 +0530 Subject: [PATCH 19/58] Refactor the sequence implementation --- ballerina/tests/xsd_sequence_array_test.bal | 6 +- ..._sequence_test_with_element_annotation.bal | 11 +- ...equence_test_with_namespace_annotation.bal | 3 + ballerina/tests/xsd_sequence_tests.bal | 36 +-- .../tests/xsd_test_with_name_annotation.bal | 3 + .../lib/data/xmldata/utils/DataUtils.java | 34 +++ .../data/xmldata/xml/xsd/SequenceInfo.java | 216 ++++++++++++------ 7 files changed, 216 insertions(+), 93 deletions(-) diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 477d76b3..26052dc3 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -247,17 +247,17 @@ function testXSDSequenceArrayRecord4() returns error? { xmlStr = string `123123123123123132`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element h is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); xmlStr = string `132123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element b is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); xmlStr = string `123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field6, field4 not found in seq_XSDSequenceArrayRecord13_2"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element field5 is not in the correct order in"), msg = (v2).message()); } type XsdSequenceArray5 record {| diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index 74d868e9..bbd81b40 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -44,10 +44,10 @@ function testXsdSequenceWithElementAnnotation() returns error? { string xmlStr; XsdSequenceWithElementAnnotation|Error v; - xmlStr = string `ABCABC`; - v = parseString(xmlStr); - test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); + // xmlStr = string `ABCABC`; + // v = parseString(xmlStr); + // test:assertTrue(v is Error); + // test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); xmlStr = string `ABCABCABABAB`; v = parseString(xmlStr); @@ -109,6 +109,9 @@ type XsdSequenceWithElementAnnotation2 record { }; type Seq_EA2 record { + @Order { + value: 1 + } record { @Element { maxOccurs: 1, diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal index 30782461..a8e7671c 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -121,6 +121,9 @@ type XsdSequenceWithNamespaceAnnotation2 record { }; type Seq_EA2_NamespaceAnnotation record { + @Order { + value: 1 + } record { @Element { maxOccurs: 1, diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index 9da2dd4f..f9f79862 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -33,7 +33,7 @@ function testXsdSequence() returns error? { test:assertTrue(v is Error); // TODO: Change error messageas // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); - test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecord"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecord"), msg = (v).message()); xmlStr = string `13`; v = parseString(xmlStr); @@ -50,7 +50,7 @@ function testXsdSequence() returns error? { xmlStr = string `11.113`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecord"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecord"), msg = (v).message()); // TODO: Create an issue // xmlStr = string `13`; @@ -94,8 +94,11 @@ type Seq_XSDSequenceRecordP2 record {| @test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdSequenceP2() returns error? { - string xmlStr = string `1311.1ABC`; - XSDSequenceRecordP2|Error v = parseString(xmlStr); + string xmlStr; + XSDSequenceRecordP2|Error v; + + xmlStr = string `1311.1ABC`; + v = parseString(xmlStr); test:assertEquals(v, {seq_XSDSequenceRecordP2: {age: [13], salary: 11.1, name: ["ABC"]}}); xmlStr = string `13131311.1ABC`; @@ -105,12 +108,12 @@ function testXsdSequenceP2() returns error? { xmlStr = string `13ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); xmlStr = string `1313ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); xmlStr = string `1311.1ABC11.1`; v = parseString(xmlStr); @@ -125,7 +128,7 @@ function testXsdSequenceP2() returns error? { xmlStr = string `11.1ABC13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element age not found in seq_XSDSequenceRecordP2"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); xmlStr = string `13131311.1ABCABC`; v = parseString(xmlStr); @@ -139,7 +142,7 @@ function testXsdSequenceP2() returns error? { xmlStr = string `131311.1ABC13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element name, salary not found in seq_XSDSequenceRecordP2"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary, name not found in seq_XSDSequenceRecordP2"), msg = (v).message()); } // TODO: Test with open records. @@ -424,17 +427,22 @@ function testXsdSequence6() returns error? { xmlStr = string `successSD1311.133`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element name not found in seq_XSDSequenceRecord6_2"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element status is not in the correct order in"), msg = (v2).message()); xmlStr = string `SDsuccess11.11333`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element age not found in seq_XSDSequenceRecord6_1"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), msg = (v2).message()); xmlStr = string `SDsuccess11.13133`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element age not found in"), (v2).message()); + test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), (v2).message()); + + xmlStr = string `SDsuccess11313.13`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } type XSDSequenceRecord7 record {| @@ -1041,16 +1049,16 @@ function testXsdSequence13() returns error? { xmlStr = string `123123123123123132`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element h is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); xmlStr = string `132123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element b is not in the correct order in"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); xmlStr = string `123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); // TODO: Fix the error message - test:assertTrue((v2).message().includes("Element field6, field4 not found in seq_XSDSequenceRecord13_2"), msg = (v2).message()); + test:assertTrue((v2).message().includes("Element field5 is not in the correct order in seq_XSDSequenceRecord13_2"), msg = (v2).message()); } diff --git a/ballerina/tests/xsd_test_with_name_annotation.bal b/ballerina/tests/xsd_test_with_name_annotation.bal index 29fcc491..21647af1 100644 --- a/ballerina/tests/xsd_test_with_name_annotation.bal +++ b/ballerina/tests/xsd_test_with_name_annotation.bal @@ -112,6 +112,9 @@ type XsdSequenceWithNameAnnotation2 record { }; type Seq_EA2_NameAnnotation record { + @Order { + value: 1 + } record { @Element { maxOccurs: 1, 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 878d8942..fe94e4ae 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 @@ -1054,6 +1054,40 @@ private static void updateFieldSetWithName(Map fieldAnnotation, } } + public static HashMap getXmlElementNameMap(RecordType fieldType) { + HashMap elementMap = new HashMap<>(); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + getXmlElementNameFromFieldAnnotation(fieldAnnotation, fieldAnnotationKey, fieldName, elementMap); + } + } + } + return elementMap; + } + + private static void getXmlElementNameFromFieldAnnotation(Map fieldAnnotation, + BString fieldAnnotationKey, String fieldName, + HashMap xmlElementNameMap) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue + .getStringValue(Constants.VALUE)); + if (xmlElementNameMap.containsKey(fieldName)) { + xmlElementNameMap.remove(fieldName); + } + xmlElementNameMap.put(xmlElementName, fieldName); + } + } + } + /** * Holds data required for the traversing. * diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index a3485914..2fd30461 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -6,11 +6,12 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Stack; +import java.util.stream.Collectors; public class SequenceInfo implements ModelGroupInfo { public String fieldName; @@ -19,15 +20,18 @@ public class SequenceInfo implements ModelGroupInfo { public int occurrences; // TODO: Update to a hashset - private final Set unvisitedElements = new HashSet<>(); - private final Set visitedElements = new HashSet<>(); - private final Set allElements = new HashSet<>(); - private final Map elementPriorityOrder = new HashMap<>(); + private final Map remainingElementCount = new HashMap<>(); + private final Map minimumElementCount = new HashMap<>(); + private final Map maxElementCount = new HashMap<>(); + private Map elementOptionality = new HashMap<>(); + private final List allElements = new ArrayList<>(); + int currentIndex = 0; + int elementCount; + String lastElement = ""; private boolean isCompleted = false; private boolean isMiddleOfElement = false; - private long currentPriority = -1L; private final Stack> xmlElementInfo; - private final HashMap xmlElementNameMap = new HashMap<>(); + private HashMap xmlElementNameMap = new HashMap<>(); public SequenceInfo(String fieldName, BMap element, RecordType fieldType, Stack> xmlElementInfo) { @@ -44,12 +48,24 @@ public SequenceInfo(String fieldName, BMap element, RecordType this.maxOccurs = Math.max(this.minOccurs, 1); } this.occurrences = 0; - - // TODO: Name Annotation not encountered - this.allElements.addAll(DataUtils.getXmlElementNames(fieldType, xmlElementNameMap)); - this.unvisitedElements.addAll(this.allElements); - updatePriorityOrder(fieldType); this.xmlElementInfo = xmlElementInfo; + updateUnvisitedElementsBasedOnPriorityOrder(fieldType); + this.xmlElementNameMap = DataUtils.getXmlElementNameMap(fieldType); + reOrderElementNamesBasedOnTheNameAnnotation(); + this.elementCount = allElements.size(); + } + + private void reOrderElementNamesBasedOnTheNameAnnotation() { + xmlElementNameMap.forEach((key, value) -> { + if (allElements.contains(value)) { + allElements.set(allElements.indexOf(value), key); + } + }); + allElements.forEach(element -> { + if (!xmlElementNameMap.containsKey(element)) { + xmlElementNameMap.put(element, element); + } + }); } public void updateOccurrences() { @@ -67,45 +83,34 @@ public void validateMinOccurrences() { @Override public void validate() { + generateElementOptionalityMapIfNotPresent(); validateCompletedSequences(); reset(); } @Override public void reset() { - this.unvisitedElements.addAll(allElements); - this.visitedElements.clear(); - this.currentPriority = -1L; this.isCompleted = false; this.isMiddleOfElement = false; + this.currentIndex = 0; + this.remainingElementCount.putAll(this.maxElementCount); + this.lastElement = ""; } @Override public void visit(String element, boolean isStartElement) { + generateElementOptionalityMapIfNotPresent(); if (isMiddleOfElement && isStartElement) { return; } isMiddleOfElement = isStartElement; - compareSequencePriorityOrder(element); - if (isStartElement) { isCompleted = false; return; } - if (this.unvisitedElements.contains(element)) { - isMiddleOfElement = false; - isCompleted = false; - this.unvisitedElements.remove(element); - this.visitedElements.add(element); - return; - } - - if (this.visitedElements.contains(element)) { - return; - } - throw new RuntimeException("Unexpected element " + xmlElementNameMap.get(element) + " found in " + fieldName); + checkElementOrderAndUpdateElementOccurences(element); } @Override @@ -123,73 +128,101 @@ public boolean isMiddleOfModelGroup() { return isMiddleOfElement; } - private void validateCompletedSequences() { - if (unvisitedElements.isEmpty()) { - isCompleted = true; - updateOccurrences(); + @Override + public boolean predictStartNewModelGroup(String element) { + generateElementOptionalityMapIfNotPresent(); + if (!isElementContains(element)) { + return false; + } + + boolean isFirstElement = element.equals(allElements.get(0)); + if (isFirstElement && currentIndex == 0 && remainingElementCount.get(allElements.get(0)) > 0) { + return false; } - if (!isCompleted && !containsAllOptionalElements(this.xmlElementInfo)) { + return !isMiddleOfElement && isFirstElement + && (isCompleted || containsAllOptionalElements()) + && !(lastElement.equals(element) && remainingElementCount.get(element) > 0); + } + + private void validateCompletedSequences() { + if (!isCompleted && !containsAllOptionalElements()) { throw new RuntimeException("Element " + getUnvisitedElements() + " not found in " + fieldName); } + updateOccurrences(); } - private void compareSequencePriorityOrder(String element) { - Long elementPriority = elementPriorityOrder.get(element); - if (elementPriority != null) { - if (elementPriority < currentPriority) { - throw new RuntimeException("Element " + xmlElementNameMap.get(element) + - " is not in the correct order in " + fieldName); + private boolean containsAllOptionalElements() { + for (int i = currentIndex; i < this.elementCount; i++) { + if (!elementOptionality.get(allElements.get(i))) { + return false; } - currentPriority = elementPriority; } + return true; } - //TODO: Check - public boolean predictStartNewModelGroup(String element) { - if (!isElementContains(element)) { - return false; - } - Long elementPriority = elementPriorityOrder.get(element); - if (elementPriority != null) { - // TODO: Priority orders should be start from 1. - return !isMiddleOfElement && elementPriority < currentPriority - && elementPriority == 1; + private void checkElementOrderAndUpdateElementOccurences(String element) { + String nextElement; + boolean isLastElement = false; + + if (element.equals(lastElement)) { + nextElement = lastElement; + isLastElement = true; + } else { + nextElement = allElements.get(currentIndex == this.elementCount ? currentIndex - 1 : currentIndex); } - return false; - } - private boolean containsAllOptionalElements(Stack> elementInfoStack) { - if (elementInfoStack.isEmpty()) { - return false; + while (!nextElement.equals(element)) { + if (!elementOptionality.get(nextElement)) { + throw new RuntimeException("Element " + xmlElementNameMap.get(element) + + " is not in the correct order in " + fieldName); + } + currentIndex++; + nextElement = allElements.get(currentIndex); + + if (currentIndex == this.elementCount) { + throw new RuntimeException("Element " + xmlElementNameMap.get(element) + + " is not in the correct order in " + fieldName); + } } - HashMap elementInfo = elementInfoStack.peek(); - Set unvisitedElementsTemp = new HashSet<>(unvisitedElements); - unvisitedElementsTemp.forEach(element -> { - if (elementInfo.containsKey(element)) { - ElementInfo elem = elementInfo.get(element); - if (elem.minOccurs == 0) { - unvisitedElements.remove(element); + if (remainingElementCount.get(nextElement) == 0) { + throw new RuntimeException("Element " + xmlElementNameMap.get(element) + + " occurs more than the max allowed times in " + fieldName); + } else { + remainingElementCount.put(element, remainingElementCount.get(nextElement) - 1); + int elementCount = maxElementCount.get(element) - remainingElementCount.get(element).intValue(); + + if (elementCount >= minimumElementCount.get(element) && !isLastElement + && currentIndex != this.elementCount) { + currentIndex++; + } else { + if (elementCount == 1) { + currentIndex++; } } - }); - if (unvisitedElements.isEmpty()) { - updateOccurrences(); - return true; + + if (currentIndex == this.elementCount && elementCount >= minimumElementCount.get(element)) { + isCompleted = true; + } } - return false; + lastElement = nextElement; } private String getUnvisitedElements() { StringBuilder unvisitedElementsStr = new StringBuilder(); - unvisitedElements.forEach(element -> unvisitedElementsStr.append(xmlElementNameMap.get(element)).append(", ")); + allElements.subList(currentIndex, this.elementCount).forEach(element -> { + if (!elementOptionality.get(element)) { + unvisitedElementsStr.append(xmlElementNameMap.get(element)).append(", "); + } + }); String result = unvisitedElementsStr.toString(); result = result.substring(0, result.length() - 2); return result; } - private void updatePriorityOrder(RecordType fieldType) { + private HashMap updatePriorityOrder(RecordType fieldType) { + HashMap elementPriorityOrder = new HashMap<>(); BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); @@ -202,11 +235,50 @@ private void updatePriorityOrder(RecordType fieldType) { if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - this.elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); + elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); } } } } } + return elementPriorityOrder; + } + + private void updateUnvisitedElementsBasedOnPriorityOrder(RecordType fieldType) { + this.allElements.addAll(updatePriorityOrder(fieldType).entrySet().stream() + .sorted(Map.Entry.comparingByValue()) // Sort by Long values in priority order + .map(entry -> entry.getKey()) // Get xml element name from + .collect(Collectors.toList())); + + this.currentIndex = 0; + } + + private void generateElementOptionalityMapIfNotPresent() { + if (elementOptionality.isEmpty()) { + if (!xmlElementInfo.isEmpty()) { + allElements.forEach(element -> { + HashMap elementInfo = xmlElementInfo.peek(); + if (elementInfo.containsKey(element)) { + ElementInfo info = elementInfo.get(element); + elementOptionality.put(element, info.minOccurs == 0); + remainingElementCount.put(element, (int) info.maxOccurs); + maxElementCount.put(element, (int) info.maxOccurs); + minimumElementCount.put(element, (int) info.minOccurs); + } else { + elementOptionality.put(element, false); + remainingElementCount.put(element, 1); + maxElementCount.put(element, 1); + minimumElementCount.put(element, 1); + } + }); + } else { + allElements.forEach(element -> { + elementOptionality.put(element, false); + remainingElementCount.put(element, 1); + maxElementCount.put(element, 1); + minimumElementCount.put(element, 1); + }); + } + } } } From 20a7935edba9bbc0f85958e3b0e1bfadf715aeb8 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 03:52:04 +0530 Subject: [PATCH 20/58] Add basic tests for choice element --- ballerina/tests/xsd_choice_test.bal | 201 ++++++++++++++++++ ballerina/tests/xsd_sequence_array_test.bal | 6 +- .../lib/data/xmldata/utils/DataUtils.java | 12 +- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 75 ++++++- .../data/xmldata/xml/xsd/SequenceInfo.java | 26 +-- 5 files changed, 291 insertions(+), 29 deletions(-) diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 3482d940..886cfd5e 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -31,6 +31,11 @@ function testXsdChoice() returns error? { test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); + xmlStr = string `11.111.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); + xmlStr = string ``; v = parseString(xmlStr); test:assertTrue(v is Error); @@ -59,6 +64,47 @@ type Choice_XSDChoiceRecordP2 record {| string[] name?; |}; +type XSDChoiceP1Record record {| + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_XSDChoiceP1Record choice_XSDChoiceP1Record?; +|}; + +type Choice_XSDChoiceP1Record record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceP1() returns error? { + string xmlStr; + XSDChoiceP1Record|Error v; + + xmlStr = string `13`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceP1Record: {age: 13}}); + + xmlStr = string `13.5`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceP1Record: {salary: 13.5}}); + + xmlStr = string `1311.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceP1Record Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `11.111.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceP1Record Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string ``; + v = parseString(xmlStr); + test:assertEquals(v, {}); +} + @test:Config {groups: ["xsd", "xsd_Choice"]} function testXsdChoiceP2() returns error? { string xmlStr = string `13`; @@ -83,3 +129,158 @@ function testXsdChoiceP2() returns error? { test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); } + +type XSDChoiceRecord2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord2 choice_XSDChoiceRecord2; + + int num; +|}; + +type Choice_XSDChoiceRecord2 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice2() returns error? { + string xmlStr = string `313`; + XSDChoiceRecord2|Error v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecord2: {age: 13}, num: 3}); + test:assertEquals((check v).choice_XSDChoiceRecord2.age, 13); + test:assertEquals((check v).num, 3); + + xmlStr = string `11.13`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XSDChoiceRecord2: {salary: 11.1}, num: 3}); + test:assertEquals((check v).choice_XSDChoiceRecord2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlStr = string `11.1133`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `13311.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); +} + +type XSDChoiceRecord3 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord3 choice_XSDChoiceRecord3; + + record{int n;} num; +|}; + +type Choice_XSDChoiceRecord3 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice3() returns error? { + string xmlStr = string `313`; + XSDChoiceRecord3|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord3: {age: 13}, num: {n: 3}}); + test:assertEquals((check v2).choice_XSDChoiceRecord3.age, 13); + test:assertEquals((check v2).num, {n: 3}); + + xmlStr = string `11.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord3: {salary: 11.1}, num: {n: 3}}); + + xmlStr = string `13311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); + + xmlStr = string `31311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); +} + +type XSDChoiceRecord4 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord4 choice_XSDChoiceRecord4; +|}; + +type Choice_XSDChoiceRecord4 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice4() returns error? { + string xmlStr = string `313`; + XSDChoiceRecord4|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord4: {age: 13}, num: {n: {n: 3}}}); + test:assertEquals((check v2).choice_XSDChoiceRecord4.age, 13); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlStr = string `11.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord4: {salary: 11.1}, num: {n: {n: 3}}}); + + xmlStr = string `13311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); + + xmlStr = string `31311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); +} + +type XSDChoiceRecord5 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord5 choice_XSDChoiceRecord5; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceRecord5 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice5() returns error? { + string xmlStr = string `3313`; + XSDChoiceRecord5|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord5: {age: 13}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlStr = string `311.13`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlStr = string `11.133`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlStr = string `133311.1`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord5 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `33`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord5 Element occurs less than the min required times"), (v2).message()); +} diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 26052dc3..2a65f8d7 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -379,7 +379,7 @@ type Seq2_Array_6 record { @test:Config {groups: ["xsd", "xsd_sequence"]} function testXSDSequenceArrayRecord6() returns error? { // TODO: Refactor sequence elements into a array - // string xmlStr = string `1111111111111111`; - // XSDSequenceArrayRecord6|Error v2 = parseString(xmlStr); - // test:assertEquals(v2, {seq_XSDSequenceArrayRecord6_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayRecord6_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + string xmlStr = string `1111111111111111`; + XSDSequenceArrayRecord6|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {"seq_XSDSequenceArrayRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); } 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 fe94e4ae..0df2e29f 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 @@ -1017,8 +1017,7 @@ public static boolean isContainsUnionType(Type expType) { } - public static Collection getXmlElementNames(RecordType fieldType, - HashMap xmlElementNameMap) { + public static Collection getXmlElementNames(RecordType fieldType) { HashSet elementNames = new HashSet<>(fieldType.getFields().keySet()); BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { @@ -1027,8 +1026,7 @@ public static Collection getXmlElementNames(RecordType fieldType, String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); Map fieldAnnotation = (Map) annotations.get(annotationKey); for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - updateFieldSetWithName(fieldAnnotation, elementNames, - fieldAnnotationKey, fieldName, xmlElementNameMap); + updateFieldSetWithName(fieldAnnotation, elementNames, fieldAnnotationKey, fieldName); } } } @@ -1036,8 +1034,7 @@ public static Collection getXmlElementNames(RecordType fieldType, } private static void updateFieldSetWithName(Map fieldAnnotation, Set elementNames, - BString fieldAnnotationKey, String fieldName, - HashMap xmlElementNameMap) { + BString fieldAnnotationKey, String fieldName) { String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { @@ -1047,10 +1044,7 @@ private static void updateFieldSetWithName(Map fieldAnnotation, .getStringValue(Constants.VALUE)); elementNames.remove(fieldName); elementNames.add(xmlElementName); - xmlElementNameMap.put(xmlElementName, fieldName); - return; } - xmlElementNameMap.put(fieldName, fieldName); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 3e0a094a..32722965 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -8,19 +8,25 @@ import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.Stack; public class ChoiceInfo implements ModelGroupInfo { private final Stack> xmlElementInfo; + private final Map remainingElementCount = new HashMap<>(); + private final Map minimumElementCount = new HashMap<>(); + private final Map maxElementCount = new HashMap<>(); + private final Map elementOptionality = new HashMap<>(); public String fieldName; public long minOccurs; public long maxOccurs; public int occurrences; + String lastElement = ""; public final Set allElements = new HashSet<>(); public final Set visitedElements = new HashSet<>(); - private final HashMap xmlElementNameMap = new HashMap<>(); + private final HashMap xmlElementNameMap; private boolean isMiddleOfElement = false; @@ -39,7 +45,9 @@ public ChoiceInfo(String fieldName, BMap element, RecordType fi this.maxOccurs = Math.max(this.minOccurs, 1); } this.occurrences = 0; - this.allElements.addAll(DataUtils.getXmlElementNames(fieldType, xmlElementNameMap)); + this.allElements.addAll(DataUtils.getXmlElementNames(fieldType)); + this.xmlElementNameMap = DataUtils.getXmlElementNameMap(fieldType); + reOrderElementNamesBasedOnTheNameAnnotation(); this.xmlElementInfo = xmlElementInfo; } @@ -52,6 +60,7 @@ public void updateOccurrences() { @Override public void validate() { + generateElementOptionalityMapIfNotPresent(); validateCompletedChoice(); markOtherElementsAsOptional(); reset(); @@ -75,10 +84,13 @@ private void markOtherElementsAsOptional() { public void reset() { this.visitedElements.clear(); isMiddleOfElement = false; + this.remainingElementCount.putAll(this.maxElementCount); + this.lastElement = ""; } @Override public void visit(String element, boolean isStartElement) { + generateElementOptionalityMapIfNotPresent(); if (isMiddleOfElement && isStartElement) { return; } @@ -89,8 +101,19 @@ public void visit(String element, boolean isStartElement) { return; } + if (visitedElements.contains(element)) { + if (remainingElementCount.get(element) == 0) { + throw new RuntimeException("Element " + xmlElementNameMap.get(element) + + " occurs more than the max allowed times"); + } + remainingElementCount.put(element, remainingElementCount.get(element) - 1); + return; + } + if (allElements.contains(element)) { this.visitedElements.add(element); + lastElement = element; + remainingElementCount.put(element, remainingElementCount.get(element) - 1); return; } @@ -114,11 +137,18 @@ public boolean isMiddleOfModelGroup() { @Override public boolean predictStartNewModelGroup(String element) { - return !visitedElements.isEmpty() && !isMiddleOfElement; + if (element.equals(lastElement) && remainingElementCount.get(element) > 0) { + return false; + } + return !isMiddleOfElement && !visitedElements.isEmpty(); } private void validateCompletedChoice() { - if (visitedElements.size() != 1) { + int elementCount = maxElementCount.get(lastElement) - remainingElementCount.get(lastElement); + if (elementCount < minimumElementCount.get(lastElement)) { + return; + } + if (visitedElements.size() > 1) { throw new RuntimeException("Only one element in " + fieldName + " should be present"); } updateOccurrences(); @@ -129,4 +159,41 @@ public void validateMinOccurrences() { throw new RuntimeException(fieldName + " Element occurs less than the min required times"); } } + + private void generateElementOptionalityMapIfNotPresent() { + if (elementOptionality.isEmpty()) { + if (!xmlElementInfo.isEmpty()) { + allElements.forEach(element -> { + HashMap elementInfo = xmlElementInfo.peek(); + if (elementInfo.containsKey(element)) { + ElementInfo info = elementInfo.get(element); + elementOptionality.put(element, info.minOccurs == 0); + remainingElementCount.put(element, (int) info.maxOccurs); + maxElementCount.put(element, (int) info.maxOccurs); + minimumElementCount.put(element, (int) info.minOccurs); + } else { + elementOptionality.put(element, false); + remainingElementCount.put(element, 1); + maxElementCount.put(element, 1); + minimumElementCount.put(element, 1); + } + }); + } else { + allElements.forEach(element -> { + elementOptionality.put(element, false); + remainingElementCount.put(element, 1); + maxElementCount.put(element, 1); + minimumElementCount.put(element, 1); + }); + } + } + } + + private void reOrderElementNamesBasedOnTheNameAnnotation() { + allElements.forEach(element -> { + if (!xmlElementNameMap.containsKey(element)) { + xmlElementNameMap.put(element, element); + } + }); + } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 2fd30461..93e930df 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -55,19 +55,6 @@ public SequenceInfo(String fieldName, BMap element, RecordType this.elementCount = allElements.size(); } - private void reOrderElementNamesBasedOnTheNameAnnotation() { - xmlElementNameMap.forEach((key, value) -> { - if (allElements.contains(value)) { - allElements.set(allElements.indexOf(value), key); - } - }); - allElements.forEach(element -> { - if (!xmlElementNameMap.containsKey(element)) { - xmlElementNameMap.put(element, element); - } - }); - } - public void updateOccurrences() { this.occurrences++; if (this.occurrences > this.maxOccurs) { @@ -281,4 +268,17 @@ private void generateElementOptionalityMapIfNotPresent() { } } } + + private void reOrderElementNamesBasedOnTheNameAnnotation() { + xmlElementNameMap.forEach((key, value) -> { + if (allElements.contains(value)) { + allElements.set(allElements.indexOf(value), key); + } + }); + allElements.forEach(element -> { + if (!xmlElementNameMap.containsKey(element)) { + xmlElementNameMap.put(element, element); + } + }); + } } From 51420e192ec8f82b188aed8ad8c189fbe81147c6 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 11:12:27 +0530 Subject: [PATCH 21/58] Add basic tests for XSD choice --- ballerina/tests/xsd_choice_test.bal | 412 ++++++++++++++++-- .../lib/data/xmldata/xml/XmlParser.java | 8 +- 2 files changed, 384 insertions(+), 36 deletions(-) diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 886cfd5e..19fc4a85 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -18,15 +18,15 @@ function testXsdChoice() returns error? { string xmlStr; XSDChoiceRecord|Error v; - xmlStr = string `13`; + xmlStr = string `10`; v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceRecord: {age: 13}}); + test:assertEquals(v, {choice_XSDChoiceRecord: {age: 10}}); - xmlStr = string `13.5`; + xmlStr = string `10.5`; v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceRecord: {salary: 13.5}}); + test:assertEquals(v, {choice_XSDChoiceRecord: {salary: 10.5}}); - xmlStr = string `1311.1`; + xmlStr = string `1011.1`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); @@ -82,15 +82,15 @@ function testXsdChoiceP1() returns error? { string xmlStr; XSDChoiceP1Record|Error v; - xmlStr = string `13`; + xmlStr = string `10`; v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceP1Record: {age: 13}}); + test:assertEquals(v, {choice_XSDChoiceP1Record: {age: 10}}); - xmlStr = string `13.5`; + xmlStr = string `10.5`; v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceP1Record: {salary: 13.5}}); + test:assertEquals(v, {choice_XSDChoiceP1Record: {salary: 10.5}}); - xmlStr = string `1311.1`; + xmlStr = string `1011.1`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceP1Record Element occurs more than the max allowed times"), (v).message()); @@ -107,9 +107,9 @@ function testXsdChoiceP1() returns error? { @test:Config {groups: ["xsd", "xsd_Choice"]} function testXsdChoiceP2() returns error? { - string xmlStr = string `13`; + string xmlStr = string `10`; XSDChoiceRecordP2|Error v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceRecordP2: {age: [13]}}); + test:assertEquals(v, {choice_XSDChoiceRecordP2: {age: [10]}}); xmlStr = string `ABC`; v = parseString(xmlStr); @@ -119,12 +119,12 @@ function testXsdChoiceP2() returns error? { v = parseString(xmlStr); test:assertEquals(v, {choice_XSDChoiceRecordP2: {salary: 11.1}}); - xmlStr = string `13131311.1ABC`; + xmlStr = string `10101011.1ABC`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); - xmlStr = string `13ABC11.1`; + xmlStr = string `10ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); @@ -147,10 +147,10 @@ type Choice_XSDChoiceRecord2 record {| @test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdChoice2() returns error? { - string xmlStr = string `313`; + string xmlStr = string `310`; XSDChoiceRecord2|Error v = parseString(xmlStr); - test:assertEquals(v, {choice_XSDChoiceRecord2: {age: 13}, num: 3}); - test:assertEquals((check v).choice_XSDChoiceRecord2.age, 13); + test:assertEquals(v, {choice_XSDChoiceRecord2: {age: 10}, num: 3}); + test:assertEquals((check v).choice_XSDChoiceRecord2.age, 10); test:assertEquals((check v).num, 3); xmlStr = string `11.13`; @@ -159,12 +159,12 @@ function testXsdChoice2() returns error? { test:assertEquals((check v).choice_XSDChoiceRecord2.salary, 11.1); test:assertEquals((check v).num, 3); - xmlStr = string `11.1133`; + xmlStr = string `11.1103`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); - xmlStr = string `13311.1`; + xmlStr = string `10311.1`; v = parseString(xmlStr); test:assertTrue(v is Error); test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); @@ -187,22 +187,22 @@ type Choice_XSDChoiceRecord3 record {| @test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdChoice3() returns error? { - string xmlStr = string `313`; + string xmlStr = string `310`; XSDChoiceRecord3|Error v2 = parseString(xmlStr); - test:assertEquals(v2, {choice_XSDChoiceRecord3: {age: 13}, num: {n: 3}}); - test:assertEquals((check v2).choice_XSDChoiceRecord3.age, 13); + test:assertEquals(v2, {choice_XSDChoiceRecord3: {age: 10}, num: {n: 3}}); + test:assertEquals((check v2).choice_XSDChoiceRecord3.age, 10); test:assertEquals((check v2).num, {n: 3}); xmlStr = string `11.13`; v2 = parseString(xmlStr); test:assertEquals(v2, {choice_XSDChoiceRecord3: {salary: 11.1}, num: {n: 3}}); - xmlStr = string `13311.1`; + xmlStr = string `10311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); - xmlStr = string `31311.1`; + xmlStr = string `31011.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); @@ -224,22 +224,22 @@ type Choice_XSDChoiceRecord4 record {| @test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdChoice4() returns error? { - string xmlStr = string `313`; + string xmlStr = string `310`; XSDChoiceRecord4|Error v2 = parseString(xmlStr); - test:assertEquals(v2, {choice_XSDChoiceRecord4: {age: 13}, num: {n: {n: 3}}}); - test:assertEquals((check v2).choice_XSDChoiceRecord4.age, 13); + test:assertEquals(v2, {choice_XSDChoiceRecord4: {age: 10}, num: {n: {n: 3}}}); + test:assertEquals((check v2).choice_XSDChoiceRecord4.age, 10); test:assertEquals((check v2).num, {n: {n: 3}}); xmlStr = string `11.13`; v2 = parseString(xmlStr); test:assertEquals(v2, {choice_XSDChoiceRecord4: {salary: 11.1}, num: {n: {n: 3}}}); - xmlStr = string `13311.1`; + xmlStr = string `10311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); - xmlStr = string `31311.1`; + xmlStr = string `31011.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); @@ -262,9 +262,9 @@ type Choice_XSDChoiceRecord5 record {| @test:Config {groups: ["xsd", "xsd_sequence"]} function testXsdChoice5() returns error? { - string xmlStr = string `3313`; + string xmlStr = string `3310`; XSDChoiceRecord5|Error v2 = parseString(xmlStr); - test:assertEquals(v2, {choice_XSDChoiceRecord5: {age: 13}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals(v2, {choice_XSDChoiceRecord5: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); xmlStr = string `311.13`; v2 = parseString(xmlStr); @@ -274,13 +274,357 @@ function testXsdChoice5() returns error? { v2 = parseString(xmlStr); test:assertEquals(v2, {choice_XSDChoiceRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); - xmlStr = string `133311.1`; + xmlStr = string `103311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); test:assertTrue((v2).message().includes("choice_XSDChoiceRecord5 Element occurs more than the max allowed times"), (v2).message()); - xmlStr = string `33`; + xmlStr = string `33`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord5 Element occurs less than the min required times"), (v2).message()); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord5' not present in XML"), (v2).message()); +} + +type XSDChoiceRecord6 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord6_1 choice_XSDChoiceRecord6_1; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord6_2 choice_XSDChoiceRecord6_2; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceRecord6_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceRecord6_2 record {| + string name?; + string status?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice6() returns error? { + string xmlStr = string `3success310`; + XSDChoiceRecord6|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord6_1: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceRecord6_2: {status: "success"}}); + test:assertEquals((check v2).choice_XSDChoiceRecord6_1.age, 10); + test:assertEquals((check v2).choice_XSDChoiceRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlStr = string `SD33`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord6_1' not present in XML"), msg = (v2).message()); + + xmlStr = string `33SD`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord6_1' not present in XML"), msg = (v2).message()); + + xmlStr = string `SDsuccess33`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord6_2 Element occurs more than the max allowed times"), msg = (v2).message()); +} + +type XSDChoiceRecord7 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord7_1 choice_XSDChoiceRecord7_1; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord7_2 choice_XSDChoiceRecord7_2; +|}; + +type Choice_XSDChoiceRecord7_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceRecord7_2 record {| + string name?; + string status?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice7() returns error? { + string xmlStr = string `success11.1`; + XSDChoiceRecord7|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord7_1: {salary: 11.1}, choice_XSDChoiceRecord7_2: {status: "success"}}); +} + +type XSDChoiceRecord8 record {| + XSDChoiceRecord8P test; + int a; +|}; + +type XSDChoiceRecord8P record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord8_1 choice_XSDChoiceRecord8_1; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord8_2 choice_XSDChoiceRecord8_2; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceRecord8_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceRecord8_2 record {| + RecChoice8 name?; + RecChoice8 status?; +|}; + +type RecChoice8 record {| + string value1; + string value2; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice8() returns error? { + string xmlStr; + XSDChoiceRecord8|Error v2; + + xmlStr = string `3SDAB3102`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceRecord8_1: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceRecord8_2: {name: {value1: "SD", value2: "AB"}}}}); + test:assertEquals((check v2).test.choice_XSDChoiceRecord8_1.age, 10); + test:assertEquals((check v2).test.choice_XSDChoiceRecord8_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `SuccessFail3311.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceRecord8_1: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceRecord8_2: {status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.choice_XSDChoiceRecord8_1.salary, 11.1); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlStr = string `33SuccessFail11.12`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceRecord8_1: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceRecord8_2: {status: {value1: "Success", value2: "Fail"}}}}); + + xmlStr = string `SDAB10SuccessFail11.12`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `10SuccessFail11.12`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `102`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("failed to parse xml: choice_XSDChoiceRecord8_2 Element occurs less than the min required times")); + + xmlStr = string `SuccessFail2`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_1 Element occurs less than the min required times"), (v2).message()); +} + +type XSDChoiceRecord9 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord9_1 choice_XSDChoiceRecord9_1; +}; + +type Choice_XSDChoiceRecord9_1 record { + Choice_A field1?; + Choice_B field2?; + Choice_C field3?; +}; + +type Choice_A record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice value1; +}; + +type Choice_B record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice value2; +}; + +type Choice_C record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice value3; +}; + +type Choice record { + string a?; + string b?; + string c?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice9() returns error? { + string xmlStr; + XSDChoiceRecord9|Error v2; + + xmlStr = string `1`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord9_1: {field2: {value2: {a: "1"}}}}); + + xmlStr = string `1`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord9_1: {field3: {value3: {c: "1"}}}}); + + xmlStr = string `11`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord9_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `11`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value1 Element occurs more than the max allowed times"), (v2).message()); +} + +type XSDChoiceRecord10 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_XSDChoiceRecord10_1 choice_XSDChoiceRecord10_1?; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceRecord10_2 choice_XSDChoiceRecord10_2; +}; + +type Choice_XSDChoiceRecord10_1 record { + Choice_A_10 field1?; + Choice_B_10 field2?; + Choice_C_10 field3?; +}; + +type Choice_XSDChoiceRecord10_2 record { + Choice_D_10 field4?; + Choice_E_10 field5?; + Choice_F_10 field6?; +}; + +type Choice_A_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_10 value1; +}; + +type Choice_B_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice2_10 value2; +}; + +type Choice_C_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice3_10 value3; +}; + +type Choice_D_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_10 value1; +}; + +type Choice_E_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice2_10 value2; +}; + +type Choice_F_10 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice3_10 value3; +}; + +type Choice_10 record { + string a?; + string b?; + string c?; +}; + +type Choice2_10 record { + string d?; + string e?; + string f?; +}; + +type Choice3_10 record { + string g?; + string h?; + string i?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoice10() returns error? { + string xmlStr = string `12`; + XSDChoiceRecord10|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceRecord10_1: {field1: {value1: {a: "1"}}}, choice_XSDChoiceRecord10_2: {field5: {value2: {"d": "2"}}}}); + + xmlStr = string `112`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord10_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `122`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord10_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlStr = string `112`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value2 Element occurs more than the max allowed times"), (v2).message()); } 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 be556cb5..8b96cff5 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 @@ -637,7 +637,9 @@ private void popElementStacksForValidatingGroup(XmlParserData xmlParserData) { popMappingTypeStacks(xmlParserData); xmlParserData.attributeHierarchy.pop(); xmlParserData.arrayIndexes.pop(); - xmlParserData.xsdModelGroupInfo.pop(); + xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { + value.validateMinOccurrences(); + });; xmlParserData.xmlElementInfo.pop(); } @@ -862,7 +864,9 @@ private void popExpectedTypeStacks(XmlParserData xmlParserData) { } private void popXsdValidationStacks(XmlParserData xmlParserData) { - xmlParserData.xsdModelGroupInfo.pop(); + xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { + value.validateMinOccurrences(); + });; xmlParserData.xmlElementInfo.pop(); } From a199c569f60024336558d7accb5184881f5acfa7 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 16:17:10 +0530 Subject: [PATCH 22/58] Add xsd choice array tests --- ballerina/tests/xsd_choice_array_test.bal | 319 ++++++++++++++++++ .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 33 +- 2 files changed, 334 insertions(+), 18 deletions(-) create mode 100644 ballerina/tests/xsd_choice_array_test.bal diff --git a/ballerina/tests/xsd_choice_array_test.bal b/ballerina/tests/xsd_choice_array_test.bal new file mode 100644 index 00000000..2092bb13 --- /dev/null +++ b/ballerina/tests/xsd_choice_array_test.bal @@ -0,0 +1,319 @@ +import ballerina/test; + +type XsdChoiceArray record {| + @Choice { + minOccurs: 1, + maxOccurs: 2 + } + Choice_XsdChoiceArray choice_XsdChoiceArray; +|}; + +type Choice_XsdChoiceArray record {| + int[] age?; + float[] salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArray() returns error? { + string xmlStr; + XsdChoiceArray|Error v; + + xmlStr = string `1311.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray: {age: [13], salary: [11.1]}}); + + xmlStr = string `1312`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray: {age: [13, 12]}}); + + xmlStr = string `11.112.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray: {salary: [11.1, 12.1]}}); + + xmlStr = string `1311.11414.11515.1`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray Element occurs more than the max allowed times")); +} + +type XsdChoiceArray2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 2 + } + Choice_XsdChoiceArray2 choice_XsdChoiceArray2; + + @Choice { + minOccurs: 0, + maxOccurs: 2 + } + Choice_XsdChoiceArray2_2 choice_XsdChoiceArray2_2?; +|}; + +type Choice_XsdChoiceArray2 record {| + int[] age?; + float[] salary?; +|}; + +type Choice_XsdChoiceArray2_2 record {| + int[] age2?; + float[] salary2?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArray2() returns error? { + string xmlStr; + XsdChoiceArray2|Error v; + + xmlStr = string `1311.11311.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray2: {age: [13], salary: [11.1]}, choice_XsdChoiceArray2_2: {age2: [13], salary2: [11.1]}}); + + xmlStr = string `13131313`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray2: {age: [13, 13]}, choice_XsdChoiceArray2_2: {age2: [13, 13]}}); + + xmlStr = string `13131311.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray2: {age: [13, 13]}, choice_XsdChoiceArray2_2: {age2: [13], salary2: [11.1]}}); + + xmlStr = string `13131311.11415`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray2_2 Element occurs more than the max allowed times")); + + xmlStr = string `13131313`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray2 Element occurs more than the max allowed times")); + + xmlStr = string `1313`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray2: {age: [13, 13]}}); + + xmlStr = string `1313`; + v = parseString(xmlStr); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray2' not present in XML"), (v).message()); +} + +type XSDChoiceArrayRecord13 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_XSDChoiceArrayRecord13_1 choice_XSDChoiceArrayRecord13_1; + + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_XSDChoiceArrayRecord13_2 choice_XSDChoiceArrayRecord13_2; +}; + +type Choice_XSDChoiceArrayRecord13_1 record { + Choice_Array_A_3[] field1?; + Choice_Array_B_3[] field2?; + Choice_Array_C_3[] field3?; +}; + +type Choice_XSDChoiceArrayRecord13_2 record { + Choice_Array_D_3[] field4?; + Choice_Array_E_3[] field5?; + Choice__Array_F_3[] field6?; +}; + +type Choice_Array_A_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_Array_3 value1; +}; + +type Choice_Array_B_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice2_Array_3 value2; +}; + +type Choice_Array_C_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice3_Array_3 value3; +}; + +type Choice_Array_D_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_Array_3 value1; +}; + +type Choice_Array_E_3 record { + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice2_Array_3 value2; +}; + +type Choice__Array_F_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice3_Array_3 value3; +}; + +type Choice_Array_3 record { + string[] a?; + string[] b?; + string[] c?; +}; + +type Choice2_Array_3 record { + string[] d?; + string[] e?; + string[] f?; +}; + +type Choice3_Array_3 record { + string[] g?; + string[] h?; + string[] i?; +}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXSDChoiceArrayRecord4() returns error? { + string xmlStr = string `123123123123123123`; + XSDChoiceArrayRecord13|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceArrayRecord13_1: {field1: [{value1: {a: ["1"], b:["2"], c: ["3"]}}],field2: [{value2: {d: ["1"], e: ["2"], f: ["3"]}}], field3: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}, choice_XSDChoiceArrayRecord13_2: {field4: [{value1: {a: ["1"], b: ["2"], c: ["3"]}}], field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + + xmlStr = string `22123233123123123`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceArrayRecord13_1: {field1: [{value1: {b:["2", "2"]}}, {value1: {a: ["1"], b:["2"], c: ["3"]}}], field3: [{value3: {h: ["2"], i: ["3", "3"]}}]}, choice_XSDChoiceArrayRecord13_2: {field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}, {value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + + xmlStr = string `22123233123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is error); + test:assertTrue((v2).message().includes("choice_XSDChoiceArrayRecord13_2 Element occurs less than the min required times"), (v2).message()); + + xmlStr = string `221232331123123`; + v2 = parseString(xmlStr); + test:assertTrue(v2 is error); + test:assertTrue((v2).message().includes("value2 Element occurs less than the min required times"), (v2).message()); +} + +type XsdChoiceArray5 record {| + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_XsdChoiceArray5 choice_XsdChoiceArray5; +|}; + +type Choice_XsdChoiceArray5 record {| + int[] age?; + float[] salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArray5() returns error? { + string xmlStr; + XsdChoiceArray5|Error v; + + xmlStr = string `1311.114`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray5: {age: [13, 14], salary: [11.1]}}); + + xmlStr = string `1311.1`; + v = parseString(xmlStr); + test:assertEquals(v, {choice_XsdChoiceArray5: {age: [13], salary: [11.1]}}); + + xmlStr = string `1311.11414.11515.11515.1`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray5 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlStr = string `13`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XsdChoiceArray5 Element occurs less than the min required times"), msg = (v).message()); +} + +type XSDChoiceArrayRecord6 record { + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice_XSDChoiceArrayRecord6_1 choice_XSDChoiceArrayRecord6_1; + + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice_XSDChoiceArrayRecord6_2 choice_XSDChoiceArrayRecord6_2; +}; + +type Choice_XSDChoiceArrayRecord6_1 record { + Choice_Array_A_6[] field1?; + Choice_Array_B_6[] field2?; +}; + +type Choice_XSDChoiceArrayRecord6_2 record { + Choice_Array_D_6[] field4?; + Choice_Array_E_6[] field5?; +}; + +type Choice_Array_A_6 record { + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_Array_6 value1; +}; + +type Choice_Array_B_6 record { + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice2_Array_6 value2; +}; + +type Choice_Array_D_6 record { + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice_Array_6 value1; +}; + +type Choice_Array_E_6 record { + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice2_Array_6 value2; +}; + +type Choice_Array_6 record { + string[] a?; +}; + +type Choice2_Array_6 record { + string[] d?; +}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXSDChoiceArrayRecord6() returns error? { + string xmlStr = string `1111111111111111`; + XSDChoiceArrayRecord6|Error v2 = parseString(xmlStr); + test:assertEquals(v2, {choice_XSDChoiceArrayRecord6_1: {field1: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field2: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1", "1"]}}]}, choice_XSDChoiceArrayRecord6_2: {field4: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field5: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1","1"]}}]}}); +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 32722965..f42155b2 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -101,19 +101,26 @@ public void visit(String element, boolean isStartElement) { return; } + remainingElementCount.put(element, remainingElementCount.get(element) - 1); + if (visitedElements.contains(element)) { if (remainingElementCount.get(element) == 0) { - throw new RuntimeException("Element " + xmlElementNameMap.get(element) - + " occurs more than the max allowed times"); + remainingElementCount.putAll(maxElementCount); + visitedElements.remove(element); } - remainingElementCount.put(element, remainingElementCount.get(element) - 1); return; } if (allElements.contains(element)) { - this.visitedElements.add(element); - lastElement = element; - remainingElementCount.put(element, remainingElementCount.get(element) - 1); + int count = maxElementCount.get(element) - remainingElementCount.get(element); + if (count >= minimumElementCount.get(element)) { + this.visitedElements.add(element); + updateOccurrences(); + } + if (remainingElementCount.get(element) == 0) { + remainingElementCount.putAll(maxElementCount); + visitedElements.remove(element); + } return; } @@ -137,21 +144,11 @@ public boolean isMiddleOfModelGroup() { @Override public boolean predictStartNewModelGroup(String element) { - if (element.equals(lastElement) && remainingElementCount.get(element) > 0) { - return false; - } - return !isMiddleOfElement && !visitedElements.isEmpty(); + generateElementOptionalityMapIfNotPresent(); + return !isMiddleOfElement && !isElementContains(element); } private void validateCompletedChoice() { - int elementCount = maxElementCount.get(lastElement) - remainingElementCount.get(lastElement); - if (elementCount < minimumElementCount.get(lastElement)) { - return; - } - if (visitedElements.size() > 1) { - throw new RuntimeException("Only one element in " + fieldName + " should be present"); - } - updateOccurrences(); } public void validateMinOccurrences() { From 961865642cc2049c6c90351555641ca027021550 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 17:53:40 +0530 Subject: [PATCH 23/58] Add tests for choice with element annotations --- ...sd_choice_test_with_element_annotation.bal | 343 ++++++++++++++++++ ..._sequence_test_with_element_annotation.bal | 8 +- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 8 +- 3 files changed, 353 insertions(+), 6 deletions(-) create mode 100644 ballerina/tests/xsd_choice_test_with_element_annotation.bal diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation.bal b/ballerina/tests/xsd_choice_test_with_element_annotation.bal new file mode 100644 index 00000000..64c695ef --- /dev/null +++ b/ballerina/tests/xsd_choice_test_with_element_annotation.bal @@ -0,0 +1,343 @@ +import ballerina/test; + +type XsdChoiceWithElementAnnotation record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_EA1 seq_EA1?; +}; + +type Choice_EA1 record { + + @Element { + maxOccurs: 2, + minOccurs: 0 + } + string[] EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotation() returns error? { + string xmlStr; + XsdChoiceWithElementAnnotation|Error v; + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlStr = string ``; + v = parseString(xmlStr); + test:assertEquals(v, {}); + + xmlStr = string `ABABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA3: ["AB", "AB", "AB", "AB"]}}); + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); + + xmlStr = string `ABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC", "ABC"]}}); + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `ABCABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA1 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABCABCABCABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); +} + +type XsdChoiceWithElementAnnotation2 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_EA2 seq_EA2; +}; + +type Choice_EA2 record { + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotation2() returns error? { + string xmlStr; + XsdChoiceWithElementAnnotation2|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCABCD`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABCCD`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdChoiceWithElementAnnotation3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XsdChoiceWithElementAnnotation3_1 seq_XsdChoiceWithElementAnnotation3_1; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XsdChoiceWithElementAnnotation3_2 seq_XsdChoiceWithElementAnnotation3_2?; +}; + +type Choice_XsdChoiceWithElementAnnotation3_1 record { + @Element { + minOccurs: 1, + maxOccurs: 3 + } + Choice_A_3[] field1?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_B_3[] field2?; + + @Element { + minOccurs: 1, + maxOccurs: 3 + } + Choice_C_3 field3?; +}; + +type Choice_XsdChoiceWithElementAnnotation3_2 record { + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_D_3[] field4?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_E_3[] field5?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_F_3[] field6?; +}; + +type Choice_A_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_3 value1; +}; + +type Choice_B_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice2_3 value2; +}; + +type Choice_C_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice3_3 value3; +}; + +type Choice_D_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_3 value1; +}; + +type Choice_E_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice2_3 value2; +}; + +type Choice_F_3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice3_3 value3; +}; + +type Choice_3 record { + @Element { + minOccurs: 0, + maxOccurs: 3 + } + string[] a?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + string[] b?; + string c?; +}; + +type Choice2_3 record { + string d?; + string e?; + string f?; +}; + +type Choice3_3 record { + string g?; + string h?; + string i?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotation3() returns error? { + string xmlStr; + XsdChoiceWithElementAnnotation3|Error v2; + + xmlStr = string `ABCABCABCABCABCABCABCABCABC`; + v2 = parseString(xmlStr); + test:assertEquals(v2, {seq_XsdChoiceWithElementAnnotation3_1: {field1: [{value1: {a: ["ABC"]}}, {value1: {a: ["ABC", "ABC"]}}, {value1: {a: ["ABC", "ABC", "ABC"]}}]}, seq_XsdChoiceWithElementAnnotation3_2: {field5: [{value2: {d: "ABC"}}, {value2: {d: "ABC"}}, {value2: {d: "ABC"}}]}}); +} diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index bbd81b40..fe4ac221 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -44,10 +44,10 @@ function testXsdSequenceWithElementAnnotation() returns error? { string xmlStr; XsdSequenceWithElementAnnotation|Error v; - // xmlStr = string `ABCABC`; - // v = parseString(xmlStr); - // test:assertTrue(v is Error); - // test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); xmlStr = string `ABCABCABABAB`; v = parseString(xmlStr); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index f42155b2..3e497e81 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -26,6 +26,7 @@ public class ChoiceInfo implements ModelGroupInfo { String lastElement = ""; public final Set allElements = new HashSet<>(); public final Set visitedElements = new HashSet<>(); + public final Set containElements = new HashSet<>(); private final HashMap xmlElementNameMap; private boolean isMiddleOfElement = false; @@ -67,10 +68,10 @@ public void validate() { } private void markOtherElementsAsOptional() { - if (!xmlElementInfo.isEmpty()) { + if (!allElements.isEmpty()) { HashMap elementInfo = xmlElementInfo.peek(); for (String element : allElements) { - if (!visitedElements.contains(element)) { + if (!containElements.contains(element)) { ElementInfo eleInfo = elementInfo.get(element); if (eleInfo != null) { eleInfo.isInsideChoice = true; @@ -86,6 +87,7 @@ public void reset() { isMiddleOfElement = false; this.remainingElementCount.putAll(this.maxElementCount); this.lastElement = ""; + this.containElements.clear(); } @Override @@ -102,6 +104,7 @@ public void visit(String element, boolean isStartElement) { } remainingElementCount.put(element, remainingElementCount.get(element) - 1); + containElements.add(element); if (visitedElements.contains(element)) { if (remainingElementCount.get(element) == 0) { @@ -149,6 +152,7 @@ public boolean predictStartNewModelGroup(String element) { } private void validateCompletedChoice() { + } public void validateMinOccurrences() { From 1e9cbea6f18d453145ce8bfae5a48a1d0208ea90 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 11 Nov 2024 18:01:06 +0530 Subject: [PATCH 24/58] Add tests for xsd choice with name annotations --- .../xsd_choice_with_name_annotations.bal | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 ballerina/tests/xsd_choice_with_name_annotations.bal diff --git a/ballerina/tests/xsd_choice_with_name_annotations.bal b/ballerina/tests/xsd_choice_with_name_annotations.bal new file mode 100644 index 00000000..2209015f --- /dev/null +++ b/ballerina/tests/xsd_choice_with_name_annotations.bal @@ -0,0 +1,216 @@ +import ballerina/test; + +type XsdChoiceWithNameAnnotation record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_Name_EA1 seq_EA1?; +}; + +type Choice_Name_EA1 record { + + @Element { + maxOccurs: 2, + minOccurs: 0 + } + @Name { + value: "A1" + } + string[] EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Name { + value: "A2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Name { + value: "A3" + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithNameAnnotation() returns error? { + string xmlStr; + XsdChoiceWithNameAnnotation|Error v; + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlStr = string ``; + v = parseString(xmlStr); + test:assertEquals(v, {}); + + xmlStr = string `ABABABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA3: ["AB", "AB", "AB", "AB"]}}); + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); + + xmlStr = string `ABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC", "ABC"]}}); + + xmlStr = string `ABC`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlStr = string `ABCABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A1 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = string `ABCABCABCABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); +} + +type XsdChoiceWithNameAnnotation2 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_Name_EA2 seq_EA2; +}; + +type Choice_Name_EA2 record { + @Name { + value: "A" + } + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Name { + value: "A1" + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Name { + value: "A2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Name { + value: "A3" + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithNameAnnotation2() returns error? { + string xmlStr; + XsdChoiceWithNameAnnotation2|Error v; + + xmlStr = string `ABCABC`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlStr = string `ABCABCABCD`; + v = parseString(xmlStr); + test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABABABABABAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + + xmlStr = string `ABCABAB`; + v = parseString(xmlStr); + test:assertEquals(v, {"seq_EA2": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = string `ABCABCCD`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `AB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlStr = string `ABCAB`; + v = parseString(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} From 33ee4fdd8d9574014efc0ce0f6483ace6cfb7fca Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Tue, 12 Nov 2024 02:11:19 +0530 Subject: [PATCH 25/58] Add initial implementation for XSD with xml values --- .../xsd_sequence_tests_with_parse_type.bal | 938 ++++++++++++++++++ .../lib/data/xmldata/utils/DataUtils.java | 60 +- .../lib/data/xmldata/utils/XsdUtils.java | 184 ++++ .../lib/data/xmldata/xml/XmlParser.java | 218 +--- .../lib/data/xmldata/xml/XmlTraversal.java | 195 +++- 5 files changed, 1335 insertions(+), 260 deletions(-) create mode 100644 ballerina/tests/xsd_sequence_tests_with_parse_type.bal create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal new file mode 100644 index 00000000..ad187f18 --- /dev/null +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -0,0 +1,938 @@ +import ballerina/test; + +type XSDSequenceRecordWithXmlValue record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue seq_XSDSequenceRecordWithXmlValue; +|}; + +type Seq_XSDSequenceRecordWithXmlValue record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue() returns error? { + xml xmlValue = xml `1311.1`; + XSDSequenceRecordWithXmlValue|Error v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue: {age: 13, salary: 11.1}}); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.salary, 11.1); + + xmlValue = xml `11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + // TODO: Change error messageas + // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); + test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); + + xmlValue = xml `13`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary not found in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); + + xmlValue = xml ``; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + + // TODO: Change the error message in validate Fields function + test:assertTrue((v).message().includes("required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML"), msg = (v).message()); + + xmlValue = xml `11.113`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); + + // TODO: Create an issue + // xmlValue = xml `13`; + // v = parseAsType(xmlValue); + // test:assertTrue(v is Error); + // test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); +} + +type XSDSequenceRecordWithXmlValueP2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValueP2 seq_XSDSequenceRecordWithXmlValueP2; +|}; + +type Seq_XSDSequenceRecordWithXmlValueP2 record {| + @Element { + minOccurs: 1, + maxOccurs: 3 + } + @Order { + value: 1 + } + int[] age; + + @Order { + value: 2 + } + float salary; + + @Order { + value: 3 + } + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string[] name; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValueP2() returns error? { + xml xmlValue; + XSDSequenceRecordWithXmlValueP2|Error v; + + xmlValue = xml `1311.1ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13], salary: 11.1, name: ["ABC"]}}); + + xmlValue = xml `13131311.1ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC"]}}); + + xmlValue = xml `13ABC11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + + xmlValue = xml `1313ABC11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + + xmlValue = xml `1311.1ABC11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + + xmlValue = xml `1313131311.1ABC11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("age Element occurs more than the max allowed times"), msg = (v).message()); + + xmlValue = xml `11.1ABC13`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + + xmlValue = xml `13131311.1ABCABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC", "ABC"]}}); + + xmlValue = xml `13131311.1ABCABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("name Element occurs more than the max allowed"), msg = (v).message()); + + xmlValue = xml `131311.1ABC13`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary, name not found in seq_XSDSequenceRecordWithXmlValueP2"), msg = (v).message()); +} + +// TODO: Test with open records. +type XSDSequenceRecordWithXmlValue2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue2 seq_XSDSequenceRecordWithXmlValue2; + + // TODO: After adding XSD validation for traverse, check union fields as well + int num; +|}; + +type Seq_XSDSequenceRecordWithXmlValue2 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue2() returns error? { + xml xmlValue = xml `31311.1`; + XSDSequenceRecordWithXmlValue2|Error v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue2: {age: 13, salary: 11.1}, num: 3}); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlValue = xml `1311.13`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue2: {age: 13, salary: 11.1}, num: 3}); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.age, 13); + test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlValue = xml `13311.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element salary not found in")); +} + +type XSDSequenceRecordWithXmlValue3 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue3 seq_XSDSequenceRecordWithXmlValue3; + + // TODO: After adding XSD validation for traverse, check union fields as well + record{int n;} num; +|}; + +type Seq_XSDSequenceRecordWithXmlValue3 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue3() returns error? { + xml xmlValue = xml `31311.1`; + XSDSequenceRecordWithXmlValue3|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue3: {age: 13, salary: 11.1}, num: {n: 3}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.salary, 11.1); + test:assertEquals((check v2).num, {n: 3}); + + xmlValue = xml `1311.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue3: {age: 13, salary: 11.1}, num: {n: 3}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.salary, 11.1); + test:assertEquals((check v2).num, {n: 3}); + + xmlValue = xml `13311.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in")); +} + +type XSDSequenceRecordWithXmlValue4 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue4 seq_XSDSequenceRecordWithXmlValue4; +|}; + +type Seq_XSDSequenceRecordWithXmlValue4 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue4() returns error? { + xml xmlValue = xml `31311.1`; + XSDSequenceRecordWithXmlValue4|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue4: {age: 13, salary: 11.1}, num: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlValue = xml `1311.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue4: {age: 13, salary: 11.1}, num: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlValue = xml `13311.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in")); +} + +type XSDSequenceRecordWithXmlValue5 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue5 seq_XSDSequenceRecordWithXmlValue5; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue5 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue5() returns error? { + xml xmlValue = xml `331311.1`; + XSDSequenceRecordWithXmlValue5|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `31311.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `1311.133`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue5: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `1333`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecordWithXmlValue6 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue6_1 seq_XSDSequenceRecordWithXmlValue6_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue6_2 seq_XSDSequenceRecordWithXmlValue6_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue6_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue6_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue6() returns error? { + xml xmlValue = xml `3SDsuccess31311.1`; + XSDSequenceRecordWithXmlValue6|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `SDsuccess331311.1`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `33SDsuccess1311.1`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `SDsuccess1311.133`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue6_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue6_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `SD13success11.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlValue = xml `13success11.1SD`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + + xmlValue = xml `successSD1311.133`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `SDsuccess11.11333`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `SDsuccess11.13133`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), (v2).message()); + + xmlValue = xml `SDsuccess11313.13`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecordWithXmlValue7 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue7_1 seq_XSDSequenceRecordWithXmlValue7_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue7_2 seq_XSDSequenceRecordWithXmlValue7_2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue7_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue7_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue7() returns error? { + xml xmlValue = xml `SDsuccess1311.1`; + XSDSequenceRecordWithXmlValue7|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue7_1: {age: 13, salary: 11.1}, seq_XSDSequenceRecordWithXmlValue7_2: {name: "SD", status: "success"}}); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_1.age, 13); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_1.salary, 11.1); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_2.name, "SD"); + test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_2.status, "success"); +} + +type XSDSequenceRecordWithXmlValue8 record {| + XSDSequenceRecordWithXmlValue8P2 test; + int 'check; +|}; + +type XSDSequenceRecordWithXmlValue8P2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue8_1 seq_XSDSequenceRecordWithXmlValue8_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue8_2 seq_XSDSequenceRecordWithXmlValue8_2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue8_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue8_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue8() returns error? { + xml xmlValue = xml `SDsuccess1311.12`; + XSDSequenceRecordWithXmlValue8|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {'check: 2, test: {seq_XSDSequenceRecordWithXmlValue8_1: {age: 13, salary: 11.1}, seq_XSDSequenceRecordWithXmlValue8_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_2.status, "success"); +} + +type XSDSequenceRecordWithXmlValue9 record {| + XSDSequenceRecordWithXmlValue9P test; + int a; +|}; + +type XSDSequenceRecordWithXmlValue9P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue9_1 seq_XSDSequenceRecordWithXmlValue9_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue9_2 seq_XSDSequenceRecordWithXmlValue9_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue9_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue9_2 record {| + @Order { + value: 1 + } + string name; + + @Order { + value: 2 + } + string status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue9() returns error? { + xml xmlValue = xml `3SDsuccess31311.12`; + XSDSequenceRecordWithXmlValue9|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SDsuccess331311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `33SDsuccess1311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SDsuccess1311.1332`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue9_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue9_2: {name: "SD", status: "success"}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.name, "SD"); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SD13success11.12`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlValue = xml `13success11.1SD2`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecordWithXmlValue10 record {| + XSDSequenceRecordWithXmlValue10P test; + int a; +|}; + +type XSDSequenceRecordWithXmlValue10P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue10_1 seq_XSDSequenceRecordWithXmlValue10_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue10_2 seq_XSDSequenceRecordWithXmlValue10_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue10_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue10_2 record {| + @Order { + value: 1 + } + Rec10 name; + + @Order { + value: 2 + } + Rec10 status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue10() returns error? { + xml xmlValue = xml `3SDABSuccessFail31311.12`; + XSDSequenceRecordWithXmlValue10|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SDABSuccessFail331311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `33SDABSuccessFail1311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SDABSuccessFail1311.1332`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue10_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue10_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SDAB13SuccessFail11.12`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + + xmlValue = xml `13SuccessFail11.1SDAB2`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecordWithXmlValue11 record {| + XSDSequenceRecordWithXmlValue11P test; + int a; + XSDSequenceRecordWithXmlValue11P2 test2; +|}; + +type XSDSequenceRecordWithXmlValue11P record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue11_1 seq_XSDSequenceRecordWithXmlValue11_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue11_2 seq_XSDSequenceRecordWithXmlValue11_2; + record{record {int n;} n;} num2; +|}; + +type XSDSequenceRecordWithXmlValue11P2 record {| + record{record {int n;} n;} num; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue11_1 seq_XSDSequenceRecordWithXmlValue11_1; + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue11_2 seq_XSDSequenceRecordWithXmlValue11_2; + record{record {int n;} n;} num2; +|}; + +type Seq_XSDSequenceRecordWithXmlValue11_1 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XSDSequenceRecordWithXmlValue11_2 record {| + @Order { + value: 1 + } + Rec11 name; + + @Order { + value: 2 + } + Rec11 status; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue11() returns error? { + xml xmlValue = xml `3SDABSuccessFail31311.123SDABSuccessFail31311.1`; + XSDSequenceRecordWithXmlValue11|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `3SDABSuccessFail31311.1SDABSuccessFail331311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `33SDABSuccessFail1311.13SDABSuccessFail31311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}, test2: {seq_XSDSequenceRecordWithXmlValue11_1: {age: 13, salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, seq_XSDSequenceRecordWithXmlValue11_2: {name: {value1: "SD", value2: "AB"}, status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.age, 13); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_1.salary, 11.1); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `3SDABSuccessFail31311.113SuccessFail11.1SDAB2`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); +} + +type XSDSequenceRecordWithXmlValue12 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue12_1 seq_XSDSequenceRecordWithXmlValue12_1; +}; + +type Seq_XSDSequenceRecordWithXmlValue12_1 record { + @Order {value: 1} + Seq_A field1; + + @Order {value: 2} + Seq_B field2; + + @Order {value: 3} + Seq_C field3; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue12() returns error? { + xml xmlValue = xml `123123123`; + XSDSequenceRecordWithXmlValue12|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue12_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {a: "1", b: "2", c: "3"}}, field3: {value3: {a: "1", b: "2", c: "3"}}}}); +} + +type XSDSequenceRecordWithXmlValue13 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue13_1 seq_XSDSequenceRecordWithXmlValue13_1?; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithXmlValue13_2 seq_XSDSequenceRecordWithXmlValue13_2; +}; + +type Seq_XSDSequenceRecordWithXmlValue13_1 record { + @Order {value: 1} + Seq_A_13 field1; + + @Order {value: 2} + Seq_B_13 field2; + + @Order {value: 3} + Seq_C_13 field3; +}; + +type Seq_XSDSequenceRecordWithXmlValue13_2 record { + @Order {value: 1} + Seq_D_13 field4; + + @Order {value: 2} + Seq_E_13 field5; + + @Order {value: 3} + Seq_F_13 field6; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithXmlValue13() returns error? { + xml xmlValue = xml `123123123123123123`; + XSDSequenceRecordWithXmlValue13|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + + xmlValue = xml `123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + + xmlValue = xml `123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + + xmlValue = xml `123123123123121233123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + + xmlValue = xml `123123123123123132`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `132123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + // TODO: Fix the error message + test:assertTrue((v2).message().includes("Element field5 is not in the correct order in seq_XSDSequenceRecordWithXmlValue13_2"), msg = (v2).message()); +} 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 0df2e29f..61ab4892 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 @@ -23,6 +23,8 @@ import io.ballerina.lib.data.xmldata.xml.QualifiedNameFactory; import io.ballerina.lib.data.xmldata.xml.QualifiedNameMap; import io.ballerina.lib.data.xmldata.xml.QualifiedNameSemantic; +import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; @@ -61,6 +63,7 @@ import javax.xml.namespace.QName; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.popXsdValidationStacks; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.ATTRIBUTE; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.ELEMENT; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.NOT_DEFINED; @@ -348,6 +351,8 @@ public static void updateExpectedTypeStacks(RecordType recordType, XmlAnalyzerDa analyzerData.fieldHierarchy.push(new QualifiedNameMap<>(getAllFieldsInRecordType(recordType, analyzerData))); analyzerData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); analyzerData.restTypes.push(recordType.getRestFieldType()); + analyzerData.xsdModelGroupInfo.push(new HashMap<>()); + analyzerData.xmlElementInfo.push(new HashMap<>()); } public static void popExpectedTypeStacks(XmlAnalyzerData analyzerData) { @@ -356,6 +361,7 @@ public static void popExpectedTypeStacks(XmlAnalyzerData analyzerData) { analyzerData.restTypes.pop(); analyzerData.attributeHierarchy.pop(); analyzerData.arrayIndexes.pop(); + popXsdValidationStacks(analyzerData); } public static boolean isAnydataOrJson(int typeTag) { @@ -1082,26 +1088,38 @@ private static void getXmlElementNameFromFieldAnnotation(Map fi } } - /** - * Holds data required for the traversing. - * - * @since 0.1.0 - */ - public static class XmlAnalyzerData { + public static void popMappingTypeStacks(XmlAnalyzerMetaData xmlParserData) { + xmlParserData.fieldHierarchy.pop(); + xmlParserData.visitedFieldHierarchy.pop(); + xmlParserData.restTypes.pop(); + } + + public static class XmlAnalyzerMetaData { + public Stack> attributeHierarchy = new Stack<>(); + public Stack> arrayIndexes = new Stack<>(); + public Stack> xmlElementInfo = new Stack<>(); + public Stack> xsdModelGroupInfo = new Stack<>(); + public Stack modelGroupStack = new Stack<>(); + public Stack nodesStack = new Stack<>(); public Stack> fieldHierarchy = new Stack<>(); public Stack> visitedFieldHierarchy = new Stack<>(); - public Stack> attributeHierarchy = new Stack<>(); public Stack restTypes = new Stack<>(); - public Stack> arrayIndexes = new Stack<>(); public RecordType rootRecord; public Field currentField; - public QualifiedName rootElement; - public String attributePrefix; - public String textFieldName; public boolean allowDataProjection; public boolean useSemanticEquality; + public String attributePrefix; + public String textFieldName; + public BMap currentNode; + } + /** + * Holds data required for the traversing. + * + * @since 0.1.0 + */ + public static class XmlAnalyzerData extends XmlAnalyzerMetaData { @SuppressWarnings("unchecked") public static XmlAnalyzerData copy(XmlAnalyzerData analyzerData) { XmlAnalyzerData data = new XmlAnalyzerData(); @@ -1113,11 +1131,13 @@ public static XmlAnalyzerData copy(XmlAnalyzerData analyzerData) { data.arrayIndexes = (Stack>) analyzerData.arrayIndexes.clone(); data.rootRecord = analyzerData.rootRecord; data.currentField = analyzerData.currentField; - data.rootElement = analyzerData.rootElement; data.attributePrefix = analyzerData.attributePrefix; data.textFieldName = analyzerData.textFieldName; data.allowDataProjection = analyzerData.allowDataProjection; data.useSemanticEquality = analyzerData.useSemanticEquality; + data.xmlElementInfo = (Stack>) analyzerData.xmlElementInfo.clone(); + data.xsdModelGroupInfo = (Stack>) analyzerData.xsdModelGroupInfo.clone(); + data.modelGroupStack = (Stack) analyzerData.modelGroupStack.clone(); return data; } @@ -1131,11 +1151,25 @@ public void resetFrom(XmlAnalyzerData analyzerData) { this.arrayIndexes = analyzerData.arrayIndexes; this.rootRecord = analyzerData.rootRecord; this.currentField = analyzerData.currentField; - this.rootElement = analyzerData.rootElement; this.attributePrefix = analyzerData.attributePrefix; this.textFieldName = analyzerData.textFieldName; this.allowDataProjection = analyzerData.allowDataProjection; this.useSemanticEquality = analyzerData.useSemanticEquality; + this.xmlElementInfo = analyzerData.xmlElementInfo; + this.xsdModelGroupInfo = analyzerData.xsdModelGroupInfo; + this.modelGroupStack = analyzerData.modelGroupStack; } } + + /** + * Holds data required for the parsing. + * + * @since 0.1.0 + */ + public static class XmlParserData extends XmlAnalyzerMetaData { + public final Stack restFieldsPoints = new Stack<>(); + public final Stack recordTypeStack = new Stack<>(); + public final Stack> parents = new Stack<>(); + public QualifiedNameMap siblings = new QualifiedNameMap<>(new LinkedHashMap<>()); + } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java new file mode 100644 index 00000000..8797ac4f --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java @@ -0,0 +1,184 @@ +package io.ballerina.lib.data.xmldata.utils; + +import io.ballerina.lib.data.xmldata.xml.QualifiedName; +import io.ballerina.lib.data.xmldata.xml.xsd.ChoiceInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; +import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.TypeUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import java.util.HashMap; +import java.util.Map; + +import static io.ballerina.lib.data.xmldata.utils.DataUtils.XmlAnalyzerMetaData; +import static io.ballerina.lib.data.xmldata.utils.DataUtils.popMappingTypeStacks; + +public class XsdUtils { + public static void initializeXsdInformation(RecordType recordType, XmlAnalyzerMetaData parserData) { + BMap annotations = recordType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + String xmlElementName = DataUtils.getModifiedName(fieldAnnotation, fieldName); + for (BString fieldAnnotationKey: fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + handleModuleXsdAnnotations(fieldAnnotation, fieldAnnotationKeyStr, fieldName, + recordType, fieldAnnotationKey, xmlElementName, parserData); + } + } + } + } + } + + public static void handleModuleXsdAnnotations(Map fieldAnnotation, String fieldAnnotationKeyStr, + String fieldName, RecordType recordType, BString fieldAnnotationKey, + String xmlElementName, XmlAnalyzerMetaData parserData) { + if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { + handleModuleXsdElementAnnotation(fieldAnnotation, fieldAnnotationKey, + fieldName, xmlElementName, parserData); + } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + handleModuleXsdSequenceAnnotations(fieldAnnotation, fieldName, recordType, + fieldAnnotationKey, parserData); + } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + handleModuleChoiceSequenceAnnotations(fieldAnnotation, fieldName, recordType, + fieldAnnotationKey, parserData); + } + } + + public static void handleModuleXsdElementAnnotation(Map fieldAnnotation, + BString fieldAnnotationKey, String fieldName, String xmlElementName, XmlAnalyzerMetaData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + parserData.xmlElementInfo.peek().put(xmlElementName, + new ElementInfo(xmlElementName, fieldName, fieldAnnotationValue)); + } + + public static void handleModuleXsdSequenceAnnotations(Map fieldAnnotation, String fieldName, + RecordType recordType, BString fieldAnnotationKey, XmlAnalyzerMetaData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); + if (fieldType instanceof RecordType recType) { + parserData.xsdModelGroupInfo.peek().put(fieldName, + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else if (fieldType instanceof ArrayType arrayType) { + Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); + if (elementType instanceof RecordType recType) { + //TODO: Define a separate function + parserData.xsdModelGroupInfo.peek().put(fieldName, + new SequenceInfo(fieldName, + fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } + + public static void handleModuleChoiceSequenceAnnotations(Map fieldAnnotation, String fieldName, + RecordType recordType, BString fieldAnnotationKey, XmlAnalyzerMetaData parserData) { + BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); + Type fieldType = TypeUtils.getReferredType(recordType + .getFields().get(fieldName).getFieldType()); + if (fieldType instanceof RecordType recType) { + parserData.xsdModelGroupInfo.peek().put(fieldName, + new ChoiceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); + } else { + throw new RuntimeException("Cannot include Sequence annotation into " + + fieldName + " of type " + fieldType); + } + } + + public static void validateCurrentElementInfo(XmlAnalyzerMetaData xmlAnalyzerMetaData) { + if (!xmlAnalyzerMetaData.xmlElementInfo.isEmpty()) { + xmlAnalyzerMetaData.xmlElementInfo.peek().forEach((key, value) -> value.validate()); + } + } + + public static void validateElementInfoStack(XmlAnalyzerMetaData xmlAnalyzerMetaData) { + while (!xmlAnalyzerMetaData.xmlElementInfo.isEmpty()) { + xmlAnalyzerMetaData.xmlElementInfo.pop().forEach((key, value) -> value.validate()); + } + } + + public static void validateModelGroupInfoStack(XmlAnalyzerMetaData xmlAnalyzerMetaData) { + while (!xmlAnalyzerMetaData.xsdModelGroupInfo.isEmpty()) { + xmlAnalyzerMetaData.xsdModelGroupInfo.pop().forEach((key, value) -> value.validateMinOccurrences()); + } + } + + public static void popXsdValidationStacks(XmlAnalyzerMetaData xmlAnalyzerMetaData) { + xmlAnalyzerMetaData.xsdModelGroupInfo.pop().forEach((key, value) -> value.validateMinOccurrences()); + xmlAnalyzerMetaData.xmlElementInfo.pop(); + } + + public static void updateElementOccurrence(XmlAnalyzerMetaData xmlAnalyzerMetaData, QualifiedName elemQName) { + if (!xmlAnalyzerMetaData.xmlElementInfo.isEmpty()) { + HashMap elementInfo = xmlAnalyzerMetaData.xmlElementInfo.peek(); + if (elementInfo.containsKey(elemQName.getLocalPart())) { + elementInfo.get(elemQName.getLocalPart()).updateOccurrences(); + } + } + } + + public static void validateModelGroupStack(XmlAnalyzerMetaData xmlAnalyzerMetaData, + QualifiedName elemQName, boolean isStartElement) { + String localPart = elemQName.getLocalPart(); + while (!xmlAnalyzerMetaData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = xmlAnalyzerMetaData.modelGroupStack.peek(); + if ((!modelGroup.isElementContains(localPart) + && !modelGroup.isMiddleOfModelGroup())) { + validateModelGroup(modelGroup, xmlAnalyzerMetaData); + continue; + } + + if (isStartElement && modelGroup.predictStartNewModelGroup(localPart)) { + validateModelGroup(modelGroup, xmlAnalyzerMetaData, false); + return; + } + + if (modelGroup.isElementContains(localPart)) { + modelGroup.visit(localPart, isStartElement); + } + return; + } + } + + public static void validateModelGroup(ModelGroupInfo modelGroup, XmlAnalyzerMetaData xmlAnalyzerMetaData) { + validateModelGroup(modelGroup, xmlAnalyzerMetaData, true); + } + + public static void validateModelGroup(ModelGroupInfo modelGroup, + XmlAnalyzerMetaData xmlAnalyzerMetaData, boolean isTerminated) { + modelGroup.validate(); + if (isTerminated) { + modelGroup.validateMinOccurrences(); + } + xmlAnalyzerMetaData.currentNode = (BMap) xmlAnalyzerMetaData.nodesStack.pop(); + xmlAnalyzerMetaData.modelGroupStack.pop(); + if (xmlAnalyzerMetaData instanceof DataUtils.XmlParserData xmlParserData) { + xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); + } + validateCurrentElementInfo(xmlAnalyzerMetaData); + popElementStacksForValidatingGroup(xmlAnalyzerMetaData); + } + + public static void popElementStacksForValidatingGroup(XmlAnalyzerMetaData xmlAnalyzerMetaData) { + popMappingTypeStacks(xmlAnalyzerMetaData); + xmlAnalyzerMetaData.attributeHierarchy.pop(); + xmlAnalyzerMetaData.arrayIndexes.pop(); + xmlAnalyzerMetaData.xsdModelGroupInfo.pop().forEach((key, value) -> value.validateMinOccurrences()); + xmlAnalyzerMetaData.xmlElementInfo.pop(); + } +} 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 8b96cff5..1f8ab102 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 @@ -23,10 +23,7 @@ import io.ballerina.lib.data.xmldata.utils.DataUtils; import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; -import io.ballerina.lib.data.xmldata.xml.xsd.ChoiceInfo; -import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; -import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.TypeCreator; @@ -64,6 +61,13 @@ import javax.xml.stream.XMLStreamReader; import static io.ballerina.lib.data.xmldata.utils.Constants.ENABLE_CONSTRAINT_VALIDATION; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.initializeXsdInformation; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.popXsdValidationStacks; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.updateElementOccurrence; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateCurrentElementInfo; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateElementInfoStack; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateModelGroupInfoStack; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateModelGroupStack; import static javax.xml.stream.XMLStreamConstants.CDATA; import static javax.xml.stream.XMLStreamConstants.CHARACTERS; import static javax.xml.stream.XMLStreamConstants.COMMENT; @@ -72,6 +76,7 @@ import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.PROCESSING_INSTRUCTION; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; +import static io.ballerina.lib.data.xmldata.utils.DataUtils.XmlParserData; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.ATTRIBUTE; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.ELEMENT; import static io.ballerina.lib.data.xmldata.xml.QualifiedName.AttributeState.NOT_DEFINED; @@ -274,8 +279,7 @@ private void parseRootElement(XMLStreamReader xmlStreamReader, @SuppressWarnings("unchecked") private void readText(XMLStreamReader xmlStreamReader, - boolean isCData, - XmlParserData xmlParserData) throws XMLStreamException { + boolean isCData, XmlParserData xmlParserData) throws XMLStreamException { Field currentField = xmlParserData.currentField; TextValue textValue = new TextValue(); String text; @@ -585,64 +589,6 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse initializeNextValueBasedOnExpectedType(fieldName, fieldType, temp, currentNode, xmlParserData); } - private void updateElementOccurrence(XmlParserData xmlParserData, QualifiedName elemQName) { - if (!xmlParserData.xmlElementInfo.isEmpty()) { - HashMap elementInfo = xmlParserData.xmlElementInfo.peek(); - if (elementInfo.containsKey(elemQName.getLocalPart())) { - elementInfo.get(elemQName.getLocalPart()).updateOccurrences(); - } - } - } - - private void validateModelGroupStack(XmlParserData xmlParserData, - QualifiedName elemQName, boolean isStartElement) { - String localPart = elemQName.getLocalPart(); - while (!xmlParserData.modelGroupStack.isEmpty()) { - ModelGroupInfo modelGroup = xmlParserData.modelGroupStack.peek(); - if ((!modelGroup.isElementContains(localPart) - && !modelGroup.isMiddleOfModelGroup())) { - validateModelGroup(modelGroup, xmlParserData); - continue; - } - - if (isStartElement && modelGroup.predictStartNewModelGroup(localPart)) { - validateModelGroup(modelGroup, xmlParserData, false); - return; - } - - if (modelGroup.isElementContains(localPart)) { - modelGroup.visit(localPart, isStartElement); - } - return; - } - } - - private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData) { - validateModelGroup(modelGroup, xmlParserData, true); - } - - private void validateModelGroup(ModelGroupInfo modelGroup, XmlParserData xmlParserData, boolean isTerminated) { - modelGroup.validate(); - if (isTerminated) { - modelGroup.validateMinOccurrences(); - } - xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); - xmlParserData.modelGroupStack.pop(); - xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); - validateCurrentElementInfo(xmlParserData); - popElementStacksForValidatingGroup(xmlParserData); - } - - private void popElementStacksForValidatingGroup(XmlParserData xmlParserData) { - popMappingTypeStacks(xmlParserData); - xmlParserData.attributeHierarchy.pop(); - xmlParserData.arrayIndexes.pop(); - xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { - value.validateMinOccurrences(); - });; - xmlParserData.xmlElementInfo.pop(); - } - private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap modelGroupInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { @@ -654,7 +600,6 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, QualifiedName qualifiedName = QualifiedNameFactory .createQualifiedName("", key, "", xmlParserData.useSemanticEquality); - // TODO: Validate Namespaces if (modelGroupValue.isElementContains(elemQName.getLocalPart())) { Object temp = null; Field field = xmlParserData.rootRecord.getFields().get(key); @@ -662,13 +607,8 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, temp = xmlParserData.currentNode.get(StringUtils.fromString(key)); } xmlParserData.modelGroupStack.push(modelGroupValue); - - // TODO: Test on arrays visitedFields.put(qualifiedName, field); fieldMap.remove(qualifiedName); - - // TODO: temp != null for arrays and currentNode - // TODO: Check for arrays as well Type referredType = TypeUtils.getReferredType(field.getFieldType()); updateNextRecordForXsd(xmlParserData, key, referredType, temp, xmlParserData.currentNode); readElement(xmlStreamReader, xmlParserData); @@ -863,17 +803,8 @@ private void popExpectedTypeStacks(XmlParserData xmlParserData) { popXsdValidationStacks(xmlParserData); } - private void popXsdValidationStacks(XmlParserData xmlParserData) { - xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { - value.validateMinOccurrences(); - });; - xmlParserData.xmlElementInfo.pop(); - } - private void popMappingTypeStacks(XmlParserData xmlParserData) { - xmlParserData.fieldHierarchy.pop(); - xmlParserData.visitedFieldHierarchy.pop(); - xmlParserData.restTypes.pop(); + DataUtils.popMappingTypeStacks(xmlParserData); } private void updateSiblingAndRootRecord(XmlParserData xmlParserData) { @@ -1316,107 +1247,6 @@ private QualifiedName getElementName(XMLStreamReader xmlStreamReader, boolean us qName.getPrefix(), ELEMENT, useSemanticEquality); } - private void initializeXsdInformation(RecordType recordType, XmlParserData parserData) { - BMap annotations = recordType.getAnnotations(); - for (BString annotationKey : annotations.getKeys()) { - String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - String xmlElementName = DataUtils.getModifiedName(fieldAnnotation, fieldName); - for (BString fieldAnnotationKey: fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - handleModuleXsdAnnotations(fieldAnnotation, fieldAnnotationKeyStr, fieldName, - recordType, fieldAnnotationKey, xmlElementName, parserData); - } - } - } - } - } - - private void handleModuleXsdAnnotations(Map fieldAnnotation, String fieldAnnotationKeyStr, - String fieldName, RecordType recordType, BString fieldAnnotationKey, - String xmlElementName, XmlParserData parserData) { - if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { - handleModuleXsdElementAnnotation(fieldAnnotation, fieldAnnotationKey, - fieldName, xmlElementName, parserData); - } else if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { - handleModuleXsdSequenceAnnotations(fieldAnnotation, fieldName, recordType, - fieldAnnotationKey, parserData); - } else if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - handleModuleChoiceSequenceAnnotations(fieldAnnotation, fieldName, recordType, - fieldAnnotationKey, parserData); - } - } - - private void handleModuleXsdElementAnnotation(Map fieldAnnotation, BString fieldAnnotationKey, - String fieldName, String xmlElementName, XmlParserData parserData) { - BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - parserData.xmlElementInfo.peek().put(xmlElementName, - new ElementInfo(xmlElementName, fieldName, fieldAnnotationValue)); - } - - private void handleModuleXsdSequenceAnnotations(Map fieldAnnotation, String fieldName, - RecordType recordType, BString fieldAnnotationKey, XmlParserData parserData) { - BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = TypeUtils.getReferredType(recordType - .getFields().get(fieldName).getFieldType()); - if (fieldType instanceof RecordType recType) { - parserData.xsdModelGroupInfo.peek().put(fieldName, - new SequenceInfo(fieldName, - fieldAnnotationValue, recType, parserData.xmlElementInfo)); - } else if (fieldType instanceof ArrayType arrayType) { - Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); - if (elementType instanceof RecordType recType) { - //TODO: Define a separate function - parserData.xsdModelGroupInfo.peek().put(fieldName, - new SequenceInfo(fieldName, - fieldAnnotationValue, recType, parserData.xmlElementInfo)); - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } - - private void handleModuleChoiceSequenceAnnotations(Map fieldAnnotation, String fieldName, - RecordType recordType, BString fieldAnnotationKey, XmlParserData parserData) { - BMap fieldAnnotationValue = (BMap) fieldAnnotation.get(fieldAnnotationKey); - Type fieldType = TypeUtils.getReferredType(recordType - .getFields().get(fieldName).getFieldType()); - if (fieldType instanceof RecordType recType) { - parserData.xsdModelGroupInfo.peek().put(fieldName, - new ChoiceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); - } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); - } - } - - private void validateCurrentElementInfo(XmlParserData xmlParserData) { - if (!xmlParserData.xmlElementInfo.isEmpty()) { - xmlParserData.xmlElementInfo.peek().forEach((key, value) -> value.validate()); - } - } - - private void validateElementInfoStack(XmlParserData xmlParserData) { - while (!xmlParserData.xmlElementInfo.isEmpty()) { - xmlParserData.xmlElementInfo.pop().forEach((key, value) -> value.validate()); - } - } - - private void validateModelGroupInfoStack(XmlParserData xmlParserData) { - while (!xmlParserData.xsdModelGroupInfo.isEmpty()) { - xmlParserData.xsdModelGroupInfo.pop().forEach((key, value) -> { - value.validateMinOccurrences(); - }); - } - } - /** * Represents the content of an XML element. * @@ -1426,32 +1256,4 @@ static class TextValue { String text; boolean isCommentInTheMiddle = false; } - - /** - * Holds data required for the parsing. - * - * @since 0.1.0 - */ - public static class XmlParserData { - private final Stack nodesStack = new Stack<>(); - private final Stack> fieldHierarchy = new Stack<>(); - Stack> visitedFieldHierarchy = new Stack<>(); - private final Stack> attributeHierarchy = new Stack<>(); - private final Stack restTypes = new Stack<>(); - private final Stack restFieldsPoints = new Stack<>(); - private final Stack recordTypeStack = new Stack<>(); - Stack> arrayIndexes = new Stack<>(); - private RecordType rootRecord; - private Field currentField; - private final Stack> parents = new Stack<>(); - private QualifiedNameMap siblings = new QualifiedNameMap<>(new LinkedHashMap<>()); - private BMap currentNode; - private String attributePrefix; - private String textFieldName; - private boolean allowDataProjection; - private boolean useSemanticEquality; - Stack> xmlElementInfo = new Stack<>(); - Stack> xsdModelGroupInfo = new Stack<>(); - Stack modelGroupStack = new Stack<>(); - } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java index 273ee4a8..4393d647 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java @@ -23,6 +23,9 @@ import io.ballerina.lib.data.xmldata.utils.DataUtils.XmlAnalyzerData; import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; +import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; +import io.ballerina.runtime.api.PredefinedTypes; +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.flags.SymbolFlags; @@ -56,6 +59,12 @@ import javax.xml.namespace.QName; import static io.ballerina.lib.data.xmldata.utils.Constants.ENABLE_CONSTRAINT_VALIDATION; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.initializeXsdInformation; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.updateElementOccurrence; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateCurrentElementInfo; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateElementInfoStack; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateModelGroupInfoStack; +import static io.ballerina.lib.data.xmldata.utils.XsdUtils.validateModelGroupStack; /** * Convert Xml value to a ballerina record. @@ -68,6 +77,7 @@ public class XmlTraversal { public static Object traverse(BXml xml, BMap options, BTypedesc typed) { Object convertedValue = traverse(xml, options, typed.getDescribingType()); + if (convertedValue instanceof BError) { return convertedValue; } @@ -81,12 +91,13 @@ public static Object traverse(BXml xml, BMap options, Type type } static class XmlTree { - private Object currentNode; - public Object traverseXml(BXml xml, BMap options, Type type) { XmlAnalyzerData analyzerData = new XmlAnalyzerData(); DataUtils.updateOptions(options, analyzerData); - return traverseXml(xml, analyzerData, type); + traverseXml(xml, analyzerData, type); + validateElementInfoStack(analyzerData); + validateModelGroupInfoStack(analyzerData); + return analyzerData.currentNode; } public Object traverseXml(BXml xml, XmlAnalyzerData analyzerData, Type type) { @@ -105,12 +116,14 @@ public Object traverseXml(BXml xml, XmlAnalyzerData analyzerData, Type type) { } } - private Object traverseXmlWithRecordAsExpectedType(BXml xml, - XmlAnalyzerData analyzerData, RecordType recordType) { - currentNode = ValueCreator.createRecordValue(recordType.getPackage(), recordType.getName()); + private Object traverseXmlWithRecordAsExpectedType(BXml xml, XmlAnalyzerData analyzerData, + RecordType recordType) { + analyzerData.currentNode = ValueCreator.createRecordValue(recordType.getPackage(), recordType.getName()); BXml nextXml = validateRootElement(xml, recordType, analyzerData); Object resultRecordValue = traverseXml(nextXml, recordType, analyzerData); - DataUtils.validateRequiredFields(analyzerData, (BMap) currentNode); + validateModelGroupStackForRootElement(analyzerData); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); + validateCurrentElementInfo(analyzerData); return resultRecordValue; } @@ -143,13 +156,14 @@ private Object traverseXml(BXml xml, Type type, XmlAnalyzerData analyzerData) { case SEQUENCE -> convertSequence((BXmlSequence) xml, type, analyzerData); case TEXT -> convertText(xml.toString(), analyzerData); } - return currentNode; + + return analyzerData.currentNode; } @SuppressWarnings("unchecked") private void convertText(String text, XmlAnalyzerData analyzerData) { Field currentField = analyzerData.currentField; - BMap mapValue = (BMap) currentNode; + BMap mapValue = analyzerData.currentNode; String textFieldName = analyzerData.textFieldName; if (currentField == null) { @@ -226,14 +240,24 @@ private void convertText(String text, XmlAnalyzerData analyzerData) { @SuppressWarnings("unchecked") private void convertElement(BXmlItem xmlItem, XmlAnalyzerData analyzerData) { QualifiedName elementQName = DataUtils.getElementName(xmlItem.getQName(), analyzerData.useSemanticEquality); + updateElementOccurrence(analyzerData, elementQName); + validateModelGroupStack(analyzerData, elementQName, true); QualifiedNameMap fieldsMap = analyzerData.fieldHierarchy.peek(); Field currentField; if (analyzerData.visitedFieldHierarchy.peek().contains(elementQName)) { currentField = analyzerData.visitedFieldHierarchy.peek().get(elementQName); Type fieldType = TypeUtils.getReferredType(currentField.getFieldType()); - if (!DataUtils.isArrayValueAssignable(fieldType)) { - throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, - currentField.getFieldName()); + String fieldName = currentField.getFieldName(); + if (!analyzerData.modelGroupStack.isEmpty()) { + ModelGroupInfo modelGroup = analyzerData.modelGroupStack.peek(); + + // Check whether this is a model group array + if (fieldType.getTag() != TypeTags.ARRAY_TAG && modelGroup == null) { + throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, + fieldType, fieldName); + } + } else if (!DataUtils.isArrayValueAssignable(fieldType)) { + throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName); } } else { currentField = fieldsMap.get(elementQName); @@ -246,6 +270,18 @@ private void convertElement(BXmlItem xmlItem, XmlAnalyzerData analyzerData) { analyzerData.currentField = currentField; if (currentField == null) { + // TODO: In here assume that if XSD is present then no rest type can be there. + HashMap modelGroupInfo = null; + if (!analyzerData.xsdModelGroupInfo.isEmpty()) { + modelGroupInfo = analyzerData.xsdModelGroupInfo.peek(); + } + + if (modelGroupInfo != null && !modelGroupInfo.isEmpty()) { + validateElementInXsdSequenceOrElement(elementQName, modelGroupInfo, + xmlItem, analyzerData, analyzerData.visitedFieldHierarchy.peek(), fieldsMap); + return; + } + Type restType = analyzerData.restTypes.peek(); String elementName = elementQName.getLocalPart(); if (restType != null) { @@ -272,7 +308,9 @@ private void convertElement(BXmlItem xmlItem, XmlAnalyzerData analyzerData) { } convertToFieldType(xmlItem, currentField, currentField.getFieldName(), currentFieldType, - (BMap) currentNode, analyzerData); + analyzerData.currentNode, analyzerData); + validateCurrentElementInfo(analyzerData); + validateModelGroupStack(analyzerData, elementQName, false); } private void convertToFieldType(BXmlItem xmlItem, Field currentField, String fieldName, Type currentFieldType, @@ -288,6 +326,7 @@ private void convertToFieldType(BXmlItem xmlItem, Field currentField, String fie updateNextMap(currentFieldType, analyzerData); analyzerData.arrayIndexes.push(new HashMap<>()); convertToRestType(xmlItem, currentFieldType, analyzerData); + validateCurrentElementInfo(analyzerData); DataUtils.popExpectedTypeStacks(analyzerData); } case TypeTags.TYPE_REFERENCED_TYPE_TAG -> @@ -384,25 +423,28 @@ private void convertToUnionMemberType(BXmlItem xmlItem, String fieldName, ArrayT private void convertToRecordType(BXmlItem xmlItem, Type currentFieldType, String fieldName, RecordType elementType, BMap mapValue, XmlAnalyzerData analyzerData) { - currentNode = updateNextRecord(xmlItem, elementType, fieldName, + analyzerData.currentNode = updateNextRecord(xmlItem, elementType, fieldName, currentFieldType, mapValue, analyzerData); RecordType prevRecord = analyzerData.rootRecord; analyzerData.rootRecord = elementType; traverseXml(xmlItem.getChildrenSeq(), currentFieldType, analyzerData); - DataUtils.validateRequiredFields(analyzerData, (BMap) currentNode); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); + validateCurrentElementInfo(analyzerData); DataUtils.popExpectedTypeStacks(analyzerData); analyzerData.rootRecord = prevRecord; - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); } private void convertToMapType(BXmlItem xmlItem, Type fieldType, Type elementType, String fieldName, BMap mapValue, XmlAnalyzerData analyzerData) { updateNextMap(elementType, analyzerData); - currentNode = updateNextMappingValue(elementType, fieldName, fieldType, mapValue, analyzerData); + analyzerData.currentNode = updateNextMappingValue( + elementType, fieldName, fieldType, mapValue, analyzerData); traverseXml(xmlItem.getChildrenSeq(), fieldType, analyzerData); - DataUtils.validateRequiredFields(analyzerData, (BMap) currentNode); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); + validateCurrentElementInfo(analyzerData); DataUtils.popExpectedTypeStacks(analyzerData); - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); } private void updateNextMap(Type fieldType, XmlAnalyzerData analyzerData) { @@ -414,12 +456,15 @@ private void updateNextMap(Type fieldType, XmlAnalyzerData analyzerData) { analyzerData.fieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); analyzerData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); analyzerData.attributeHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); + analyzerData.xsdModelGroupInfo.push(new HashMap<>()); + analyzerData.xmlElementInfo.push(new HashMap<>()); } private BMap updateNextRecord(BXmlItem xmlItem, RecordType recordType, String fieldName, Type fieldType, BMap currentMapValue, XmlAnalyzerData analyzerData) { DataUtils.updateExpectedTypeStacks(recordType, analyzerData); + initializeXsdInformation(recordType, analyzerData); BMap nextValue = updateNextMappingValue(recordType, fieldName, fieldType, currentMapValue, analyzerData); QName qName = xmlItem.getQName(); @@ -467,7 +512,7 @@ private BMap updateNextMappingValue(Type type, String fieldName private void convertToRestType(BXmlItem xmlItem, Type restType, XmlAnalyzerData analyzerData) { String elemName = xmlItem.getQName().getLocalPart(); analyzerData.currentField = TypeCreator.createField(restType, elemName, SymbolFlags.PUBLIC); - BMap mapValue = (BMap) currentNode; + BMap mapValue = analyzerData.currentNode; checkRestTypeAndConvert(xmlItem, elemName, restType, restType, mapValue, analyzerData); } @@ -564,10 +609,10 @@ private void handleArrayValueForRestType(BXmlItem xmlItem, String elemName, Type if (!nextValue.isEmpty()) { analyzerData.currentField = TypeCreator.createField(restType, analyzerData.textFieldName, SymbolFlags.REQUIRED); - analyzerData.nodesStack.push(currentNode); - currentNode = nextValue; + analyzerData.nodesStack.push(analyzerData.currentNode); + analyzerData.currentNode = nextValue; traverseXml(xmlItem.getChildrenSeq(), restType, analyzerData); - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); return; } } @@ -588,12 +633,14 @@ private void handleArrayValueForRestType(BXmlItem xmlItem, String elemName, Type arrayValue.add(currentIndex, nextValue); } } - analyzerData.nodesStack.push(currentNode); - currentNode = nextValue; + analyzerData.nodesStack.push(analyzerData.currentNode); + analyzerData.currentNode = nextValue; handleAttributesRest(xmlItem, nextValue, restType, useSemanticEquality); analyzerData.fieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); analyzerData.visitedFieldHierarchy.push(new QualifiedNameMap<>(new HashMap<>())); + analyzerData.xsdModelGroupInfo.push(new HashMap<>()); + analyzerData.xmlElementInfo.push(new HashMap<>()); analyzerData.arrayIndexes.push(new HashMap<>()); if (restType.getTag() == TypeTags.ARRAY_TAG) { Type memberType = ((ArrayType) restType).getElementType(); @@ -607,7 +654,7 @@ private void handleArrayValueForRestType(BXmlItem xmlItem, String elemName, Type analyzerData.visitedFieldHierarchy.pop(); analyzerData.restTypes.pop(); analyzerData.arrayIndexes.pop(); - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); } private void convertContentToRestType(BXmlItem xmlItem, BString bElementName, Type restType, @@ -621,10 +668,10 @@ private void convertContentToRestType(BXmlItem xmlItem, BString bElementName, Ty if (!nextValue.isEmpty()) { analyzerData.currentField = TypeCreator.createField(restType, analyzerData.textFieldName, SymbolFlags.REQUIRED); - analyzerData.nodesStack.push(currentNode); - currentNode = nextValue; + analyzerData.nodesStack.push(analyzerData.currentNode); + analyzerData.currentNode = nextValue; traverseXml(xmlItem.getChildrenSeq(), restType, analyzerData); - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); return; } } @@ -636,11 +683,11 @@ private void convertToJsonOrAnydataAsRestType(BXmlItem xmlItem, BString bElement BMap nextValue = ValueCreator.createMapValue(DataUtils.getMapTypeFromConstraintType(restType)); mapValue.put(bElementName, nextValue); - analyzerData.nodesStack.push(currentNode); - currentNode = nextValue; + analyzerData.nodesStack.push(analyzerData.currentNode); + analyzerData.currentNode = nextValue; handleAttributesRest(xmlItem, nextValue, restType, analyzerData.useSemanticEquality); traverseXml(xmlItem.getChildrenSeq(), restType, analyzerData); - currentNode = analyzerData.nodesStack.pop(); + analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); } private boolean isElementHasAttributes(BXmlItem xmlItem) { @@ -684,10 +731,11 @@ private void convertSequence(BXmlSequence xmlSequence, Type type, XmlAnalyzerDat } if (newSequence.size() == 1) { - currentNode = traverseXml(newSequence.get(0), type, analyzerData); + analyzerData.currentNode = (BMap) traverseXml(newSequence.get(0), type, analyzerData); return; } - currentNode = convertHeterogeneousSequence(newSequence, type, analyzerData); + analyzerData.currentNode = + (BMap) convertHeterogeneousSequence(newSequence, type, analyzerData); } private Object convertHeterogeneousSequence(List sequence, Type type, XmlAnalyzerData analyzerData) { @@ -700,7 +748,7 @@ private Object convertHeterogeneousSequence(List sequence, Type type, XmlA traverseXml(bXml, type, analyzerData); } } - return currentNode; + return analyzerData.currentNode; } private boolean isAllChildrenText(List sequence) { @@ -722,7 +770,7 @@ private Object handleCommentInMiddleOfText(List sequence, Type type, XmlAn textBuilder.append(bXml.toString()); } convertText(textBuilder.toString(), analyzerData); - return currentNode; + return analyzerData.currentNode; } private static boolean isCommentOrPi(BXml bxml) { @@ -744,14 +792,14 @@ private BXml validateRootElement(BXml xml, RecordType recordType, XmlAnalyzerDat analyzerData.rootRecord = recordType; boolean useSemanticEquality = analyzerData.useSemanticEquality; QualifiedName elementQName = DataUtils.getElementName(xmlItem.getQName(), useSemanticEquality); - analyzerData.rootElement = - DataUtils.validateAndGetXmlNameFromRecordAnnotation(recordType, recordType.getName(), elementQName, + DataUtils.validateAndGetXmlNameFromRecordAnnotation(recordType, recordType.getName(), elementQName, useSemanticEquality); DataUtils.validateTypeNamespace(elementQName.getPrefix(), elementQName.getNamespaceURI(), recordType); // Keep track of fields and attributes DataUtils.updateExpectedTypeStacks(recordType, analyzerData); - handleAttributes(xmlItem, (BMap) currentNode, analyzerData); + initializeXsdInformation(recordType, analyzerData); + handleAttributes(xmlItem, analyzerData.currentNode, analyzerData); analyzerData.arrayIndexes.push(new HashMap<>()); return xmlItem.getChildrenSeq(); } @@ -867,5 +915,74 @@ private HashSet findAllInnerElement(BXmlItem xmlItem) { } return elements; } + + private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, + HashMap modelGroupInfo, BXmlItem xmlItem, + XmlAnalyzerData xmlAnalyzerData, QualifiedNameMap visitedFields, + QualifiedNameMap fieldMap) { + if (modelGroupInfo != null) { + // TODO: Optimize this with stream.findFirst() + for (Map.Entry entry : modelGroupInfo.entrySet()) { + ModelGroupInfo modelGroupValue = entry.getValue(); + String key = entry.getKey(); + QualifiedName qualifiedName = QualifiedNameFactory + .createQualifiedName("", key, "", xmlAnalyzerData.useSemanticEquality); + + if (modelGroupValue.isElementContains(elemQName.getLocalPart())) { + Object temp = null; + Field field = xmlAnalyzerData.rootRecord.getFields().get(key); + if (visitedFields.contains(qualifiedName)) { + temp = (xmlAnalyzerData.currentNode).get(StringUtils.fromString(key)); + } + xmlAnalyzerData.modelGroupStack.push(modelGroupValue); + visitedFields.put(qualifiedName, field); + fieldMap.remove(qualifiedName); + Type referredType = TypeUtils.getReferredType(field.getFieldType()); + updateNextRecordForXsd(xmlAnalyzerData, key, referredType, temp, xmlAnalyzerData.currentNode); + convertElement(xmlItem, xmlAnalyzerData); + return; + } + } + } + } + + private void updateNextRecordForXsd(XmlAnalyzerData xmlAnalyzerData, String fieldName, + Type fieldType, Object temp, Object currentNode) { + if (fieldType.getTag() == TypeTags.RECORD_TYPE_TAG) { + DataUtils.updateExpectedTypeStacks((RecordType) fieldType, xmlAnalyzerData); + RecordType recType = (RecordType) fieldType; + xmlAnalyzerData.currentNode = updateNextMappingValue(recType, fieldName, fieldType, + (BMap) currentNode, xmlAnalyzerData); + xmlAnalyzerData.rootRecord = recType; + return; + } + + if (fieldType.getTag() == TypeTags.ARRAY_TAG) { + Type elementType = TypeUtils.getReferredType(((ArrayType) fieldType).getElementType()); + if (temp == null) { + xmlAnalyzerData.arrayIndexes.peek().put(fieldName, 0); + ((BMap) currentNode).put(StringUtils.fromString(fieldName), + ValueCreator.createArrayValue((ArrayType) fieldType)); + } else { + HashMap indexes = xmlAnalyzerData.arrayIndexes.peek(); + indexes.put(fieldName, indexes.get(fieldName) + 1); + } + + if (elementType.getTag() == TypeTags.RECORD_TYPE_TAG) { + DataUtils.updateExpectedTypeStacks((RecordType) elementType, xmlAnalyzerData); + RecordType recType = (RecordType) elementType; + xmlAnalyzerData.rootRecord = recType; + xmlAnalyzerData.currentNode = updateNextMappingValue(recType, fieldName, + fieldType, (BMap) currentNode, xmlAnalyzerData); + return; + } + } + throw new RuntimeException("Not implemented"); + } + + private void validateModelGroupStackForRootElement(XmlAnalyzerData analyzerData) { + validateModelGroupStack(analyzerData, + QualifiedNameFactory.createQualifiedName("", "", "", analyzerData.useSemanticEquality), false); + } } } From 54d162107db77ed4044d4e0368d7ea4042dcd2b9 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Tue, 12 Nov 2024 11:22:45 +0530 Subject: [PATCH 26/58] Add tests for xsd with element info for parse type API --- ...sd_sequence_array_test_with_parse_type.bal | 262 +++++++++++++++ ...ith_element_annotation_with_parse_type.bal | 316 ++++++++++++++++++ .../lib/data/xmldata/utils/DataUtils.java | 2 +- .../lib/data/xmldata/utils/XsdUtils.java | 6 +- .../lib/data/xmldata/xml/XmlParser.java | 6 +- .../lib/data/xmldata/xml/XmlTraversal.java | 15 +- 6 files changed, 593 insertions(+), 14 deletions(-) create mode 100644 ballerina/tests/xsd_sequence_array_test_with_parse_type.bal create mode 100644 ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal new file mode 100644 index 00000000..169759e2 --- /dev/null +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -0,0 +1,262 @@ +import ballerina/test; + +type XsdSequenceArrayWithXmlValue record {| + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArrayWithXmlValue[] seq_XsdSequenceArrayWithXmlValue; +|}; + +type Seq_XsdSequenceArrayWithXmlValue record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArrayWithXmlValue() returns error? { + xml xmlValue; + XsdSequenceArrayWithXmlValue|Error v; + + xmlValue = xml `1311.11415.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + + xmlValue = xml `1311.11414.11515.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); +} + +type XsdSequenceArrayWithXmlValue2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArrayWithXmlValue2[] seq_XsdSequenceArrayWithXmlValue2; + + @Sequence { + minOccurs: 0, + maxOccurs: 2 + } + Seq_XsdSequenceArrayWithXmlValue2_2[] seq_XsdSequenceArrayWithXmlValue2_2 = []; +|}; + +type Seq_XsdSequenceArrayWithXmlValue2 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +type Seq_XsdSequenceArrayWithXmlValue2_2 record {| + @Order { + value: 1 + } + int age2; + + @Order { + value: 2 + } + float salary2; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArrayWithXmlValue2() returns error? { + xml xmlValue; + XsdSequenceArrayWithXmlValue2|Error v; + + xmlValue = xml `1311.11415.11311.11415.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue2: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}], seq_XsdSequenceArrayWithXmlValue2_2: [{age2: 13, salary2: 11.1}, {age2: 14, salary2: 15.1}]}); + + xmlValue = xml `1311.11311.11415.11311.11415.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue2 Element occurs more than the max allowed times")); + + xmlValue = xml `1311.11415.11311.11311.11415.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue2_2 Element occurs more than the max allowed times")); +} + +type XSDSequenceArrayWithXmlValueRecord13 record { + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_XSDSequenceArrayWithXmlValueRecord13_1[] seq_XSDSequenceArrayWithXmlValueRecord13_1; + + @Sequence { + minOccurs: 1, + maxOccurs: 3 + } + Seq_XSDSequenceArrayWithXmlValueRecord13_2[] seq_XSDSequenceArrayWithXmlValueRecord13_2; +}; + +type Seq_XSDSequenceArrayWithXmlValueRecord13_1 record { + @Order {value: 1} + Seq_Array_A_3 field1; + + @Order {value: 2} + Seq_Array_B_3 field2; + + @Order {value: 3} + Seq_Array_C_3 field3; +}; + +type Seq_XSDSequenceArrayWithXmlValueRecord13_2 record { + @Order {value: 1} + Seq_Array_D_3 field4; + + @Order {value: 2} + Seq_Array_E_3 field5; + + @Order {value: 3} + Seq__Array_F_3 field6; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXSDSequenceArrayWithXmlValueRecord4() returns error? { + xml xmlValue = xml `123123123123123123`; + XSDSequenceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + + xmlValue = xml `123123123123123123123123123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + + xmlValue = xml `123123123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}]}}]}); + + xmlValue = xml `123123123123123123123123123123123123123123123123123123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("seq_XSDSequenceArrayWithXmlValueRecord13_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `123123123123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value3 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + + xmlValue = xml `123123123123121233123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + + xmlValue = xml `123123123123123132`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `132123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + + xmlValue = xml `123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field5 is not in the correct order in"), msg = (v2).message()); +} + +type XsdSequenceArrayWithXmlValue5 record {| + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XsdSequenceArrayWithXmlValue5[] seq_XsdSequenceArrayWithXmlValue5; +|}; + +type Seq_XsdSequenceArrayWithXmlValue5 record {| + @Order { + value: 1 + } + int age; + + @Order { + value: 2 + } + float salary; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceArrayWithXmlValue5() returns error? { + xml xmlValue; + XsdSequenceArrayWithXmlValue5|Error v; + + xmlValue = xml `1311.11415.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue5: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + + xmlValue = xml `1311.11414.11515.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue5: [{age: 13, salary: 11.1}, {age: 14, salary: 14.1}, {age: 15, salary: 15.1}]}); + + xmlValue = xml `1311.11414.11515.11515.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue5 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlValue = xml `1311.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue5 Element occurs less than the min required times"), msg = (v).message()); +} + +type XSDSequenceArrayWithXmlValueRecord6 record { + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XSDSequenceArrayWithXmlValueRecord6_1[] seq_XSDSequenceArrayWithXmlValueRecord6_1; + + @Sequence { + minOccurs: 2, + maxOccurs: 3 + } + Seq_XSDSequenceArrayWithXmlValueRecord6_2[] seq_XSDSequenceArrayWithXmlValueRecord6_2; +}; + +type Seq_XSDSequenceArrayWithXmlValueRecord6_1 record { + @Order {value: 1} + Seq_Array_A_6 field1; + + @Order {value: 2} + Seq_Array_B_6 field2; +}; + +type Seq_XSDSequenceArrayWithXmlValueRecord6_2 record { + @Order {value: 1} + Seq_Array_D_6 field4; + + @Order {value: 2} + Seq_Array_E_6 field5; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXSDSequenceArrayWithXmlValueRecord6() returns error? { + // TODO: Refactor sequence elements into a array + xml xmlValue = xml `1111111111111111`; + XSDSequenceArrayWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {"seq_XSDSequenceArrayWithXmlValueRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayWithXmlValueRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); +} diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal new file mode 100644 index 00000000..08357acd --- /dev/null +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -0,0 +1,316 @@ +import ballerina/test; + +// TODO: Add tests with attributes +type XsdSequenceWithElementAnnotationWithXmlValue record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1_Xml_Value seq_EA1_Xml_Value; +}; + +type Seq_EA1_Xml_Value record { + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { + xml xmlValue; + XsdSequenceWithElementAnnotationWithXmlValue|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_Xml_Value"), (v).message()); + + xmlValue = xml `ABCABCABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1_Xml_Value: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABCABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_Xml_Value":{EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdSequenceWithElementAnnotationWithXmlValue2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2_With_Xml_Value seq_EA2_With_Xml_Value; +}; + +type Seq_EA2_With_Xml_Value record { + @Order { + value: 1 + } + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { + xml xmlValue; + XsdSequenceWithElementAnnotationWithXmlValue2|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCABCD`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA2_With_Xml_Value: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABCCD`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdSequenceWithElementAnnotationWithXmlValue3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XsdSequenceWithElementAnnotationWithXmlValue3_1 seq_XsdSequenceWithElementAnnotationWithXmlValue3_1; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 seq_XsdSequenceWithElementAnnotationWithXmlValue3_2?; +}; + +type Seq_XsdSequenceWithElementAnnotationWithXmlValue3_1 record { + @Element { + minOccurs: 1, + maxOccurs: 3 + } + @Order {value: 1} + Seq_A_3[] field1; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + @Order {value: 2} + Seq_B_3[] field2?; + + @Element { + minOccurs: 1, + maxOccurs: 3 + } + @Order {value: 3} + Seq_C_3 field3; +}; + +type Seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 record { + @Order {value: 1} + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Seq_D_3[] field4?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + @Order {value: 2} + Seq_E_3[] field5?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + @Order {value: 3} + Seq_F_3[] field6?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithElementAnnotationWithXmlValue3() returns error? { + xml xmlValue; + XsdSequenceWithElementAnnotationWithXmlValue3|Error v2; + + xmlValue = xml `123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}, {value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlValue = xml `123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}]}}); + + xmlValue = xml `123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlValue = xml `2312312312323123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1", "2", "3"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + + xmlValue = xml `33123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 Element occurs less than the min required times"), (v2).message()); + + xmlValue = xml `2312312323123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("Element field3 not found in seq_XsdSequenceWithElementAnnotationWithXmlValue3_1"), (v2).message()); + + xmlValue = xml `231231231232312312`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'i' not present in XML"), (v2).message()); + + xmlValue = xml `123123123123123123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("field5 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `1231222223123123123123123123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("b Element occurs more than the max allowed times"), (v2).message()); +} 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 61ab4892..c5c6a35b 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 @@ -1112,6 +1112,7 @@ public static class XmlAnalyzerMetaData { public String attributePrefix; public String textFieldName; public BMap currentNode; + public final Stack recordTypeStack = new Stack<>(); } /** @@ -1168,7 +1169,6 @@ public void resetFrom(XmlAnalyzerData analyzerData) { */ public static class XmlParserData extends XmlAnalyzerMetaData { public final Stack restFieldsPoints = new Stack<>(); - public final Stack recordTypeStack = new Stack<>(); public final Stack> parents = new Stack<>(); public QualifiedNameMap siblings = new QualifiedNameMap<>(new LinkedHashMap<>()); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java index 8797ac4f..d2a770ef 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java @@ -118,7 +118,7 @@ public static void validateModelGroupInfoStack(XmlAnalyzerMetaData xmlAnalyzerMe } } - public static void popXsdValidationStacks(XmlAnalyzerMetaData xmlAnalyzerMetaData) { +public static void popXsdValidationStacks(XmlAnalyzerMetaData xmlAnalyzerMetaData) { xmlAnalyzerMetaData.xsdModelGroupInfo.pop().forEach((key, value) -> value.validateMinOccurrences()); xmlAnalyzerMetaData.xmlElementInfo.pop(); } @@ -167,9 +167,7 @@ public static void validateModelGroup(ModelGroupInfo modelGroup, } xmlAnalyzerMetaData.currentNode = (BMap) xmlAnalyzerMetaData.nodesStack.pop(); xmlAnalyzerMetaData.modelGroupStack.pop(); - if (xmlAnalyzerMetaData instanceof DataUtils.XmlParserData xmlParserData) { - xmlParserData.rootRecord = xmlParserData.recordTypeStack.pop(); - } + xmlAnalyzerMetaData.rootRecord = xmlAnalyzerMetaData.recordTypeStack.pop(); validateCurrentElementInfo(xmlAnalyzerMetaData); popElementStacksForValidatingGroup(xmlAnalyzerMetaData); } 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 1f8ab102..0dabc1ab 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 @@ -234,8 +234,8 @@ public void parseRecordRest(String startElementName, XmlParserData xmlParserData if (next == END_ELEMENT) { QName endElement = xmlStreamReader.getName(); if (endElement.getLocalPart().equals(startElementName)) { - validateRequiredFields(xmlParserData); validateCurrentElementInfo(xmlParserData); + validateRequiredFields(xmlParserData); popExpectedTypeStacks(xmlParserData); break; } @@ -467,8 +467,8 @@ private void buildDocument(XmlParserData xmlParserData) { if (xmlParserData.fieldHierarchy.empty()) { return; } - validateRequiredFields(xmlParserData); validateCurrentElementInfo(xmlParserData); + validateRequiredFields(xmlParserData); } @SuppressWarnings("unchecked") @@ -486,8 +486,8 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser return; } - validateRequiredFields(xmlParserData); validateCurrentElementInfo(xmlParserData); + validateRequiredFields(xmlParserData); validateModelGroupStack(xmlParserData, elemQName, false); xmlParserData.currentNode = (BMap) xmlParserData.nodesStack.pop(); popExpectedTypeStacks(xmlParserData); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java index 4393d647..ecfb838c 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java @@ -122,8 +122,8 @@ private Object traverseXmlWithRecordAsExpectedType(BXml xml, XmlAnalyzerData ana BXml nextXml = validateRootElement(xml, recordType, analyzerData); Object resultRecordValue = traverseXml(nextXml, recordType, analyzerData); validateModelGroupStackForRootElement(analyzerData); - DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); validateCurrentElementInfo(analyzerData); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); return resultRecordValue; } @@ -309,7 +309,6 @@ private void convertElement(BXmlItem xmlItem, XmlAnalyzerData analyzerData) { convertToFieldType(xmlItem, currentField, currentField.getFieldName(), currentFieldType, analyzerData.currentNode, analyzerData); - validateCurrentElementInfo(analyzerData); validateModelGroupStack(analyzerData, elementQName, false); } @@ -428,8 +427,8 @@ private void convertToRecordType(BXmlItem xmlItem, Type currentFieldType, String RecordType prevRecord = analyzerData.rootRecord; analyzerData.rootRecord = elementType; traverseXml(xmlItem.getChildrenSeq(), currentFieldType, analyzerData); - DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); validateCurrentElementInfo(analyzerData); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); DataUtils.popExpectedTypeStacks(analyzerData); analyzerData.rootRecord = prevRecord; analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); @@ -441,8 +440,8 @@ private void convertToMapType(BXmlItem xmlItem, Type fieldType, Type elementType analyzerData.currentNode = updateNextMappingValue( elementType, fieldName, fieldType, mapValue, analyzerData); traverseXml(xmlItem.getChildrenSeq(), fieldType, analyzerData); - DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); validateCurrentElementInfo(analyzerData); + DataUtils.validateRequiredFields(analyzerData, analyzerData.currentNode); DataUtils.popExpectedTypeStacks(analyzerData); analyzerData.currentNode = (BMap) analyzerData.nodesStack.pop(); } @@ -949,10 +948,12 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, private void updateNextRecordForXsd(XmlAnalyzerData xmlAnalyzerData, String fieldName, Type fieldType, Object temp, Object currentNode) { if (fieldType.getTag() == TypeTags.RECORD_TYPE_TAG) { - DataUtils.updateExpectedTypeStacks((RecordType) fieldType, xmlAnalyzerData); RecordType recType = (RecordType) fieldType; + DataUtils.updateExpectedTypeStacks((RecordType) fieldType, xmlAnalyzerData); + initializeXsdInformation(recType, xmlAnalyzerData); xmlAnalyzerData.currentNode = updateNextMappingValue(recType, fieldName, fieldType, (BMap) currentNode, xmlAnalyzerData); + xmlAnalyzerData.recordTypeStack.push(xmlAnalyzerData.rootRecord); xmlAnalyzerData.rootRecord = recType; return; } @@ -969,8 +970,10 @@ private void updateNextRecordForXsd(XmlAnalyzerData xmlAnalyzerData, String fiel } if (elementType.getTag() == TypeTags.RECORD_TYPE_TAG) { - DataUtils.updateExpectedTypeStacks((RecordType) elementType, xmlAnalyzerData); RecordType recType = (RecordType) elementType; + DataUtils.updateExpectedTypeStacks((RecordType) elementType, xmlAnalyzerData); + initializeXsdInformation(recType, xmlAnalyzerData); + xmlAnalyzerData.recordTypeStack.push(xmlAnalyzerData.rootRecord); xmlAnalyzerData.rootRecord = recType; xmlAnalyzerData.currentNode = updateNextMappingValue(recType, fieldName, fieldType, (BMap) currentNode, xmlAnalyzerData); From 3ed008cd30cfc9c0282666259ece9134d5b68379 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Tue, 12 Nov 2024 11:53:31 +0530 Subject: [PATCH 27/58] Add tests for xsd choice and sequences arrays with parse type API --- .../xsd_choice_array_test_with_parse_type.bal | 213 +++++++ ballerina/tests/xsd_choice_test.bal | 2 +- ...ith_element_annotation_with_parse_type.bal | 216 +++++++ .../tests/xsd_choice_test_with_parse_type.bal | 529 ++++++++++++++++++ ..._with_name_annotations_with_parse_type.bal | 152 +++++ .../xsd_element_test_with_parse_type.bal | 206 +++++++ ...sd_sequence_test_with_name_annotation.bal} | 0 ...t_with_name_annotation_with_parse_type.bal | 216 +++++++ ...h_namespace_annotation_with_parse_type.bal | 234 ++++++++ 9 files changed, 1767 insertions(+), 1 deletion(-) create mode 100644 ballerina/tests/xsd_choice_array_test_with_parse_type.bal create mode 100644 ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal create mode 100644 ballerina/tests/xsd_choice_test_with_parse_type.bal create mode 100644 ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal create mode 100644 ballerina/tests/xsd_element_test_with_parse_type.bal rename ballerina/tests/{xsd_test_with_name_annotation.bal => xsd_sequence_test_with_name_annotation.bal} (100%) create mode 100644 ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal create mode 100644 ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal diff --git a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal new file mode 100644 index 00000000..46ed9c0c --- /dev/null +++ b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal @@ -0,0 +1,213 @@ +import ballerina/test; + +type XsdChoiceArrayWithXmlValue record {| + @Choice { + minOccurs: 1, + maxOccurs: 2 + } + Choice_XsdChoiceArrayWithXmlValue choice_XsdChoiceArrayWithXmlValue; +|}; + +type Choice_XsdChoiceArrayWithXmlValue record {| + int[] age?; + float[] salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArrayWithXmlValue() returns error? { + xml xmlValue; + XsdChoiceArrayWithXmlValue|Error v; + + xmlValue = xml `1311.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {age: [13], salary: [11.1]}}); + + xmlValue = xml `1312`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {age: [13, 12]}}); + + xmlValue = xml `11.112.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {salary: [11.1, 12.1]}}); + + xmlValue = xml `1311.11414.11515.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue Element occurs more than the max allowed times")); +} + +type XsdChoiceArrayWithXmlValue2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 2 + } + Choice_XsdChoiceArrayWithXmlValue2 choice_XsdChoiceArrayWithXmlValue2; + + @Choice { + minOccurs: 0, + maxOccurs: 2 + } + Choice_XsdChoiceArrayWithXmlValue2_2 choice_XsdChoiceArrayWithXmlValue2_2?; +|}; + +type Choice_XsdChoiceArrayWithXmlValue2 record {| + int[] age?; + float[] salary?; +|}; + +type Choice_XsdChoiceArrayWithXmlValue2_2 record {| + int[] age2?; + float[] salary2?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArrayWithXmlValue2() returns error? { + xml xmlValue; + XsdChoiceArrayWithXmlValue2|Error v; + + xmlValue = xml `1311.11311.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13], salary: [11.1]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13], salary2: [11.1]}}); + + xmlValue = xml `13131313`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13, 13]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13, 13]}}); + + xmlValue = xml `13131311.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13, 13]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13], salary2: [11.1]}}); + + xmlValue = xml `13131311.11415`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2_2 Element occurs more than the max allowed times")); + + xmlValue = xml `13131313`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2 Element occurs more than the max allowed times")); + + xmlValue = xml `1313`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13, 13]}}); + + xmlValue = xml `1313`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2' not present in XML"), (v).message()); +} + +type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13 record { + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1 choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1; + + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2 choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2; +}; + +type Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1 record { + Choice_Array_A_3[] field1?; + Choice_Array_B_3[] field2?; + Choice_Array_C_3[] field3?; +}; + +type Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2 record { + Choice_Array_D_3[] field4?; + Choice_Array_E_3[] field5?; + Choice__Array_F_3[] field6?; +}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord4() returns error? { + xml xmlValue = xml `123123123123123123`; + XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1: {field1: [{value1: {a: ["1"], b:["2"], c: ["3"]}}],field2: [{value2: {d: ["1"], e: ["2"], f: ["3"]}}], field3: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2: {field4: [{value1: {a: ["1"], b: ["2"], c: ["3"]}}], field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + + xmlValue = xml `22123233123123123`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1: {field1: [{value1: {b:["2", "2"]}}, {value1: {a: ["1"], b:["2"], c: ["3"]}}], field3: [{value3: {h: ["2"], i: ["3", "3"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2: {field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}, {value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + + xmlValue = xml `22123233123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is error); + test:assertTrue((v2).message().includes("choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2 Element occurs less than the min required times"), (v2).message()); + + xmlValue = xml `221232331123123`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is error); + test:assertTrue((v2).message().includes("value2 Element occurs less than the min required times"), (v2).message()); +} + +type XsdChoiceArrayWithXmlValue5 record {| + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_XsdChoiceArrayWithXmlValue5 choice_XsdChoiceArrayWithXmlValue5; +|}; + +type Choice_XsdChoiceArrayWithXmlValue5 record {| + int[] age?; + float[] salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceArrayWithXmlValue5() returns error? { + xml xmlValue; + XsdChoiceArrayWithXmlValue5|Error v; + + xmlValue = xml `1311.114`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue5: {age: [13, 14], salary: [11.1]}}); + + xmlValue = xml `1311.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue5: {age: [13], salary: [11.1]}}); + + xmlValue = xml `1311.11414.11515.11515.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue5 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlValue = xml `13`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue5 Element occurs less than the min required times"), msg = (v).message()); +} + +type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6 record { + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_1 choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_1; + + @Choice { + minOccurs: 2, + maxOccurs: 4 + } + Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_2 choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_2; +}; + +type Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_1 record { + Choice_Array_A_6[] field1?; + Choice_Array_B_6[] field2?; +}; + +type Choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_2 record { + Choice_Array_D_6[] field4?; + Choice_Array_E_6[] field5?; +}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6() returns error? { + xml xmlValue = xml `1111111111111111`; + XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_1: {field1: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field2: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1", "1"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_2: {field4: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field5: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1","1"]}}]}}); +} diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 19fc4a85..27681ba2 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -439,7 +439,7 @@ function testXsdChoice8() returns error? { xmlStr = string `102`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("failed to parse xml: choice_XSDChoiceRecord8_2 Element occurs less than the min required times")); + test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_2 Element occurs less than the min required times")); xmlStr = string `SuccessFail2`; v2 = parseString(xmlStr); diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal new file mode 100644 index 00000000..0a1df5ba --- /dev/null +++ b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal @@ -0,0 +1,216 @@ +import ballerina/test; + +type XsdChoiceWithElementAnnotationWithXmlValue record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_EA1 seq_EA1?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { + xml xmlValue; + XsdChoiceWithElementAnnotationWithXmlValue|Error v; + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlValue = xml ``; + v = parseAsType(xmlValue); + test:assertEquals(v, {}); + + xmlValue = xml `ABABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA3: ["AB", "AB", "AB", "AB"]}}); + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); + + xmlValue = xml `ABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC", "ABC"]}}); + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `ABCABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA1 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABCABCABCABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); +} + +type XsdChoiceWithElementAnnotationWithXmlValue2 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_EA2 seq_EA2; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { + xml xmlValue; + XsdChoiceWithElementAnnotationWithXmlValue2|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCABCD`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABCCD`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdChoiceWithElementAnnotationWithXmlValue3 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XsdChoiceWithElementAnnotationWithXmlValue3_1 seq_XsdChoiceWithElementAnnotationWithXmlValue3_1; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XsdChoiceWithElementAnnotationWithXmlValue3_2 seq_XsdChoiceWithElementAnnotationWithXmlValue3_2?; +}; + +type Choice_XsdChoiceWithElementAnnotationWithXmlValue3_1 record { + @Element { + minOccurs: 1, + maxOccurs: 3 + } + Choice_A_3[] field1?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_B_3[] field2?; + + @Element { + minOccurs: 1, + maxOccurs: 3 + } + Choice_C_3 field3?; +}; + +type Choice_XsdChoiceWithElementAnnotationWithXmlValue3_2 record { + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_D_3[] field4?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_E_3[] field5?; + + @Element { + minOccurs: 0, + maxOccurs: 3 + } + Choice_F_3[] field6?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithElementAnnotationWithXmlValue3() returns error? { + xml xmlValue; + XsdChoiceWithElementAnnotationWithXmlValue3|Error v2; + + xmlValue = xml `ABCABCABCABCABCABCABCABCABC`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {seq_XsdChoiceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["ABC"]}}, {value1: {a: ["ABC", "ABC"]}}, {value1: {a: ["ABC", "ABC", "ABC"]}}]}, seq_XsdChoiceWithElementAnnotationWithXmlValue3_2: {field5: [{value2: {d: "ABC"}}, {value2: {d: "ABC"}}, {value2: {d: "ABC"}}]}}); +} diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal new file mode 100644 index 00000000..41e8697a --- /dev/null +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -0,0 +1,529 @@ +import ballerina/test; + +type XSDChoiceWithXmlValueRecord record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord choice_XSDChoiceWithXmlValueRecord?; +|}; + +type Choice_XSDChoiceWithXmlValueRecord record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceWithXmlValue() returns error? { + xml xmlValue; + XSDChoiceWithXmlValueRecord|Error v; + + xmlValue = xml `10`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {age: 10}}); + + xmlValue = xml `10.5`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {salary: 10.5}}); + + xmlValue = xml `1011.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `11.111.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml ``; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs less than the min required times"), (v).message()); +} + +type XSDChoiceWithXmlValueRecordP2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecordP2 choice_XSDChoiceWithXmlValueRecordP2; +|}; + +type Choice_XSDChoiceWithXmlValueRecordP2 record {| + @Element { + minOccurs: 1, + maxOccurs: 3 + } + int[] age?; + float salary?; + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string[] name?; +|}; + +type XSDChoiceWithXmlValueP1Record record {| + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueP1Record choice_XSDChoiceWithXmlValueP1Record?; +|}; + +type Choice_XSDChoiceWithXmlValueP1Record record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceWithXmlValueP1() returns error? { + xml xmlValue; + XSDChoiceWithXmlValueP1Record|Error v; + + xmlValue = xml `10`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueP1Record: {age: 10}}); + + xmlValue = xml `10.5`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueP1Record: {salary: 10.5}}); + + xmlValue = xml `1011.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueP1Record Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `11.111.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueP1Record Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml ``; + v = parseAsType(xmlValue); + test:assertEquals(v, {}); +} + +@test:Config {groups: ["xsd", "xsd_Choice"]} +function testXsdChoiceWithXmlValueP2() returns error? { + xml xmlValue = xml `10`; + XSDChoiceWithXmlValueRecordP2|Error v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {age: [10]}}); + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {name: ["ABC"]}}); + + xmlValue = xml `11.1`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {salary: 11.1}}); + + xmlValue = xml `10101011.1ABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + + xmlValue = xml `10ABC11.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); +} + +type XSDChoiceWithXmlValueRecord2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord2 choice_XSDChoiceWithXmlValueRecord2; + + int num; +|}; + +type Choice_XSDChoiceWithXmlValueRecord2 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue2() returns error? { + xml xmlValue = xml `310`; + XSDChoiceWithXmlValueRecord2|Error v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord2: {age: 10}, num: 3}); + test:assertEquals((check v).choice_XSDChoiceWithXmlValueRecord2.age, 10); + test:assertEquals((check v).num, 3); + + xmlValue = xml `11.13`; + v = parseAsType(xmlValue); + test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord2: {salary: 11.1}, num: 3}); + test:assertEquals((check v).choice_XSDChoiceWithXmlValueRecord2.salary, 11.1); + test:assertEquals((check v).num, 3); + + xmlValue = xml `11.1103`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord2 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `10311.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord2 Element occurs more than the max allowed times"), (v).message()); +} + +type XSDChoiceWithXmlValueRecord3 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord3 choice_XSDChoiceWithXmlValueRecord3; + + record{int n;} num; +|}; + +type Choice_XSDChoiceWithXmlValueRecord3 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue3() returns error? { + xml xmlValue = xml `310`; + XSDChoiceWithXmlValueRecord3|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord3: {age: 10}, num: {n: 3}}); + test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord3.age, 10); + test:assertEquals((check v2).num, {n: 3}); + + xmlValue = xml `11.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord3: {salary: 11.1}, num: {n: 3}}); + + xmlValue = xml `10311.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord3 Element occurs more than the max allowed times")); + + xmlValue = xml `31011.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord3 Element occurs more than the max allowed times")); +} + +type XSDChoiceWithXmlValueRecord4 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord4 choice_XSDChoiceWithXmlValueRecord4; +|}; + +type Choice_XSDChoiceWithXmlValueRecord4 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue4() returns error? { + xml xmlValue = xml `310`; + XSDChoiceWithXmlValueRecord4|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord4: {age: 10}, num: {n: {n: 3}}}); + test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord4.age, 10); + test:assertEquals((check v2).num, {n: {n: 3}}); + + xmlValue = xml `11.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord4: {salary: 11.1}, num: {n: {n: 3}}}); + + xmlValue = xml `10311.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord4 Element occurs more than the max allowed times")); + + xmlValue = xml `31011.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord4 Element occurs more than the max allowed times")); +} + +type XSDChoiceWithXmlValueRecord5 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord5 choice_XSDChoiceWithXmlValueRecord5; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceWithXmlValueRecord5 record {| + int age?; + float salary?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue5() returns error? { + xml xmlValue = xml `3310`; + XSDChoiceWithXmlValueRecord5|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlValue = xml `311.13`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlValue = xml `11.133`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + + xmlValue = xml `103311.1`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord5 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `33`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord5' not present in XML"), (v2).message()); +} + +type XSDChoiceWithXmlValueRecord6 record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord6_1 choice_XSDChoiceWithXmlValueRecord6_1; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord6_2 choice_XSDChoiceWithXmlValueRecord6_2; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceWithXmlValueRecord6_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceWithXmlValueRecord6_2 record {| + string name?; + string status?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue6() returns error? { + xml xmlValue = xml `3success310`; + XSDChoiceWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord6_1: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceWithXmlValueRecord6_2: {status: "success"}}); + test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord6_1.age, 10); + test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord6_2.status, "success"); + test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals((check v2).num2, {n: {n: 3}}); + + xmlValue = xml `SD33`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"), msg = (v2).message()); + + xmlValue = xml `33SD`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"), msg = (v2).message()); + + xmlValue = xml `SDsuccess33`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord6_2 Element occurs more than the max allowed times"), msg = (v2).message()); +} + +type XSDChoiceWithXmlValueRecord7 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord7_1 choice_XSDChoiceWithXmlValueRecord7_1; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord7_2 choice_XSDChoiceWithXmlValueRecord7_2; +|}; + +type Choice_XSDChoiceWithXmlValueRecord7_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceWithXmlValueRecord7_2 record {| + string name?; + string status?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue7() returns error? { + xml xmlValue = xml `success11.1`; + XSDChoiceWithXmlValueRecord7|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord7_1: {salary: 11.1}, choice_XSDChoiceWithXmlValueRecord7_2: {status: "success"}}); +} + +type XSDChoiceWithXmlValueRecord8 record {| + XSDChoiceWithXmlValueRecord8P test; + int a; +|}; + +type XSDChoiceWithXmlValueRecord8P record {| + record{record {int n;} n;} num; + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord8_1 choice_XSDChoiceWithXmlValueRecord8_1; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord8_2 choice_XSDChoiceWithXmlValueRecord8_2; + record{record {int n;} n;} num2; +|}; + +type Choice_XSDChoiceWithXmlValueRecord8_1 record {| + int age?; + float salary?; +|}; + +type Choice_XSDChoiceWithXmlValueRecord8_2 record {| + RecChoice8 name?; + RecChoice8 status?; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue8() returns error? { + xml xmlValue; + XSDChoiceWithXmlValueRecord8|Error v2; + + xmlValue = xml `3SDAB3102`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceWithXmlValueRecord8_1: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceWithXmlValueRecord8_2: {name: {value1: "SD", value2: "AB"}}}}); + test:assertEquals((check v2).test.choice_XSDChoiceWithXmlValueRecord8_1.age, 10); + test:assertEquals((check v2).test.choice_XSDChoiceWithXmlValueRecord8_2.name, {value1: "SD", value2: "AB"}); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `SuccessFail3311.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceWithXmlValueRecord8_1: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceWithXmlValueRecord8_2: {status: {value1: "Success", value2: "Fail"}}}}); + test:assertEquals((check v2).test.choice_XSDChoiceWithXmlValueRecord8_1.salary, 11.1); + test:assertEquals((check v2).test.num, {n: {n: 3}}); + test:assertEquals((check v2).test.num2, {n: {n: 3}}); + + xmlValue = xml `33SuccessFail11.12`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {a: 2, test: {choice_XSDChoiceWithXmlValueRecord8_1: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}, choice_XSDChoiceWithXmlValueRecord8_2: {status: {value1: "Success", value2: "Fail"}}}}); + + xmlValue = xml `SDAB10SuccessFail11.12`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `10SuccessFail11.12`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `102`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_2 Element occurs less than the min required times"), (v2).message()); + + xmlValue = xml `SuccessFail2`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_1 Element occurs less than the min required times"), (v2).message()); +} + +type XSDChoiceWithXmlValueRecord9 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord9_1 choice_XSDChoiceWithXmlValueRecord9_1; +}; + +type Choice_XSDChoiceWithXmlValueRecord9_1 record { + Choice_A field1?; + Choice_B field2?; + Choice_C field3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue9() returns error? { + xml xmlValue; + XSDChoiceWithXmlValueRecord9|Error v2; + + xmlValue = xml `1`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord9_1: {field2: {value2: {a: "1"}}}}); + + xmlValue = xml `1`; + v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord9_1: {field3: {value3: {c: "1"}}}}); + + xmlValue = xml `11`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord9_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `11`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value1 Element occurs more than the max allowed times"), (v2).message()); +} + +type XSDChoiceWithXmlValueRecord10 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord10_1 choice_XSDChoiceWithXmlValueRecord10_1?; + + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_XSDChoiceWithXmlValueRecord10_2 choice_XSDChoiceWithXmlValueRecord10_2; +}; + +type Choice_XSDChoiceWithXmlValueRecord10_1 record { + Choice_A_10 field1?; + Choice_B_10 field2?; + Choice_C_10 field3?; +}; + +type Choice_XSDChoiceWithXmlValueRecord10_2 record { + Choice_D_10 field4?; + Choice_E_10 field5?; + Choice_F_10 field6?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdChoiceWithXmlValue10() returns error? { + xml xmlValue = xml `12`; + XSDChoiceWithXmlValueRecord10|Error v2 = parseAsType(xmlValue); + test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {"d": "2"}}}}); + + xmlValue = xml `112`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord10_1 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `122`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord10_2 Element occurs more than the max allowed times"), (v2).message()); + + xmlValue = xml `112`; + v2 = parseAsType(xmlValue); + test:assertTrue(v2 is Error); + test:assertTrue((v2).message().includes("value2 Element occurs more than the max allowed times"), (v2).message()); +} diff --git a/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal new file mode 100644 index 00000000..ebd7a840 --- /dev/null +++ b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal @@ -0,0 +1,152 @@ +import ballerina/test; + +type XsdChoiceWithNameAnnotationWithXmlValue record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_Name_EA1 seq_EA1?; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithNameAnnotationWithXmlValue() returns error? { + xml xmlValue; + XsdChoiceWithNameAnnotationWithXmlValue|Error v; + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlValue = xml ``; + v = parseAsType(xmlValue); + test:assertEquals(v, {}); + + xmlValue = xml `ABABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA3: ["AB", "AB", "AB", "AB"]}}); + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); + + xmlValue = xml `ABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC", "ABC"]}}); + + xmlValue = xml `ABC`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + + xmlValue = xml `ABCABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A1 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABCABCABCABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); +} + +type XsdChoiceWithNameAnnotationWithXmlValue2 record { + @Choice { + minOccurs: 0, + maxOccurs: 1 + } + Choice_Name_EA2 seq_EA2; +}; + +@test:Config {groups: ["xsd", "xsd_choice", "xsd_element", "xsd_element_and_sequence"]} +function testXsdChoiceWithNameAnnotationWithXmlValue2() returns error? { + xml xmlValue; + XsdChoiceWithNameAnnotationWithXmlValue2|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCABCD`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABCCD`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} diff --git a/ballerina/tests/xsd_element_test_with_parse_type.bal b/ballerina/tests/xsd_element_test_with_parse_type.bal new file mode 100644 index 00000000..7de7677a --- /dev/null +++ b/ballerina/tests/xsd_element_test_with_parse_type.bal @@ -0,0 +1,206 @@ +import ballerina/test; + +type ElementRecordWithXmlValue record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + string name?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + int age?; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElementWithXmlValue() returns error? { + xml xmlStr = xml `John25`; + ElementRecordWithXmlValue|Error rec = parseAsType(xmlStr); + test:assertEquals(rec, {name: "John", age: 25}); + + xmlStr = xml `John`; + rec = parseAsType(xmlStr); + test:assertEquals(rec, {name: "John"}); +} + +type ElementRecordWithXmlValue2 record { + @Element { + maxOccurs: 10, + minOccurs: 0 + } + string[] name?; + + @Element { + maxOccurs: 3, + minOccurs: 1 + } + int[] age?; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElementWithXmlValue2() returns error? { + xml xmlStr = xml `John25`; + ElementRecordWithXmlValue2|Error rec = parseAsType(xmlStr); + test:assertEquals(rec, {name: ["John"], age: [25]}); + + xmlStr = xml `John`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs less than the min required times")); + + xmlStr = xml `25`; + rec = parseAsType(xmlStr); + test:assertEquals(rec, {age: [25]}); + + xmlStr = xml `1112131415`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = xml `11Abc12131415`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = xml `AbcAbcAbcAbcAbc1112131415`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = xml `11AbcAbc12Abc13Abc1415`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); +} + +type ElementRecordWithXmlValue3 record { + record { + @Element { + maxOccurs: 10, + minOccurs: 0 + } + string[] name?; + + @Element { + maxOccurs: 3, + minOccurs: 1 + } + int[] age?; + + @Element { + maxOccurs: 3, + minOccurs: 2 + } + int[] id?; + } user; + + @Element { + maxOccurs: 1, + minOccurs: 1 + } + int status; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElementWithXmlValue3() returns error? { + xml xmlStr; + ElementRecordWithXmlValue3|Error rec; + + xmlStr = xml `John12353`; + rec = parseAsType(xmlStr); + test:assertEquals(rec, {user: {name: ["John"], age: [35], id: [1, 2]}, status: 3}); + + xmlStr = xml `John123`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs less than the min required times"), (rec).message()); + + xmlStr = xml `12353`; + rec = parseAsType(xmlStr); + test:assertEquals(rec, {user: {age: [35], id: [1, 2]}, status: 3}); + + xmlStr = xml `1211131314153`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + + xmlStr = xml `1211Abc131314153`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + + xmlStr = xml `12AbcAbcAbcAbcAbc11131314153`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + + xmlStr = xml `1211AbcAbc13Abc13Abc14153`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); +} + +type ElementRecordWithXmlValue4 record { + @Element { + maxOccurs: 3, + minOccurs: 2 + } + record { + @Element { + maxOccurs: 4, + minOccurs: 3 + } + string[] firstName; + + @Element { + maxOccurs: 3, + minOccurs: 3 + } + string[] lastName; + }[] name; + + @Element { + maxOccurs: 4, + minOccurs: 3 + } + int[] status; + + @Element { + maxOccurs: 3, + minOccurs: 3 + } + int[] age; +}; + +@test:Config{groups: ["xsd", "xsd_element"]} +function testXsdElementWithXmlValue4() returns error? { + xml xmlStr = xml `JohnJaneJimDoeSmithBrownJohnJaneJimDoeSmithBrown123202530`; + ElementRecordWithXmlValue4|Error rec = parseAsType(xmlStr); + test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3], age: [20, 25, 30]}); + + xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlStr); + test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + + xmlStr = xml `JohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("name Element occurs less than the min required times")); + + xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("name Element occurs more than the max allowed times")); + + xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("lastName Element occurs more than the max allowed times")); + + xmlStr = xml `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; + rec = parseAsType(xmlStr); + test:assertTrue(rec is error); + test:assertTrue((rec).message().includes("firstName Element occurs more than the max allowed times"), (rec).message()); +} \ No newline at end of file diff --git a/ballerina/tests/xsd_test_with_name_annotation.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal similarity index 100% rename from ballerina/tests/xsd_test_with_name_annotation.bal rename to ballerina/tests/xsd_sequence_test_with_name_annotation.bal diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal new file mode 100644 index 00000000..0904ee45 --- /dev/null +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal @@ -0,0 +1,216 @@ +import ballerina/test; + +// TODO: Add tests with attributes +type XsdSequenceWithNameAnnotationWithXmlValue record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1_NameAnnotationWithXmlValue seq_EA1_NameAnnotationWithXmlValue; +}; + +type Seq_EA1_NameAnnotationWithXmlValue record { + + @Name {value: "A1"} + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + string EA1?; + + @Name {value: "A2"} + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + string EA2?; + + @Name {value: "A3"} + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNameAnnotationWithXmlValue() returns error? { + xml xmlValue; + XsdSequenceWithNameAnnotationWithXmlValue|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NameAnnotationWithXmlValue"), (v).message()); + + xmlValue = xml `ABCABCABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA1_NameAnnotationWithXmlValue: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_NameAnnotationWithXmlValue":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABCABABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_NameAnnotationWithXmlValue":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_NameAnnotationWithXmlValue":{EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA1_NameAnnotationWithXmlValue":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} + +type XsdSequenceWithNameAnnotationWithXmlValue2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2_NameAnnotationWithXmlValue seq_EA2_NameAnnotationWithXmlValue; +}; + +type Seq_EA2_NameAnnotationWithXmlValue record { + @Order { + value: 1 + } + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Name {value: "A1"} + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Name {value: "A2"} + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + + @Name {value: "A3"} + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNameAnnotationWithXmlValue2() returns error? { + xml xmlValue; + XsdSequenceWithNameAnnotationWithXmlValue2|Error v; + + xmlValue = xml `ABCABC`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + + xmlValue = xml `ABCABCABCD`; + v = parseAsType(xmlValue); + test:assertEquals(v, {seq_EA2_NameAnnotationWithXmlValue: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_NameAnnotationWithXmlValue": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_NameAnnotationWithXmlValue": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_NameAnnotationWithXmlValue": {EA: {EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABABABABABAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + + xmlValue = xml `ABCABAB`; + v = parseAsType(xmlValue); + test:assertEquals(v, {"seq_EA2_NameAnnotationWithXmlValue": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlValue = xml `ABCABCCD`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `AB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + + xmlValue = xml `ABCAB`; + v = parseAsType(xmlValue); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); +} diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal new file mode 100644 index 00000000..e3a2d510 --- /dev/null +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal @@ -0,0 +1,234 @@ +import ballerina/test; + +// TODO: Add tests with attributes +type XsdSequenceWithNamespaceAnnotationWithXmlValue record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA1_NamespaceAnnotationWithXmlValue seq_EA1_NamespaceAnnotationWithXmlValue; +}; + +type Seq_EA1_NamespaceAnnotationWithXmlValue record { + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Namespace { + uri: "example1.com", + prefix: "ea1" + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Namespace { + uri: "example2.com", + prefix: "ea2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + @Namespace { + uri: "example3.com", + prefix: "ea3" + } + @Order { + value: 3 + } + string[] EA3?; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { + xml xmlStr; + XsdSequenceWithNamespaceAnnotationWithXmlValue|Error v; + + xmlStr = xml `ABCABC`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NamespaceAnnotationWithXmlValue"), (v).message()); + + xmlStr = xml `ABCABCABABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {seq_EA1_NamespaceAnnotationWithXmlValue: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = xml `ABCABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotationWithXmlValue":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = xml `ABCABABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotationWithXmlValue":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + + xmlStr = xml `ABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotationWithXmlValue":{EA3: ["AB", "AB"]}}); + + xmlStr = xml `ABABABABABAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + + xmlStr = xml `ABCABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA1_NamespaceAnnotationWithXmlValue":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + + xmlStr = xml `ABCABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `AB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} + +type XsdSequenceWithNamespaceAnnotationWithXmlValue2 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_EA2_NamespaceAnnotationWithXmlValue seq_EA2_NamespaceAnnotationWithXmlValue; +}; + +type Seq_EA2_NamespaceAnnotationWithXmlValue record { + @Order { + value: 1 + } + record { + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 1 + } + @Namespace { + uri: "example1.com", + prefix: "ea1" + } + string EA1?; + + @Element { + maxOccurs: 1, + minOccurs: 0 + } + @Order { + value: 2 + } + @Namespace { + uri: "example2.com", + prefix: "ea2" + } + string EA2?; + + @Element { + maxOccurs: 4, + minOccurs: 2 + } + + @Namespace { + uri: "example3.com", + prefix: "ea3" + } + @Order { + value: 3 + } + string[] EA3?; + } EA; +}; + +@test:Config {groups: ["xsd", "xsd_sequence", "xsd_element", "xsd_element_and_sequence"]} +function testXsdSequenceWithNamespaceAnnotationWithXmlValue2() returns error? { + xml xmlStr; + XsdSequenceWithNamespaceAnnotationWithXmlValue2|Error v; + + xmlStr = xml `ABCABC`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = xml `ABCABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + + xmlStr = xml `ABCABCABCD`; + v = parseAsType(xmlStr); + test:assertEquals(v, {seq_EA2_NamespaceAnnotationWithXmlValue: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + + xmlStr = xml `ABCABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotationWithXmlValue": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = xml `ABCABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotationWithXmlValue": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = xml `ABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotationWithXmlValue": {EA: {EA3: ["AB", "AB"]}}}); + + xmlStr = xml `ABABABABABAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + + xmlStr = xml `ABCABAB`; + v = parseAsType(xmlStr); + test:assertEquals(v, {"seq_EA2_NamespaceAnnotationWithXmlValue": {EA: {"EA1": "ABC", EA3: ["AB", "AB"]}}}); + + xmlStr = xml `ABCABCCD`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `AB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + + xmlStr = xml `ABCAB`; + v = parseAsType(xmlStr); + test:assertTrue(v is Error); + test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); +} From 1a9b6b0c0266eb6907a0544e5116a34ecec60628 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Tue, 12 Nov 2024 12:00:52 +0530 Subject: [PATCH 28/58] Refactor model group interface --- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 13 +------------ .../data/xmldata/xml/xsd/ModelGroupInfo.java | 4 ---- .../lib/data/xmldata/xml/xsd/SequenceInfo.java | 17 +++++------------ 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 3e497e81..471b1115 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -62,7 +62,6 @@ public void updateOccurrences() { @Override public void validate() { generateElementOptionalityMapIfNotPresent(); - validateCompletedChoice(); markOtherElementsAsOptional(); reset(); } @@ -81,8 +80,7 @@ private void markOtherElementsAsOptional() { } } - @Override - public void reset() { + private void reset() { this.visitedElements.clear(); isMiddleOfElement = false; this.remainingElementCount.putAll(this.maxElementCount); @@ -130,11 +128,6 @@ public void visit(String element, boolean isStartElement) { throw new RuntimeException("Unexpected element " + xmlElementNameMap.get(element) + " found in " + fieldName); } - @Override - public int getOccurences() { - return occurrences; - } - @Override public boolean isElementContains(String elementName) { return allElements.contains(elementName); @@ -151,10 +144,6 @@ public boolean predictStartNewModelGroup(String element) { return !isMiddleOfElement && !isElementContains(element); } - private void validateCompletedChoice() { - - } - public void validateMinOccurrences() { if (this.occurrences < this.minOccurs) { throw new RuntimeException(fieldName + " Element occurs less than the min required times"); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index 862c69b5..07806e5d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -1,12 +1,8 @@ package io.ballerina.lib.data.xmldata.xml.xsd; public interface ModelGroupInfo { - void updateOccurrences(); void validate(); - void reset(); - void visit(String element, boolean isStartElement); - int getOccurences(); boolean isElementContains(String elementName); boolean isMiddleOfModelGroup(); boolean predictStartNewModelGroup(String element); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 93e930df..dbe2d77c 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Stack; -import java.util.stream.Collectors; public class SequenceInfo implements ModelGroupInfo { public String fieldName; @@ -23,7 +22,7 @@ public class SequenceInfo implements ModelGroupInfo { private final Map remainingElementCount = new HashMap<>(); private final Map minimumElementCount = new HashMap<>(); private final Map maxElementCount = new HashMap<>(); - private Map elementOptionality = new HashMap<>(); + private final Map elementOptionality = new HashMap<>(); private final List allElements = new ArrayList<>(); int currentIndex = 0; int elementCount; @@ -75,8 +74,7 @@ public void validate() { reset(); } - @Override - public void reset() { + private void reset() { this.isCompleted = false; this.isMiddleOfElement = false; this.currentIndex = 0; @@ -100,11 +98,6 @@ public void visit(String element, boolean isStartElement) { checkElementOrderAndUpdateElementOccurences(element); } - @Override - public int getOccurences() { - return this.occurrences; - } - @Override public boolean isElementContains(String elementName) { return this.allElements.contains(elementName); @@ -178,7 +171,7 @@ private void checkElementOrderAndUpdateElementOccurences(String element) { " occurs more than the max allowed times in " + fieldName); } else { remainingElementCount.put(element, remainingElementCount.get(nextElement) - 1); - int elementCount = maxElementCount.get(element) - remainingElementCount.get(element).intValue(); + int elementCount = maxElementCount.get(element) - remainingElementCount.get(element); if (elementCount >= minimumElementCount.get(element) && !isLastElement && currentIndex != this.elementCount) { @@ -234,8 +227,8 @@ private HashMap updatePriorityOrder(RecordType fieldType) { private void updateUnvisitedElementsBasedOnPriorityOrder(RecordType fieldType) { this.allElements.addAll(updatePriorityOrder(fieldType).entrySet().stream() .sorted(Map.Entry.comparingByValue()) // Sort by Long values in priority order - .map(entry -> entry.getKey()) // Get xml element name from - .collect(Collectors.toList())); + .map(Map.Entry::getKey) // Get xml element name from + .toList()); this.currentIndex = 0; } From 4cea19f6f8e68290e10a8604a294db9d729e70c3 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 13 Nov 2024 21:06:42 +0530 Subject: [PATCH 29/58] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 9cef1b0d..1d7806c0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -94,7 +94,6 @@ scope = "testOnly" org = "ballerina" name = "lang.value" version = "0.0.0" -scope = "testOnly" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 535a2eb55b6e73f70f193942c2309ee2b610ddca Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 14 Nov 2024 09:48:41 +0530 Subject: [PATCH 30/58] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 1d7806c0..9cef1b0d 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -94,6 +94,7 @@ scope = "testOnly" org = "ballerina" name = "lang.value" version = "0.0.0" +scope = "testOnly" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From ed128fd359fc45368cbda867393fae49869b5202 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 14 Nov 2024 09:55:51 +0530 Subject: [PATCH 31/58] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 9cef1b0d..1d7806c0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -94,7 +94,6 @@ scope = "testOnly" org = "ballerina" name = "lang.value" version = "0.0.0" -scope = "testOnly" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From f4022448c7e3b90a578f8dc6dee5bbb27be67438 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 14 Nov 2024 10:11:14 +0530 Subject: [PATCH 32/58] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 1d7806c0..9cef1b0d 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -94,6 +94,7 @@ scope = "testOnly" org = "ballerina" name = "lang.value" version = "0.0.0" +scope = "testOnly" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 02849f96e460b2c068c7d7a20692182ea112b4b6 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 15 Nov 2024 00:44:45 +0530 Subject: [PATCH 33/58] Add toXml logic in java --- ...sd_sequence_array_test_with_parse_type.bal | 6 + .../xsd_sequence_tests_with_parse_type.bal | 71 ++- ballerina/tests/xsd_to_xml_tests.bal | 235 +++++++++ ballerina/xml_api.bal | 489 ++++++++++-------- native/build.gradle | 2 + .../lib/data/xmldata/utils/Constants.java | 2 + .../lib/data/xmldata/utils/DataUtils.java | 130 ++++- .../lib/data/xmldata/utils/ToXmlUtils.java | 437 ++++++++++++++++ .../data/xmldata/xml/xsd/SequenceInfo.java | 25 +- native/src/main/java/module-info.java | 2 + 10 files changed, 1128 insertions(+), 271 deletions(-) create mode 100644 ballerina/tests/xsd_to_xml_tests.bal create mode 100644 native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 169759e2..1c4ef7c0 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -28,11 +28,17 @@ function testXsdSequenceArrayWithXmlValue() returns error? { xmlValue = xml `1311.11415.1`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + test:assertEquals(toXml(check v), xml `1311.11415.1`); xmlValue = xml `1311.11414.11515.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); + + xmlValue = xml `131415.111.1`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); } type XsdSequenceArrayWithXmlValue2 record {| diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index ad187f18..ef955d2f 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue record {| @Sequence { minOccurs: 1, @@ -27,6 +30,7 @@ function testXsdSequenceWithXmlValue() returns error? { test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue: {age: 13, salary: 11.1}}); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.salary, 11.1); + test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValue)), xmlValue); xmlValue = xml `11.1`; v = parseAsType(xmlValue); @@ -59,6 +63,9 @@ function testXsdSequenceWithXmlValue() returns error? { // test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValueP2 record {| @Sequence { minOccurs: 1, @@ -100,10 +107,12 @@ function testXsdSequenceWithXmlValueP2() returns error? { xmlValue = xml `1311.1ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13], salary: 11.1, name: ["ABC"]}}); + test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValueP2)), xmlValue); xmlValue = xml `13131311.1ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC"]}}); + test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValueP2)), xmlValue); xmlValue = xml `13ABC11.1`; v = parseAsType(xmlValue); @@ -146,6 +155,9 @@ function testXsdSequenceWithXmlValueP2() returns error? { } // TODO: Test with open records. +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue2 record {| @Sequence { minOccurs: 1, @@ -173,10 +185,11 @@ type Seq_XSDSequenceRecordWithXmlValue2 record {| function testXsdSequenceWithXmlValue2() returns error? { xml xmlValue = xml `31311.1`; XSDSequenceRecordWithXmlValue2|Error v = parseAsType(xmlValue); - test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue2: {age: 13, salary: 11.1}, num: 3}); + test:assertEquals(v, {num: 3, seq_XSDSequenceRecordWithXmlValue2: {age: 13, salary: 11.1}}); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.salary, 11.1); test:assertEquals((check v).num, 3); + test:assertEquals(toXml(check v), xml `1311.13`); xmlValue = xml `1311.13`; v = parseAsType(xmlValue); @@ -184,6 +197,7 @@ function testXsdSequenceWithXmlValue2() returns error? { test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue2.salary, 11.1); test:assertEquals((check v).num, 3); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13311.1`; v = parseAsType(xmlValue); @@ -191,6 +205,9 @@ function testXsdSequenceWithXmlValue2() returns error? { test:assertTrue((v).message().includes("Element salary not found in")); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue3 record {| @Sequence { minOccurs: 1, @@ -218,10 +235,11 @@ type Seq_XSDSequenceRecordWithXmlValue3 record {| function testXsdSequenceWithXmlValue3() returns error? { xml xmlValue = xml `31311.1`; XSDSequenceRecordWithXmlValue3|Error v2 = parseAsType(xmlValue); - test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue3: {age: 13, salary: 11.1}, num: {n: 3}}); + test:assertEquals(v2, {num: {n: 3}, seq_XSDSequenceRecordWithXmlValue3: {age: 13, salary: 11.1}}); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.salary, 11.1); test:assertEquals((check v2).num, {n: 3}); + test:assertEquals(toXml(check v2), xml `1311.13`); xmlValue = xml `1311.13`; v2 = parseAsType(xmlValue); @@ -229,6 +247,7 @@ function testXsdSequenceWithXmlValue3() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue3.salary, 11.1); test:assertEquals((check v2).num, {n: 3}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `13311.1`; v2 = parseAsType(xmlValue); @@ -236,6 +255,9 @@ function testXsdSequenceWithXmlValue3() returns error? { test:assertTrue((v2).message().includes("Element salary not found in")); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue4 record {| record{record {int n;} n;} num; @Sequence { @@ -265,13 +287,15 @@ function testXsdSequenceWithXmlValue4() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xmlValue); - xmlValue = xml `1311.13`; + xmlValue = xml `31311.1`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue4: {age: 13, salary: 11.1}, num: {n: {n: 3}}}); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue4.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `13311.1`; v2 = parseAsType(xmlValue); @@ -279,6 +303,9 @@ function testXsdSequenceWithXmlValue4() returns error? { test:assertTrue((v2).message().includes("Element salary not found in")); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue5 record {| record{record {int n;} n;} num; @Sequence { @@ -310,6 +337,7 @@ function testXsdSequenceWithXmlValue5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.13`); xmlValue = xml `31311.13`; v2 = parseAsType(xmlValue); @@ -318,6 +346,7 @@ function testXsdSequenceWithXmlValue5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.13`); xmlValue = xml `1311.133`; v2 = parseAsType(xmlValue); @@ -326,6 +355,7 @@ function testXsdSequenceWithXmlValue5() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue5.salary, 11.1); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.13`); xmlValue = xml `1333`; v2 = parseAsType(xmlValue); @@ -333,6 +363,9 @@ function testXsdSequenceWithXmlValue5() returns error? { test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue6 record {| record{record {int n;} n;} num; @Sequence { @@ -383,6 +416,7 @@ function testXsdSequenceWithXmlValue6() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess3`); xmlValue = xml `SDsuccess331311.1`; v2 = parseAsType(xmlValue); @@ -393,6 +427,7 @@ function testXsdSequenceWithXmlValue6() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess3`); xmlValue = xml `33SDsuccess1311.1`; v2 = parseAsType(xmlValue); @@ -403,6 +438,7 @@ function testXsdSequenceWithXmlValue6() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess3`); xmlValue = xml `SDsuccess1311.133`; v2 = parseAsType(xmlValue); @@ -413,6 +449,7 @@ function testXsdSequenceWithXmlValue6() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue6_2.status, "success"); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess3`); xmlValue = xml `SD13success11.1`; v2 = parseAsType(xmlValue); @@ -445,6 +482,9 @@ function testXsdSequenceWithXmlValue6() returns error? { test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue7 record {| @Sequence { minOccurs: 1, @@ -490,9 +530,12 @@ function testXsdSequenceWithXmlValue7() returns error? { test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_1.age, 13); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_1.salary, 11.1); test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_2.name, "SD"); - test:assertEquals((check v2).seq_XSDSequenceRecordWithXmlValue7_2.status, "success"); + test:assertEquals(toXml(check v2), xml `1311.1SDsuccess`); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue8 record {| XSDSequenceRecordWithXmlValue8P2 test; int 'check; @@ -539,13 +582,17 @@ type Seq_XSDSequenceRecordWithXmlValue8_2 record {| function testXsdSequenceWithXmlValue8() returns error? { xml xmlValue = xml `SDsuccess1311.12`; XSDSequenceRecordWithXmlValue8|Error v2 = parseAsType(xmlValue); - test:assertEquals(v2, {'check: 2, test: {seq_XSDSequenceRecordWithXmlValue8_1: {age: 13, salary: 11.1}, seq_XSDSequenceRecordWithXmlValue8_2: {name: "SD", status: "success"}}}); + test:assertEquals(v2, {'check: 2, test: {seq_XSDSequenceRecordWithXmlValue8_1: {salary: 11.1, age: 13}, seq_XSDSequenceRecordWithXmlValue8_2: {name: "SD", status: "success"}}}); test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_1.age, 13); test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_1.salary, 11.1); test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_2.name, "SD"); test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue8_2.status, "success"); + test:assertEquals(toXml(check v2), xml `1311.1SDsuccess2`); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue9 record {| XSDSequenceRecordWithXmlValue9P test; int a; @@ -643,6 +690,9 @@ function testXsdSequenceWithXmlValue9() returns error? { test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue10 record {| XSDSequenceRecordWithXmlValue10P test; int a; @@ -740,6 +790,9 @@ function testXsdSequenceWithXmlValue10() returns error? { test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue11 record {| XSDSequenceRecordWithXmlValue11P test; int a; @@ -838,6 +891,9 @@ function testXsdSequenceWithXmlValue11() returns error? { test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue12 record { @Sequence { minOccurs: 1, @@ -864,6 +920,9 @@ function testXsdSequenceWithXmlValue12() returns error? { test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue12_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {a: "1", b: "2", c: "3"}}, field3: {value3: {a: "1", b: "2", c: "3"}}}}); } +@Name { + value: "Root" +} type XSDSequenceRecordWithXmlValue13 record { @Sequence { minOccurs: 0, @@ -905,10 +964,12 @@ function testXsdSequenceWithXmlValue13() returns error? { xml xmlValue = xml `123123123123123123`; XSDSequenceRecordWithXmlValue13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertEquals(toXml(check v2), xml `123123123123123123`); xmlValue = xml `123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertEquals(toXml(check v2), xml `123123123`); xmlValue = xml `123123123123123123123`; v2 = parseAsType(xmlValue); diff --git a/ballerina/tests/xsd_to_xml_tests.bal b/ballerina/tests/xsd_to_xml_tests.bal new file mode 100644 index 00000000..c4f0e6b6 --- /dev/null +++ b/ballerina/tests/xsd_to_xml_tests.bal @@ -0,0 +1,235 @@ +import ballerina/test; + +@Name { + value: "A" +} +type ToXml1 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml seq_a; +}; + +type Seq_A_toXml record { + @Order { + value: 3 + } + string c; + + @Order { + value: 1 + } + string a; + + @Order { + value: 2 + } + string b; +}; + +@Name { + value: "A" +} +type ToXml2 record { + Seq_A_toXml2 name; +}; + +type Seq_A_toXml2 record { + @Order { + value: 3 + } + string c; + + @Order { + value: 1 + } + string a; + + @Order { + value: 2 + } + string b; +}; + +@test:Config {groups: ["xsd", "to_xml"]} +function testToXmlWithSimpleRecord2() { + ToXml2 a; + xml|Error xmlResult; + + a = {name: {b: "B", a: "A", c: "C"}}; + xmlResult = toXml(a); + test:assertEquals(xmlResult, xml `CAB`); +} + +@Name { + value: "A" +} +type ToXml3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3_2 name2; +}; + +type Seq_A_toXml3 record { + @Order { + value: 3 + } + string c; + + @Order { + value: 1 + } + string a; + + @Order { + value: 3 + } + string b?; +}; + +type Seq_A_toXml3_2 record { + @Order { + value: 3 + } + string c2; + + @Order { + value: 1 + } + string a2; + + @Order { + value: 3 + } + string b2; +}; + +@Name { + value: "A" +} +type ToXml4 record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3_2 name2; + + record{record{int n;} name;} name3; +}; + +@Name { + value: "A" +} +type ToXml5 record { + record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3_2 name2; + + record{record{int n;} name;} name3; + } a; + + string c; + + record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXml3_2 name2; + + record{record{int n;} name;} name3; + } b; +}; + +@Name { + value: "Root" +} +type ToXml6 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_XSDSequenceRecord13_1 seq_XSDSequenceRecord13_1?; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord13_2 seq_XSDSequenceRecord13_2; +}; + +@test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlWithXsdProvider} +function testToXmlWithXsd(typedesc recordType, record{} value, xml expected) returns error?{ + xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); + test:assertEquals(xmlResult, expected); +} + +function testToXmlWithXsdProvider() returns [typedesc, record{}, xml][] { + return [[ + ToXml1, + {seq_a: {b: "B", a: "A", c: "C"}}, + xml `ABC` + ], + [ + ToXml2, + {name: {b: "B", a: "A", c: "C"}}, + xml `CAB` + ], + [ + ToXml3, + {name: {b: "B", a: "A", c: "C"}, name2: {b2: "B", a2: "A", c2: "C"}}, + xml `ABCABC` + ], + [ + ToXml4, + {name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}, n: 1}, + xml `1ABCABC1` + ], + [ + ToXml5, + {a: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, b: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, c: "A"}, + xml `1ABCABC1A1ABCABC1` + ], + [ + ToXml6, + {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}, + xml `123123123123123123` + ] + ]; +} \ No newline at end of file diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 7fe2f1be..19dc9a14 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -177,31 +177,31 @@ public isolated function toXml(map mapValue, Options options = {}) retu return convertMapXml(jsonValue); } else if jsonValue is json[] { jsonOptions.rootTag = jsonValue[1].toString(); - return fromJson(jsonValue[0], jsonOptions); + return fromModifiedRecordToXml(jsonValue[0], jsonOptions, inputType); } - return fromJson(jsonValue.toJson(), jsonOptions); + return fromModifiedRecordToXml(jsonValue.toJson(), jsonOptions, inputType); } -isolated function convertMapXml(map|map mapValue) returns xml { - xml xNode = xml ``; - foreach [string, xml|xml[]] [key, xmlVal] in mapValue.entries() { - xml|xml[] values = xmlVal; - if values is xml[] { - foreach xml value in values { - xNode += xml:createElement(key, {}, value); - } - } else { - xNode += xml:createElement(key, {}, values); - } - } - return xml:createElement("root", {}, xNode); -} +// isolated function convertMapXml(map|map mapValue) returns xml { +// xml xNode = xml ``; + // foreach [string, xml|xml[]] [key, xmlVal] in mapValue.entries() { + // xml|xml[] values = xmlVal; + // if values is xml[] { + // foreach xml value in values { + // xNode += xml:createElement(key, {}, value); + // } + // } else { + // xNode += xml:createElement(key, {}, values); + // } + // } +// return xml:createElement("root", {}, xNode); +// } isolated function getModifiedRecord(map mapValue, string textFieldName, typedesc<(map|json)> inputType) returns json|record {}|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.DataUtils"} external; # Provides configurations for converting JSON to XML. -type JsonOptions record {| +public type JsonOptions record {| # The prefix of JSON elements' key which is to be treated as an attribute in the XML representation string attributePrefix = "@"; # The name of the XML elements that represent a converted JSON array entry @@ -259,217 +259,217 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); } -isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, - string? 'key = ()) returns xml|Error { - map namespacesOfElem = {}; - string attributePrefix = options.attributePrefix; - xml xNode = xml ``; - if jNode is map { - foreach [string, json] [k, value] in jNode.entries() { - string jsonKey = k.trim(); - if jsonKey.startsWith(attributePrefix) { - continue; - } - - if jsonKey == options.textFieldName { - xNode += xml:createText(value.toString()); - } else { - namespacesOfElem = check getNamespacesMap(value, options, parentNamespaces); - addNamespaces(allNamespaces, namespacesOfElem); - if value is json[] { - xNode += check traverseNode(value, allNamespaces, namespacesOfElem, options, jsonKey); - } else { - xNode += - check getElement(jsonKey, check traverseNode(value, allNamespaces, namespacesOfElem, options), - allNamespaces, options, - check getAttributesMap(value, options, allNamespaces, parentNamespaces)); - } - } - } - } else if jNode is json[] { - foreach var i in jNode { - string arrayEntryTagKey = EMPTY_STRING; - if 'key is string { - arrayEntryTagKey = 'key; - } else if options.arrayEntryTag != EMPTY_STRING { - arrayEntryTagKey = options.arrayEntryTag; - } - namespacesOfElem = check getNamespacesMap(i, options, parentNamespaces); - addNamespaces(allNamespaces, namespacesOfElem); - if options.arrayEntryTag == EMPTY_STRING { - xNode += check getElement(arrayEntryTagKey, - check traverseNode(i, allNamespaces, namespacesOfElem, options, 'key), - allNamespaces, options, - check getAttributesMap(i, options, allNamespaces, parentNamespaces)); - } else { - xNode += check getElement(arrayEntryTagKey, - check traverseNode(i, allNamespaces, namespacesOfElem, options), - allNamespaces, options, - check getAttributesMap(i, options, allNamespaces, parentNamespaces)); - } - } - } else { - xNode = xml:createText(jNode.toString()); - } - return xNode; -} - -isolated function isSingleNode(json node) returns boolean { - map|error jMap = node.ensureType(); - return node !is json[] && (jMap !is map || jMap.length() <= 1); -} - -isolated function getElement(string name, xml children, map namespaces, JsonOptions options, - map attributes = {}) returns xml|Error { - string attributePrefix = options.attributePrefix; - string userAttributePrefix = options.userAttributePrefix; - xml:Element element; - int? index = name.indexOf(":"); - if index is int { - string prefix = name.substring(0, index); - - string elementName; - if userAttributePrefix !is EMPTY_STRING { - elementName = removeUserAttributePrefix(name, userAttributePrefix, index); - } else { - elementName = name.substring(index + 1, name.length()); - } - - string namespaceUrl = attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); - if namespaceUrl == EMPTY_STRING { - namespaceUrl = namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); - if namespaceUrl != EMPTY_STRING { - attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = namespaceUrl; - } - } - if namespaceUrl == EMPTY_STRING { - element = xml:createElement(elementName, attributes, children); - } else { - element = xml:createElement(string `{${namespaceUrl}}${elementName}`, attributes, children); - } - } else { - if name.startsWith(attributePrefix) { - return error("attribute cannot be an object or array"); - } - map newAttributes = attributes; - if newAttributes.hasKey(string `{${XMLNS_NAMESPACE_URI}}`) { - string value = newAttributes.get(string `{${XMLNS_NAMESPACE_URI}}`); - _ = newAttributes.remove(string `{${XMLNS_NAMESPACE_URI}}`); - newAttributes[XMLNS] = value; - } - if userAttributePrefix !is EMPTY_STRING { - element = xml:createElement(removeUserAttributePrefix(name, userAttributePrefix, ()), newAttributes, children); - } else { - element = xml:createElement(name, newAttributes, children); - } - } - return element; -} - -isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string { - int? usrAttIndex = name.indexOf(userAttributePrefix); - if usrAttIndex is int { - return name.substring(usrAttIndex + 1, name.length()); - } - - if index is int { - return name.substring(index + 1, name.length()); - } - return name; -} - -isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, - map parentNamespaces = {}) returns map|Error { - map attributes = parentNamespaces.clone(); - map|error attr = jTree.ensureType(); - string attributePrefix = options.attributePrefix; - if attr !is map { - return attributes; - } - - foreach [string, json] [k, v] in attr.entries() { - if !k.startsWith(attributePrefix) { - continue; - } - - if v is map || v is json[] { - return error("attribute cannot be an object or array"); - } - - int? index = k.indexOf(":"); - if index is int { - string suffix = k.substring(index + 1); - if k.startsWith(attributePrefix + XMLNS) { - attributes[string `{${XMLNS_NAMESPACE_URI}}${suffix}`] = v.toString(); - } else { - int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); - string prefix = k.substring(startIndex, index); - string namespaceUrl = namespaces.get(string `{${XMLNS_NAMESPACE_URI}}${prefix}`); - attributes[string `{${namespaceUrl}}${suffix}`] = v.toString(); - } - } else { - if k == attributePrefix + XMLNS { - attributes[XMLNS] = v.toString(); - } else { - int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); - attributes[k.substring(startIndex)] = v.toString(); - } - } - } - return attributes; -} - -isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int { - int startIndex = 1; - if attributePrefix !is ATTRIBUTE_PREFIX { - return startIndex; - } - - int? location = userAttributePrefix is EMPTY_STRING ? 'key.indexOf("_") : 'key.indexOf(userAttributePrefix); - if location is int { - startIndex = location + 1; - } - return startIndex; -} - -isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) - returns map|Error { - map namespaces = parentNamespaces.clone(); - map|error attr = jTree.ensureType(); - string attributePrefix = options.attributePrefix; - if attr !is map { - return namespaces; - } - - foreach [string, json] [k, v] in attr.entries() { - if !k.startsWith(attributePrefix) { - continue; - } - - if v is map|json[] { - return error("attribute cannot be an object or array."); - } - - if !k.startsWith(attributePrefix + XMLNS) { - continue; - } - - int? index = k.indexOf(":"); - if index is int { - string prefix = k.substring(index + 1); - namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString(); - } else { - namespaces[string `{${XMLNS_NAMESPACE_URI}}`] = v.toString(); - } - } - return namespaces; -} - -isolated function addNamespaces(map allNamespaces, map namespaces) { - foreach [string, string] ['key, namespace] in namespaces.entries() { - allNamespaces['key] = namespace; - } -} +// isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, +// string? 'key = ()) returns xml|Error { +// map namespacesOfElem = {}; +// string attributePrefix = options.attributePrefix; +// xml xNode = xml ``; +// if jNode is map { +// foreach [string, json] [k, value] in jNode.entries() { +// string jsonKey = k.trim(); +// if jsonKey.startsWith(attributePrefix) { +// continue; +// } + +// if jsonKey == options.textFieldName { +// xNode += xml:createText(value.toString()); +// } else { +// namespacesOfElem = check getNamespacesMap(value, options, parentNamespaces); +// addNamespaces(allNamespaces, namespacesOfElem); +// if value is json[] { +// xNode += check traverseNode(value, allNamespaces, namespacesOfElem, options, jsonKey); +// } else { +// xNode += +// check getElement(jsonKey, check traverseNode(value, allNamespaces, namespacesOfElem, options), +// allNamespaces, options, +// check getAttributesMap(value, options, allNamespaces, parentNamespaces)); +// } +// } +// } +// } else if jNode is json[] { +// foreach var i in jNode { +// string arrayEntryTagKey = EMPTY_STRING; +// if 'key is string { +// arrayEntryTagKey = 'key; +// } else if options.arrayEntryTag != EMPTY_STRING { +// arrayEntryTagKey = options.arrayEntryTag; +// } +// namespacesOfElem = check getNamespacesMap(i, options, parentNamespaces); +// addNamespaces(allNamespaces, namespacesOfElem); +// if options.arrayEntryTag == EMPTY_STRING { +// xNode += check getElement(arrayEntryTagKey, +// check traverseNode(i, allNamespaces, namespacesOfElem, options, 'key), +// allNamespaces, options, +// check getAttributesMap(i, options, allNamespaces, parentNamespaces)); +// } else { +// xNode += check getElement(arrayEntryTagKey, +// check traverseNode(i, allNamespaces, namespacesOfElem, options), +// allNamespaces, options, +// check getAttributesMap(i, options, allNamespaces, parentNamespaces)); +// } +// } +// } else { +// xNode = xml:createText(jNode.toString()); +// } +// return xNode; +// } + +// isolated function isSingleNode(json node) returns boolean { +// map|error jMap = node.ensureType(); +// return node !is json[] && (jMap !is map || jMap.length() <= 1); +// } + +// isolated function getElement(string name, xml children, map namespaces, JsonOptions options, +// map attributes = {}) returns xml|Error { +// string attributePrefix = options.attributePrefix; +// string userAttributePrefix = options.userAttributePrefix; +// xml:Element element; +// int? index = name.indexOf(":"); +// if index is int { +// string prefix = name.substring(0, index); + +// string elementName; +// if userAttributePrefix !is EMPTY_STRING { +// elementName = removeUserAttributePrefix(name, userAttributePrefix, index); +// } else { +// elementName = name.substring(index + 1, name.length()); +// } + +// string namespaceUrl = attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); +// if namespaceUrl == EMPTY_STRING { +// namespaceUrl = namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); +// if namespaceUrl != EMPTY_STRING { +// attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = namespaceUrl; +// } +// } +// if namespaceUrl == EMPTY_STRING { +// element = xml:createElement(elementName, attributes, children); +// } else { +// element = xml:createElement(string `{${namespaceUrl}}${elementName}`, attributes, children); +// } +// } else { +// if name.startsWith(attributePrefix) { +// return error("attribute cannot be an object or array"); +// } +// map newAttributes = attributes; +// if newAttributes.hasKey(string `{${XMLNS_NAMESPACE_URI}}`) { +// string value = newAttributes.get(string `{${XMLNS_NAMESPACE_URI}}`); +// _ = newAttributes.remove(string `{${XMLNS_NAMESPACE_URI}}`); +// newAttributes[XMLNS] = value; +// } +// if userAttributePrefix !is EMPTY_STRING { +// element = xml:createElement(removeUserAttributePrefix(name, userAttributePrefix, ()), newAttributes, children); +// } else { +// element = xml:createElement(name, newAttributes, children); +// } +// } +// return element; +// } + +// isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string { +// int? usrAttIndex = name.indexOf(userAttributePrefix); +// if usrAttIndex is int { +// return name.substring(usrAttIndex + 1, name.length()); +// } + +// if index is int { +// return name.substring(index + 1, name.length()); +// } +// return name; +// } + +// isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, +// map parentNamespaces = {}) returns map|Error { +// map attributes = parentNamespaces.clone(); +// map|error attr = jTree.ensureType(); +// string attributePrefix = options.attributePrefix; +// if attr !is map { +// return attributes; +// } + +// foreach [string, json] [k, v] in attr.entries() { +// if !k.startsWith(attributePrefix) { +// continue; +// } + +// if v is map || v is json[] { +// return error("attribute cannot be an object or array"); +// } + +// int? index = k.indexOf(":"); +// if index is int { +// string suffix = k.substring(index + 1); +// if k.startsWith(attributePrefix + XMLNS) { +// attributes[string `{${XMLNS_NAMESPACE_URI}}${suffix}`] = v.toString(); +// } else { +// int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); +// string prefix = k.substring(startIndex, index); +// string namespaceUrl = namespaces.get(string `{${XMLNS_NAMESPACE_URI}}${prefix}`); +// attributes[string `{${namespaceUrl}}${suffix}`] = v.toString(); +// } +// } else { +// if k == attributePrefix + XMLNS { +// attributes[XMLNS] = v.toString(); +// } else { +// int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); +// attributes[k.substring(startIndex)] = v.toString(); +// } +// } +// } +// return attributes; +// } + +// isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int { +// int startIndex = 1; +// if attributePrefix !is ATTRIBUTE_PREFIX { +// return startIndex; +// } + +// int? location = userAttributePrefix is EMPTY_STRING ? 'key.indexOf("_") : 'key.indexOf(userAttributePrefix); +// if location is int { +// startIndex = location + 1; +// } +// return startIndex; +// } + +// isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) +// returns map|Error { +// map namespaces = parentNamespaces.clone(); +// map|error attr = jTree.ensureType(); +// string attributePrefix = options.attributePrefix; +// if attr !is map { +// return namespaces; +// } + +// foreach [string, json] [k, v] in attr.entries() { +// if !k.startsWith(attributePrefix) { +// continue; +// } + +// if v is map|json[] { +// return error("attribute cannot be an object or array."); +// } + +// if !k.startsWith(attributePrefix + XMLNS) { +// continue; +// } + +// int? index = k.indexOf(":"); +// if index is int { +// string prefix = k.substring(index + 1); +// namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString(); +// } else { +// namespaces[string `{${XMLNS_NAMESPACE_URI}}`] = v.toString(); +// } +// } +// return namespaces; +// } + +// isolated function addNamespaces(map allNamespaces, map namespaces) { +// foreach [string, string] ['key, namespace] in namespaces.entries() { +// allNamespaces['key] = namespace; +// } +// } # Validates an XML document against a provided XML schema. # @@ -495,3 +495,36 @@ isolated function addNamespaces(map allNamespaces, map namespace # ``` public function validate(string|typedesc schema, xml xmlValue) returns boolean = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; + +public isolated function isSingleNode(json node) returns boolean + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) + returns map|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +public isolated function addNamespaces(map allNamespaces, map namespaces) + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +public isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, + map parentNamespaces = {}) returns map|Error + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function getElement(string name, xml children, map namespaces, JsonOptions options, + map attributes = {}) returns xml|Error + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, + string? 'key = ()) returns xml|Error + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +isolated function convertMapXml(map|map mapValue) returns xml + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; + +public isolated function fromModifiedRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error + = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; diff --git a/native/build.gradle b/native/build.gradle index 8398a3a2..a65edca9 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -33,6 +33,8 @@ dependencies { implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'value', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'xml', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'map', version: "${ballerinaLangVersion}" implementation group: 'io.ballerina.stdlib', name: 'constraint-native', version: "${stdlibConstraintVersion}" } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java index 2d5737ef..f6ab4656 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java @@ -59,7 +59,9 @@ private Constants() {} public static final BString PREFIX = StringUtils.fromString("prefix"); public static final BString VALUE = StringUtils.fromString("value"); public static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attributePrefix"); + public static final BString USER_ATTRIBUTE_PREFIX = StringUtils.fromString("userAttributePrefix"); public static final BString TEXT_FIELD_NAME = StringUtils.fromString("textFieldName"); + public static final BString ARRAY_ENTRY_TAG = StringUtils.fromString("arrayEntryTag"); public static final BString ALLOW_DATA_PROJECTION = StringUtils.fromString("allowDataProjection"); public static final BString USE_SEMANTIC_EQUALITY = StringUtils.fromString("useSemanticEquality"); public static final BString ENABLE_CONSTRAINT_VALIDATION = StringUtils.fromString("enableConstraintValidation"); 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 c5c6a35b..6b150320 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 @@ -52,6 +52,7 @@ import java.io.Reader; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -462,6 +463,10 @@ public static Object getModifiedRecord(BMap input, BString text return input; } +// public static Object convertJsonIntoXml(Object value, BMap jsonOptions, BTypedesc inputType) { +// +// } + @SuppressWarnings("unchecked") private static BMap processArrayValue(BMap input, ArrayType arrayType) { Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); @@ -519,6 +524,19 @@ private static BMap addFields(BMap input, Type return recordValue; } + + private static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap input, + HashMap xsdSequencePriorityOrder) { + if (!xsdSequencePriorityOrder.isEmpty()) { + return xsdSequencePriorityOrder.entrySet().stream() + .sorted(Comparator.comparingInt(Map.Entry::getValue)) + .map(entry -> StringUtils.fromString(entry.getKey())) + .toArray(BString[]::new); + } else { + return input.getKeys(); + } + } + private static void processRecordField(Type fieldType, BMap annotations, BMap recordValue, Map.Entry entry, String key, Object value) { @@ -546,11 +564,11 @@ private static void processTypeReferenceType(Type fieldType, BMap recordValue, String key, Object value) { BMap namespaceAnnotRecord = ValueCreator.createMapValue(Constants.JSON_MAP_TYPE); boolean doesNamespaceDefinedInField = false; - if (annotations.size() > 0) { + if (!annotations.isEmpty()) { String fieldName = key; key = getKeyNameFromAnnotation(annotations, key); QName qName = addFieldNamespaceAnnotation(fieldName, key, annotations, namespaceAnnotRecord); - if (!qName.getNamespaceURI().equals("")) { + if (!qName.getNamespaceURI().isEmpty()) { doesNamespaceDefinedInField = true; } String localPart = qName.getLocalPart(); @@ -567,7 +585,7 @@ private static void processTypeReferenceType(Type fieldType, BMap subRecordValue = addFields(((BMap) value), referredType); addNamespaceToSubRecord(key, namespaceAnnotRecord, subRecordValue); - if (annotationRecord.size() > 0) { + if (!annotationRecord.isEmpty()) { subRecordValue.put(annotationRecord.getKeys()[0], annotationRecord.get(annotationRecord.getKeys()[0])); } recordValue.put(StringUtils.fromString(key), subRecordValue); @@ -650,17 +668,19 @@ private static void processRecord(String key, BMap parentAnnota BMap record, Object value, RecordType childType) { BMap parentRecordAnnotations = ValueCreator.createMapValue(Constants.JSON_MAP_TYPE); BMap annotation = childType.getAnnotations(); - if (parentAnnotations.size() > 0) { + if (!parentAnnotations.isEmpty()) { annotation.merge(getFieldNamespaceAndNameAnnotations(key, parentAnnotations), true); processSubRecordAnnotation(parentAnnotations, parentRecordAnnotations); } BMap subRecord = addFields(((BMap) value), childType); - if (annotation.size() > 0) { + if (!annotation.isEmpty()) { processSubRecordAnnotation(annotation, subRecord); } + key = getElementName(annotation, key); record.put(StringUtils.fromString(key), subRecord); - if (parentRecordAnnotations.size() > 0) { + + if (!parentRecordAnnotations.isEmpty()) { record.put(parentRecordAnnotations.getKeys()[0], parentRecordAnnotations.get(parentRecordAnnotations.getKeys()[0])); } @@ -703,8 +723,7 @@ private static void processArray(Type elementType, BMap annotat if (elementType.getTag() == TypeTags.RECORD_TYPE_TAG) { List> records = new ArrayList<>(); for (int i = 0; i < arrayValue.getLength(); i++) { - BMap subRecord = addFields(((BMap) arrayValue.get(i)), - elementType); + BMap subRecord = addFields(((BMap) arrayValue.get(i)), elementType); subRecord = processParentAnnotation(elementType, subRecord); records.add((BMap) subRecord.get(subRecord.getKeys()[0])); } @@ -720,7 +739,7 @@ private static void processArray(Type elementType, BMap annotat record.put(StringUtils.fromString(keyName), ValueCreator.createArrayValue(records.toArray(), TypeCreator.createArrayType(Constants.JSON_ARRAY_TYPE))); } - if (annotationRecord.size() > 0) { + if (!annotationRecord.isEmpty()) { record.put(annotationRecord.getKeys()[0], annotationRecord.get(annotationRecord.getKeys()[0])); } @@ -754,7 +773,7 @@ private static BMap processParentAnnotation(Type type, BMap namespaces = ValueCreator.createMapValue(Constants.JSON_MAP_TYPE); BMap annotations = ((RecordType) type).getAnnotations(); BString rootName = processAnnotation(annotations, type.getName(), namespaces); - if (namespaces.size() > 0) { + if (!namespaces.isEmpty()) { for (Map.Entry namespace : namespaces.entrySet()) { record.put(namespace.getKey(), namespace.getValue()); } @@ -1080,9 +1099,7 @@ private static void getXmlElementNameFromFieldAnnotation(Map fi (BMap) fieldAnnotation.get(fieldAnnotationKey); String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue .getStringValue(Constants.VALUE)); - if (xmlElementNameMap.containsKey(fieldName)) { - xmlElementNameMap.remove(fieldName); - } + xmlElementNameMap.remove(fieldName); xmlElementNameMap.put(xmlElementName, fieldName); } } @@ -1094,6 +1111,91 @@ public static void popMappingTypeStacks(XmlAnalyzerMetaData xmlParserData) { xmlParserData.restTypes.pop(); } + public static HashMap getXsdSequencePriorityOrder(RecordType fieldType) { + return getXsdSequencePriorityOrder(fieldType, false); + } + + public static HashMap getXsdSequencePriorityOrder(RecordType fieldType, + boolean isSequenceElements) { + HashMap elementPriorityOrder = new HashMap<>(); + if (!isSequenceElements) { + return elementPriorityOrder; + } + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + elementPriorityOrder.put(fieldName, + fieldAnnotationValue.getIntValue(Constants.VALUE).intValue()); + } + } + } + } + } + return elementPriorityOrder; + } + + public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordType fieldType) { + ArrayList fieldNamesWithModelGroupAnnotations = new ArrayList<>(); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) + || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + fieldNamesWithModelGroupAnnotations.add(fieldName); + } + } + } + } + } + return fieldNamesWithModelGroupAnnotations; + } + + public static BMap getXmlElementModelGroupMap(BTypedesc typed) { + Type type = TypeUtils.getReferredType(typed.getDescribingType()); + if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { + return null; + } + + BMap xmlModelGroupMap = ValueCreator + .createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_BOOLEAN)); + RecordType recordType = (RecordType) type; + BMap annotations = recordType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) + || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + xmlModelGroupMap.put(StringUtils.fromString(fieldName), true); + } else { + xmlModelGroupMap.put(StringUtils.fromString(fieldName), false); + } + } + } + } + } + return xmlModelGroupMap; + } + public static class XmlAnalyzerMetaData { public Stack> attributeHierarchy = new Stack<>(); public Stack> arrayIndexes = new Stack<>(); @@ -1165,7 +1267,7 @@ public void resetFrom(XmlAnalyzerData analyzerData) { /** * Holds data required for the parsing. * - * @since 0.1.0 + * @since 0.1.1 */ public static class XmlParserData extends XmlAnalyzerMetaData { public final Stack restFieldsPoints = new Stack<>(); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java new file mode 100644 index 00000000..02752522 --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -0,0 +1,437 @@ +package io.ballerina.lib.data.xmldata.utils; + +import io.ballerina.runtime.api.PredefinedTypes; +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.utils.StringUtils; +import io.ballerina.runtime.api.utils.ValueUtils; +import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BTypedesc; +import io.ballerina.runtime.api.values.BXml; +import org.ballerinalang.langlib.map.ToArray; +import org.ballerinalang.langlib.xml.Concat; +import org.ballerinalang.langlib.xml.CreateElement; +import org.ballerinalang.langlib.xml.CreateText; + +import java.util.HashMap; +import java.util.Map; + +public class ToXmlUtils { + private static final BString XMLNS_NAMESPACE_URI = StringUtils.fromString("http://www.w3.org/2000/xmlns/"); + private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attribute_"); + private static final BString XMLNS = StringUtils.fromString("xmlns"); + +// public isolated function fromModifiedRecordToXml(json jsonValue, JsonOptions options = {}) returns xml|Error { +// string? rootTag = options.rootTag; +// map allNamespaces = {}; +// if !isSingleNode(jsonValue) { +// addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); +// return getElement(rootTag ?: "root", +// check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, +// check getAttributesMap(jsonValue, options, allNamespaces)); +// } +// +// map|error jMap = jsonValue.ensureType(); +// if jMap is map { +// if jMap.length() == 0 { +// return xml ``; +// } +// +// json value = jMap.toArray()[0]; +// addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); +// if value is json[] { +// return getElement(rootTag ?: "root", +// check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), +// allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); +// } +// +// string key = jMap.keys()[0]; +// if key == options.textFieldName { +// return xml:createText(value.toString()); +// } +// xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), +// allNamespaces, options, +// check getAttributesMap(value, options, allNamespaces)); +// if rootTag is string { +// return xml:createElement(rootTag, {}, output); +// } +// return output; +// } +// return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); +// } + + public static BXml fromModifiedRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { + Object rootTag = options.get(StringUtils.fromString("rootTag")); + BMap allNamespaces = (BMap) ((BMap) ValueCreator.createMapValue()); + + if (!isSingleNode(jsonValue)) { + addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, + (BMap) ((BMap) ValueCreator.createMapValue()))); + return getElement(rootTag == null ? StringUtils.fromString("root") + : StringUtils.fromString(rootTag.toString()), + traverseNode(jsonValue, allNamespaces, + (BMap) ((BMap) ValueCreator.createMapValue()), options), + allNamespaces, options, getAttributesMap(jsonValue, options, allNamespaces, + (BMap) ((BMap) ValueCreator.createMapValue()))); + } + + try { + BMap jMap = (BMap) ValueUtils.convert(jsonValue, + TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + if (jMap.size() == 0) { + return ValueCreator.createXmlValue(""); + } + + Object value = ToArray.toArray(jMap).getValues()[0]; + addNamespaces(allNamespaces, getNamespacesMap(value, options, + (BMap) ((BMap) ValueCreator.createMapValue()))); + + if (value instanceof BArray arrayNode) { + return getElement(rootTag == null ? StringUtils.fromString("root") + : StringUtils.fromString(rootTag.toString()), + traverseNode(value, allNamespaces, (BMap) ((BMap) + ValueCreator.createMapValue()), options, jMap.getKeys()[0]), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, + (BMap) ((BMap) ValueCreator.createMapValue()))); + } + + BString key = jMap.getKeys()[0]; + if (key.equals(options.get(StringUtils.fromString("textFieldName")))) { + return CreateText.createText(StringUtils.fromString(value.toString())); + } + BXml output = getElement(jMap.getKeys()[0], + traverseNode(value, allNamespaces, + (BMap) ((BMap) ValueCreator.createMapValue()), options), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, + (BMap) ((BMap) ValueCreator.createMapValue()))); + if (rootTag != null) { + return CreateElement.createElement(StringUtils.fromString(rootTag.toString()), + (BMap) ((BMap) ValueCreator.createMapValue()), output); + } + return output; + } catch (BError e) { + return jsonValue == null ? ValueCreator.createXmlValue("") + : CreateText.createText(StringUtils.fromString(jsonValue.toString())); + } + } + + public static BXml convertMapXml(BMap mapValue) { + BXml xNode = ValueCreator.createXmlValue(""); + for (Map.Entry entry : mapValue.entrySet()) { + BString key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof BArray arrayNode) { + for (Object i : arrayNode.getValues()) { + if (i == null) { + continue; + } + xNode = Concat.concat(xNode, CreateElement.createElement(key, + (BMap) ((BMap) ValueCreator.createMapValue()), (BXml) i)); + } + } else { + xNode = Concat.concat(xNode, CreateElement.createElement(key, + (BMap) ((BMap) ValueCreator.createMapValue()), (BXml) value)); + } + } + return CreateElement.createElement(StringUtils.fromString("root"), + (BMap) ((BMap) ValueCreator.createMapValue()), xNode); + } + + public static BXml traverseNode(Object jNode, BMap allNamespaces, + BMap parentNamespaces, BMap options) { + return traverseNode(jNode, allNamespaces, parentNamespaces, options, null); + } + + public static BXml traverseNode(Object jNode, BMap allNamespaces, + BMap parentNamespaces, BMap options, Object keyObj) { + BMap namespacesOfElem; + BXml xNode = ValueCreator.createXmlValue(""); + String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); + + if (jNode instanceof BMap jMap) { + BMap mapNode = (BMap) jMap; + for (Map.Entry entry : mapNode.entrySet()) { + BString k = entry.getKey(); + Object value = entry.getValue(); + String jsonKey = k.getValue().trim(); + if (jsonKey.startsWith(attributePrefix)) { + continue; + } + + if (jsonKey.equals(options.get(Constants.TEXT_FIELD_NAME).toString())) { + xNode = Concat.concat(xNode, CreateText.createText(StringUtils.fromString(value.toString()))); + } else { + namespacesOfElem = getNamespacesMap(value, options, parentNamespaces); + addNamespaces(allNamespaces, namespacesOfElem); + if (value instanceof BArray arrayNode) { + xNode = Concat.concat(xNode, traverseNode(value, allNamespaces, namespacesOfElem, options, k)); + } else { + xNode = Concat.concat(xNode, getElement(k, + traverseNode(value, allNamespaces, namespacesOfElem, options), + allNamespaces, options, + getAttributesMap(value, options, allNamespaces, parentNamespaces))); + } + } + } + } else if (jNode instanceof BArray arrayNode) { + for (Object i : arrayNode.getValues()) { + if (i == null) { + continue; + } + String arrayEntryTagKey = ""; + if (keyObj instanceof BString key) { + arrayEntryTagKey = key.getValue(); + } else if (!options.get(Constants.ARRAY_ENTRY_TAG).toString().equals("")) { + arrayEntryTagKey = options.get(Constants.ARRAY_ENTRY_TAG).toString(); + } + + namespacesOfElem = getNamespacesMap(i, options, parentNamespaces); + addNamespaces(allNamespaces, namespacesOfElem); + if (options.get(Constants.ARRAY_ENTRY_TAG).toString().equals("")) { + xNode = Concat.concat(xNode, getElement(StringUtils.fromString(arrayEntryTagKey), + traverseNode(i, allNamespaces, namespacesOfElem, options, keyObj), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces))); + } else { + xNode = Concat.concat(xNode, getElement(StringUtils.fromString(arrayEntryTagKey), + traverseNode(i, allNamespaces, namespacesOfElem, options), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces))); + } + } + } else { + xNode = CreateText.createText(StringUtils.fromString(StringUtils.getStringValue(jNode))); + } + return xNode; + } + + + public static boolean isSingleNode(Object node) { + if (node instanceof BArray arrayNode) { + // TODO: Convert this into a anydata + if (arrayNode.getElementType().getTag() == TypeTags.JSON_TAG) { + return false; + } + } + + try { + Object convertedValue = ValueUtils + .convert(node, TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + if (convertedValue instanceof BMap mapNode) { + return mapNode.size() <= 1; + } + } catch (BError e) { + return true; + } + return true; + } + + public static BXml getElement(BString name, BXml children, BMap namespaces, + BMap options, BMap attributes) { + String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); + String userAttributePrefix = options.get(Constants.USER_ATTRIBUTE_PREFIX).toString(); + BXml element; + + String nameStr = name.getValue(); + + int index = nameStr.indexOf(":"); + + if (index != -1) { + String prefix = nameStr.substring(0, index); + + String elementName; + if (!userAttributePrefix.equals("")) { + elementName = removeUserAttributePrefix(StringUtils.fromString(nameStr), + StringUtils.fromString(userAttributePrefix), (long) index).getValue(); + } else { + elementName = nameStr.substring(index + 1, nameStr.length()); + } + + String namespaceUrl = attributes.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + + prefix)).toString(); + + if (namespaceUrl.equals("")) { + namespaceUrl = namespaces.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + + prefix)).toString(); + + if (!namespaceUrl.equals("")) { + attributes.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + + prefix), StringUtils.fromString(namespaceUrl)); + } + } + + if (namespaceUrl.equals("")) { + element = CreateElement.createElement(StringUtils.fromString(elementName), attributes, children); + } else { + element = CreateElement.createElement(StringUtils.fromString("{" + namespaceUrl + "}" + elementName), + attributes, children); + } + } else { + if (nameStr.startsWith(attributePrefix)) { + throw DiagnosticLog.createXmlError("attribute cannot be an object or array."); + } + + BMap newAttributes = attributes; + if (newAttributes.containsKey(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}"))) { + String value = newAttributes.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}")).toString(); + newAttributes.remove(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}")); + newAttributes.put(XMLNS, StringUtils.fromString(value)); + } + if (!userAttributePrefix.equals("")) { + element = CreateElement.createElement( + removeUserAttributePrefix(StringUtils.fromString(nameStr), + StringUtils.fromString(userAttributePrefix), null), newAttributes, children); + } else { + element = CreateElement.createElement(StringUtils.fromString(nameStr), newAttributes, children); + } + + } + return element; + } + + + public static BString removeUserAttributePrefix(BString name, BString userAttributePrefix, Object index) { + String nameStr = name.getValue(); + String userAttributePrefixStr = userAttributePrefix.getValue(); + + int usrAttIndex = nameStr.indexOf(userAttributePrefixStr); + if (usrAttIndex != -1) { + return StringUtils.fromString(nameStr.substring(usrAttIndex + 1, nameStr.length())); + } + + if (index instanceof Long indexNum) { + return StringUtils.fromString(nameStr.substring(indexNum.intValue() + 1, nameStr.length())); + } + return StringUtils.fromString(nameStr); + } + + public static BMap getAttributesMap(Object jsonTree, + BMap options, + BMap namespaces, + BMap parentNamespaces) { + BMap attributes = (BMap) parentNamespaces.copy(new HashMap<>()); + try { + BMap attr = (BMap) ValueUtils + .convert(jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + + String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); + for (Map.Entry entry : attr.entrySet()) { + String key = entry.getKey().toString(); + Object value = entry.getValue(); + if (!key.startsWith(attributePrefix)) { + continue; + } + + if (value instanceof BMap || value instanceof BArray) { + DiagnosticLog.createXmlError("attribute cannot be an object or array."); + } + + int index = key.indexOf(":"); + if (index != -1) { + String suffix = key.substring(index + 1); + if (key.startsWith(attributePrefix + XMLNS)) { + attributes.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + suffix), + StringUtils.fromString(StringUtils.getStringValue(value))); + } else { + Long startIndex = + getStartIndex(StringUtils.fromString(attributePrefix), StringUtils + .fromString(options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), + StringUtils.fromString(key)); + String prefix = key.substring(startIndex.intValue(), index); + BString namespaceUrl = + namespaces.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + prefix)); + attributes.put(StringUtils.fromString("{" + namespaceUrl + "}" + suffix), + StringUtils.fromString(StringUtils.getStringValue(value))); + } + } else { + if (key.equals(attributePrefix + XMLNS)) { + attributes.put(XMLNS, StringUtils.fromString(StringUtils.getStringValue(value))); + } else { + Long startIndex = + getStartIndex(StringUtils.fromString(attributePrefix), + StringUtils.fromString(options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), + StringUtils.fromString(key)); + attributes.put(StringUtils.fromString(key.substring(startIndex.intValue())), + StringUtils.fromString(StringUtils.getStringValue(value))); + } + } + } + return attributes; + } catch (BError e) { + return attributes; + } + } + + public static Long getStartIndex(BString attributePrefix, BString userAttributePrefix, BString key) { + String attributePrefixStr = attributePrefix.toString(); + String userAttributePrefixStr = userAttributePrefix.toString(); + String keyStr = key.toString(); + int startIndex = 1; + + if (!attributePrefixStr.equals(ATTRIBUTE_PREFIX.toString())) { + return (long) startIndex; + } + + int location = userAttributePrefixStr.equals("") + ? keyStr.indexOf("_") : keyStr.indexOf(userAttributePrefixStr); + if (location != -1) { + startIndex = location + 1; + } + return (long) startIndex; + } + + public static BMap getNamespacesMap(Object jsonTree, + BMap options, + BMap parentNamespaces) { + BMap namespaces = (BMap) parentNamespaces.copy(new HashMap<>()); + + try { + Object jsonTreeObject = ValueUtils + .convert(jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + + BMap attr = (BMap) jsonTreeObject; + String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); + + for (Map.Entry entry : attr.entrySet()) { + BString key = entry.getKey(); + Object value = entry.getValue(); + if (!key.getValue().startsWith(attributePrefix)) { + continue; + } + + if (value instanceof BMap || value instanceof BArray) { + // TODO: Add error messages + throw DiagnosticLog.createXmlError("attribute cannot be an object or array."); + } + + // TODO: Make this as a constant + if (!key.getValue().startsWith(attributePrefix + XMLNS)) { + continue; + } + + int index = key.getValue().indexOf(":"); + if (index != -1) { + String prefix = key.getValue().substring(index + 1); + + // TODO: Add constants + namespaces.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + prefix), + StringUtils.fromString(StringUtils.getStringValue(value))); + } else { + namespaces.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}"), + StringUtils.fromString(StringUtils.getStringValue(value))); + } + } + return namespaces; + } catch (BError e) { + return namespaces; + } + } + + public static void addNamespaces(BMap allNamespaces, BMap namespaces) { + for (Map.Entry entry: namespaces.entrySet()) { + allNamespaces.put(entry.getKey(), entry.getValue()); + } + } +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index dbe2d77c..a8ff419f 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -201,31 +201,8 @@ private String getUnvisitedElements() { return result; } - private HashMap updatePriorityOrder(RecordType fieldType) { - HashMap elementPriorityOrder = new HashMap<>(); - BMap annotations = fieldType.getAnnotations(); - for (BString annotationKey : annotations.getKeys()) { - String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { - BMap fieldAnnotationValue = - (BMap) fieldAnnotation.get(fieldAnnotationKey); - elementPriorityOrder.put(fieldName, fieldAnnotationValue.getIntValue(Constants.VALUE)); - } - } - } - } - } - return elementPriorityOrder; - } - private void updateUnvisitedElementsBasedOnPriorityOrder(RecordType fieldType) { - this.allElements.addAll(updatePriorityOrder(fieldType).entrySet().stream() + this.allElements.addAll(DataUtils.getXsdSequencePriorityOrder(fieldType, true).entrySet().stream() .sorted(Map.Entry.comparingByValue()) // Sort by Long values in priority order .map(Map.Entry::getKey) // Get xml element name from .toList()); diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java index 4631e40c..4b0d66a9 100644 --- a/native/src/main/java/module-info.java +++ b/native/src/main/java/module-info.java @@ -23,5 +23,7 @@ requires java.xml; requires junit; requires org.apache.commons.lang3; + requires io.ballerina.lang.xml; exports io.ballerina.lib.data.xmldata.xml; + requires io.ballerina.lang.map; } From 5f887b3dfe2a9a4c5591a4b9852debfed2d02ee9 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 15 Nov 2024 20:19:33 +0530 Subject: [PATCH 34/58] Add toxml implementatio without sequence order validation --- ballerina/tests/toXml_test.bal | 20 +- ballerina/xml_api.bal | 82 +++--- .../lib/data/xmldata/utils/DataUtils.java | 79 +++++- .../lib/data/xmldata/utils/ToXmlUtils.java | 250 ++++++++++++------ 4 files changed, 287 insertions(+), 144 deletions(-) diff --git a/ballerina/tests/toXml_test.bal b/ballerina/tests/toXml_test.bal index b9630914..5af7c067 100644 --- a/ballerina/tests/toXml_test.bal +++ b/ballerina/tests/toXml_test.bal @@ -45,20 +45,20 @@ function testToXmlBasic2() returns error? { groups: ["toXml"] } function testToXmlBasic3() returns error? { - xml xmlval1 = xml `12`; - Data3 rec1 = check parseAsType(xmlval1); - xml result1 = check toXml(rec1); - test:assertTrue(result1 == xmlval1); + // xml xmlval1 = xml `12`; + // Data3 rec1 = check parseAsType(xmlval1); + // xml result1 = check toXml(rec1); + // test:assertTrue(result1 == xmlval1); - xml xmlVal2 = xml `12`; - Data4 rec2 = check parseAsType(xmlVal2); - xml result2 = check toXml(rec2); - test:assertTrue(result2 == xmlVal2); + // xml xmlVal2 = xml `12`; + // Data4 rec2 = check parseAsType(xmlVal2); + // xml result2 = check toXml(rec2); + // test:assertTrue(result2 == xmlVal2); xml xmlVal3 = xml `123`; Data5 rec3 = check parseAsType(xmlVal3); xml result3 = check toXml(rec3); - test:assertTrue(result3 == xmlVal3); + test:assertEquals(result3, xmlVal3); } @test:Config { @@ -95,7 +95,7 @@ function testToXmlWithNameAnnotation() returns error? { xml xmlVal1 = xml `1234`; Rec2 rec1 = check parseAsType(xmlVal1); xml result = check toXml(rec1); - test:assertTrue(result == xmlVal1); + test:assertEquals(result, xmlVal1); } @test:Config { diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 19dc9a14..f88b0da8 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -219,45 +219,45 @@ public type JsonOptions record {| # # + jsonValue - The JSON source to be converted to XML # + options - The `xmldata:JsonOptions` record for JSON to XML conversion properties -# + return - XML representation of the given JSON if the JSON is successfully converted or else an `xmldata:Error`. -public isolated function fromJson(json jsonValue, JsonOptions options = {}) returns xml|Error { - string? rootTag = options.rootTag; - map allNamespaces = {}; - if !isSingleNode(jsonValue) { - addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); - return getElement(rootTag ?: "root", - check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, - check getAttributesMap(jsonValue, options, allNamespaces)); - } +// # + return - XML representation of the given JSON if the JSON is successfully converted or else an `xmldata:Error`. +// public isolated function fromJson(json jsonValue, JsonOptions options = {}) returns xml|Error { +// string? rootTag = options.rootTag; +// map allNamespaces = {}; +// if !isSingleNode(jsonValue) { +// addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); +// return getElement(rootTag ?: "root", +// check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, +// check getAttributesMap(jsonValue, options, allNamespaces)); +// } - map|error jMap = jsonValue.ensureType(); - if jMap is map { - if jMap.length() == 0 { - return xml ``; - } - - json value = jMap.toArray()[0]; - addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); - if value is json[] { - return getElement(rootTag ?: "root", - check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), - allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); - } - - string key = jMap.keys()[0]; - if key == options.textFieldName { - return xml:createText(value.toString()); - } - xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), - allNamespaces, options, - check getAttributesMap(value, options, allNamespaces)); - if rootTag is string { - return xml:createElement(rootTag, {}, output); - } - return output; - } - return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); -} +// map|error jMap = jsonValue.ensureType(); +// if jMap is map { +// if jMap.length() == 0 { +// return xml ``; +// } + +// json value = jMap.toArray()[0]; +// addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); +// if value is json[] { +// return getElement(rootTag ?: "root", +// check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), +// allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); +// } + +// string key = jMap.keys()[0]; +// if key == options.textFieldName { +// return xml:createText(value.toString()); +// } +// xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), +// allNamespaces, options, +// check getAttributesMap(value, options, allNamespaces)); +// if rootTag is string { +// return xml:createElement(rootTag, {}, output); +// } +// return output; +// } +// return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); +// } // isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, // string? 'key = ()) returns xml|Error { @@ -519,9 +519,9 @@ isolated function getElement(string name, xml children, map namespaces, map attributes = {}) returns xml|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; -isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, - string? 'key = ()) returns xml|Error - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; +// isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, +// string? 'key = ()) returns xml|Error +// = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; isolated function convertMapXml(map|map mapValue) returns xml = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; 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 6b150320..38775d0d 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 @@ -524,9 +524,8 @@ private static BMap addFields(BMap input, Type return recordValue; } - - private static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap input, - HashMap xsdSequencePriorityOrder) { + static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap input, + HashMap xsdSequencePriorityOrder) { if (!xsdSequencePriorityOrder.isEmpty()) { return xsdSequencePriorityOrder.entrySet().stream() .sorted(Comparator.comparingInt(Map.Entry::getValue)) @@ -933,7 +932,7 @@ private static boolean isNamespaceAnnotationKey(String key) { return key.startsWith(Constants.MODULE_NAME) && key.endsWith(Constants.NAMESPACE); } - private static boolean isNameAnnotationKey(String key) { + public static boolean isNameAnnotationKey(String key) { return key.startsWith(Constants.MODULE_NAME) && key.endsWith(Constants.NAME); } @@ -1115,8 +1114,12 @@ public static HashMap getXsdSequencePriorityOrder(RecordType fi return getXsdSequencePriorityOrder(fieldType, false); } - public static HashMap getXsdSequencePriorityOrder(RecordType fieldType, - boolean isSequenceElements) { + public static HashMap getXsdSequencePriorityOrder(Type type, boolean isSequenceElements) { + if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { + return new HashMap<>(); + } + + RecordType fieldType = (RecordType) type; HashMap elementPriorityOrder = new HashMap<>(); if (!isSequenceElements) { return elementPriorityOrder; @@ -1143,7 +1146,8 @@ public static HashMap getXsdSequencePriorityOrder(RecordType fi return elementPriorityOrder; } - public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordType fieldType) { + public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordType fieldType, + HashMap elementNamesMap) { ArrayList fieldNamesWithModelGroupAnnotations = new ArrayList<>(); BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { @@ -1156,7 +1160,37 @@ public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordTyp if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - fieldNamesWithModelGroupAnnotations.add(fieldName); + if (elementNamesMap.containsKey(fieldName)) { + fieldNamesWithModelGroupAnnotations.add(elementNamesMap.get(fieldName)); + } else { + fieldNamesWithModelGroupAnnotations.add(fieldName); + } + } + } + } + } + } + return fieldNamesWithModelGroupAnnotations; + } + + public static ArrayList getFieldNamesWithSequenceAnnotations(RecordType fieldType, + HashMap elementNamesMap) { + ArrayList fieldNamesWithModelGroupAnnotations = new ArrayList<>(); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + if (elementNamesMap.containsKey(fieldName)) { + fieldNamesWithModelGroupAnnotations.add(elementNamesMap.get(fieldName)); + } else { + fieldNamesWithModelGroupAnnotations.add(fieldName); + } } } } @@ -1165,6 +1199,35 @@ public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordTyp return fieldNamesWithModelGroupAnnotations; } + public static HashMap getElementNameMap(Type type) { + HashMap names = new HashMap<>(); + if (type instanceof RecordType recordType) { + BMap annotations = recordType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue + .getStringValue(Constants.VALUE)); + names.put(xmlElementName, fieldName); + } + } + } + } + } + return names; + } + + return names; + } + public static BMap getXmlElementModelGroupMap(BTypedesc typed) { Type type = TypeUtils.getReferredType(typed.getDescribingType()); if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 02752522..8d006f0d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -4,7 +4,12 @@ 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.ArrayType; +import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.utils.ValueUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -17,100 +22,74 @@ import org.ballerinalang.langlib.xml.CreateElement; import org.ballerinalang.langlib.xml.CreateText; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Optional; public class ToXmlUtils { private static final BString XMLNS_NAMESPACE_URI = StringUtils.fromString("http://www.w3.org/2000/xmlns/"); private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attribute_"); private static final BString XMLNS = StringUtils.fromString("xmlns"); -// public isolated function fromModifiedRecordToXml(json jsonValue, JsonOptions options = {}) returns xml|Error { -// string? rootTag = options.rootTag; -// map allNamespaces = {}; -// if !isSingleNode(jsonValue) { -// addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); -// return getElement(rootTag ?: "root", -// check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, -// check getAttributesMap(jsonValue, options, allNamespaces)); -// } -// -// map|error jMap = jsonValue.ensureType(); -// if jMap is map { -// if jMap.length() == 0 { -// return xml ``; -// } -// -// json value = jMap.toArray()[0]; -// addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); -// if value is json[] { -// return getElement(rootTag ?: "root", -// check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), -// allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); -// } -// -// string key = jMap.keys()[0]; -// if key == options.textFieldName { -// return xml:createText(value.toString()); -// } -// xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), -// allNamespaces, options, -// check getAttributesMap(value, options, allNamespaces)); -// if rootTag is string { -// return xml:createElement(rootTag, {}, output); -// } -// return output; -// } -// return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); -// } - public static BXml fromModifiedRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { + Type type = typed.getDescribingType(); + Type referredType = TypeUtils.getReferredType(type); Object rootTag = options.get(StringUtils.fromString("rootTag")); - BMap allNamespaces = (BMap) ((BMap) ValueCreator.createMapValue()); + BMap allNamespaces = getEmptyStringMap(); if (!isSingleNode(jsonValue)) { - addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, - (BMap) ((BMap) ValueCreator.createMapValue()))); + addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, getEmptyStringMap())); return getElement(rootTag == null ? StringUtils.fromString("root") : StringUtils.fromString(rootTag.toString()), - traverseNode(jsonValue, allNamespaces, - (BMap) ((BMap) ValueCreator.createMapValue()), options), - allNamespaces, options, getAttributesMap(jsonValue, options, allNamespaces, - (BMap) ((BMap) ValueCreator.createMapValue()))); + traverseNode(jsonValue, allNamespaces, getEmptyStringMap(), options, + null, TypeUtils.getReferredType(type)), + allNamespaces, options, getAttributesMap(jsonValue, options, allNamespaces, getEmptyStringMap())); } try { BMap jMap = (BMap) ValueUtils.convert(jsonValue, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); - if (jMap.size() == 0) { + if (jMap.isEmpty()) { return ValueCreator.createXmlValue(""); } + BString key = jMap.getKeys()[0]; + HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); + ArrayList modelGroupRelatedFieldNames = + getModelGroupRelatedFieldNames(referredType, elementNamesMap); + String localJsonKeyPart = key.getValue().contains(":") + ? key.getValue().substring(key.getValue().indexOf(":") + 1) : key.getValue(); + + String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); + boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); + Object value = ToArray.toArray(jMap).getValues()[0]; addNamespaces(allNamespaces, getNamespacesMap(value, options, - (BMap) ((BMap) ValueCreator.createMapValue()))); + getEmptyStringMap())); - if (value instanceof BArray arrayNode) { + if (value instanceof BArray) { return getElement(rootTag == null ? StringUtils.fromString("root") : StringUtils.fromString(rootTag.toString()), - traverseNode(value, allNamespaces, (BMap) ((BMap) - ValueCreator.createMapValue()), options, jMap.getKeys()[0]), - allNamespaces, options, getAttributesMap(value, options, allNamespaces, - (BMap) ((BMap) ValueCreator.createMapValue()))); + traverseNode(value, allNamespaces, getEmptyStringMap(), options, jMap.getKeys()[0], + getChildElementType(TypeUtils.getReferredType(type), key.getValue())), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, + getEmptyStringMap())); } - BString key = jMap.getKeys()[0]; if (key.equals(options.get(StringUtils.fromString("textFieldName")))) { return CreateText.createText(StringUtils.fromString(value.toString())); } BXml output = getElement(jMap.getKeys()[0], - traverseNode(value, allNamespaces, - (BMap) ((BMap) ValueCreator.createMapValue()), options), - allNamespaces, options, getAttributesMap(value, options, allNamespaces, - (BMap) ((BMap) ValueCreator.createMapValue()))); + traverseNode(value, allNamespaces, getEmptyStringMap(), + options, null, getChildElementType(TypeUtils.getReferredType(type), recordKey)), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); + if (!isNotContainXsdModelGroup) { + output = output.children(); + } if (rootTag != null) { return CreateElement.createElement(StringUtils.fromString(rootTag.toString()), - (BMap) ((BMap) ValueCreator.createMapValue()), output); + getEmptyStringMap(), output); } return output; } catch (BError e) { @@ -119,6 +98,10 @@ allNamespaces, options, getAttributesMap(value, options, allNamespaces, } } + private static BMap getEmptyStringMap() { + return (BMap) ((BMap) ValueCreator.createMapValue()); + } + public static BXml convertMapXml(BMap mapValue) { BXml xNode = ValueCreator.createXmlValue(""); for (Map.Entry entry : mapValue.entrySet()) { @@ -129,35 +112,48 @@ public static BXml convertMapXml(BMap mapValue) { if (i == null) { continue; } - xNode = Concat.concat(xNode, CreateElement.createElement(key, - (BMap) ((BMap) ValueCreator.createMapValue()), (BXml) i)); + xNode = Concat.concat(xNode, CreateElement.createElement(key, getEmptyStringMap(), (BXml) i)); } } else { - xNode = Concat.concat(xNode, CreateElement.createElement(key, - (BMap) ((BMap) ValueCreator.createMapValue()), (BXml) value)); + xNode = Concat.concat(xNode, CreateElement.createElement(key, getEmptyStringMap(), (BXml) value)); } } - return CreateElement.createElement(StringUtils.fromString("root"), - (BMap) ((BMap) ValueCreator.createMapValue()), xNode); + return CreateElement.createElement(StringUtils.fromString("root"), getEmptyStringMap(), xNode); } public static BXml traverseNode(Object jNode, BMap allNamespaces, - BMap parentNamespaces, BMap options) { - return traverseNode(jNode, allNamespaces, parentNamespaces, options, null); + BMap parentNamespaces, BMap options, Object keyObj, Type type) { + return traverseNode(jNode, allNamespaces, parentNamespaces, options, keyObj, type, false); } public static BXml traverseNode(Object jNode, BMap allNamespaces, - BMap parentNamespaces, BMap options, Object keyObj) { + BMap parentNamespaces, BMap options, + Object keyObj, Type type, boolean isParentSequence) { BMap namespacesOfElem; BXml xNode = ValueCreator.createXmlValue(""); String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); + Type referredType = TypeUtils.getReferredType(type); + HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); + ArrayList modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); + ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); + + BXml childElement; if (jNode instanceof BMap jMap) { BMap mapNode = (BMap) jMap; + + BString[] orderedRecordKeysIfXsdSequencePresent = DataUtils + .getOrderedRecordKeysIfXsdSequencePresent(mapNode, + DataUtils.getXsdSequencePriorityOrder(referredType, isParentSequence)); for (Map.Entry entry : mapNode.entrySet()) { BString k = entry.getKey(); Object value = entry.getValue(); String jsonKey = k.getValue().trim(); + String localJsonKeyPart = jsonKey.contains(":") + ? jsonKey.substring(jsonKey.indexOf(":") + 1) : jsonKey; + String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); + boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); + if (jsonKey.startsWith(attributePrefix)) { continue; } @@ -167,13 +163,21 @@ public static BXml traverseNode(Object jNode, BMap allNamespac } else { namespacesOfElem = getNamespacesMap(value, options, parentNamespaces); addNamespaces(allNamespaces, namespacesOfElem); - if (value instanceof BArray arrayNode) { - xNode = Concat.concat(xNode, traverseNode(value, allNamespaces, namespacesOfElem, options, k)); + + if (value instanceof BArray) { + childElement = traverseNode(value, + allNamespaces, namespacesOfElem, options, k, + getChildElementType(referredType, recordKey)); + xNode = Concat.concat(xNode, childElement); +// xNode = Concat.concat(xNode, isNotContainXsdModelGroup +// ? childElement : childElement.children()); } else { - xNode = Concat.concat(xNode, getElement(k, - traverseNode(value, allNamespaces, namespacesOfElem, options), - allNamespaces, options, - getAttributesMap(value, options, allNamespaces, parentNamespaces))); + childElement = getElement(k, + traverseNode(value, allNamespaces, namespacesOfElem, options, null, + getChildElementType(referredType, recordKey)), allNamespaces, options, + getAttributesMap(value, options, allNamespaces, parentNamespaces)); + xNode = Concat.concat(xNode, isNotContainXsdModelGroup + ? childElement : childElement.children()); } } } @@ -185,20 +189,27 @@ public static BXml traverseNode(Object jNode, BMap allNamespac String arrayEntryTagKey = ""; if (keyObj instanceof BString key) { arrayEntryTagKey = key.getValue(); - } else if (!options.get(Constants.ARRAY_ENTRY_TAG).toString().equals("")) { + } else if (!options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { arrayEntryTagKey = options.get(Constants.ARRAY_ENTRY_TAG).toString(); } + boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains( + arrayEntryTagKey.contains(":") ? + arrayEntryTagKey.substring(arrayEntryTagKey.indexOf(":") + 1) + : arrayEntryTagKey); + namespacesOfElem = getNamespacesMap(i, options, parentNamespaces); addNamespaces(allNamespaces, namespacesOfElem); - if (options.get(Constants.ARRAY_ENTRY_TAG).toString().equals("")) { - xNode = Concat.concat(xNode, getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, options, keyObj), - allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces))); + if (options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { + childElement = getElement(StringUtils.fromString(arrayEntryTagKey), + traverseNode(i, allNamespaces, namespacesOfElem, options, keyObj, referredType), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); + xNode = Concat.concat(xNode, isNotContainXsdModelGroup ? childElement : childElement.children()); } else { - xNode = Concat.concat(xNode, getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, options), - allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces))); + childElement = getElement(StringUtils.fromString(arrayEntryTagKey), + traverseNode(i, allNamespaces, namespacesOfElem, options, null, referredType), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); + xNode = Concat.concat(xNode, isNotContainXsdModelGroup ? childElement : childElement.children()); } } } else { @@ -207,6 +218,70 @@ public static BXml traverseNode(Object jNode, BMap allNamespac return xNode; } + private static ArrayList getModelGroupRelatedFieldNames(Type expType, + HashMap elementNamesMap) { + Type referedType = TypeUtils.getReferredType(expType); + if (referedType instanceof RecordType recordType) { + return DataUtils.getFieldNamesWithModelGroupAnnotations(recordType, elementNamesMap); + } + return new ArrayList<>(); + } + + private static ArrayList getSequenceFieldNames(Type expType, + HashMap elementNamesMap) { + Type referedType = TypeUtils.getReferredType(expType); + if (referedType instanceof RecordType recordType) { + return DataUtils.getFieldNamesWithSequenceAnnotations(recordType, elementNamesMap); + } + return new ArrayList<>(); + } + + + private static Type getChildElementType(Type type, String recordKey) { + + try { + if (type instanceof ArrayType arrayType) { + return TypeUtils.getReferredType(arrayType.getElementType()); + } + + if (type instanceof RecordType recordType) { + Map fields = recordType.getFields(); + if (fields.containsKey(recordKey)) { + return fields.get(recordKey).getFieldType(); + } + Optional fieldName = getFieldFromRecordNameAnnotation(fields, recordKey); + + if (!(fieldName.isEmpty()) && fields.containsKey(fieldName.get())) { + return fields.get(fieldName.get()).getFieldType(); + } else { + assert false; + throw DiagnosticLog.createXmlError("Invalid xml provided"); + } + } + + return type; + } catch (Exception e) { + int a = 1; + throw e; + } + } + + private static Optional getFieldFromRecordNameAnnotation(Map fields, String recordKey) { + for (Field field: fields.values()) { + Type fieldType = TypeUtils.getReferredType(field.getFieldType()); + if (fieldType instanceof RecordType recordType) { + for (Map.Entry annotation: recordType.getAnnotations().entrySet()) { + if (DataUtils.isNameAnnotationKey(annotation.getKey().getValue())) { + String name = ((BMap) annotation.getValue()).get(Constants.VALUE).toString(); + if (name.equals(recordKey)) { + return Optional.of(field.getFieldName()); + } + } + } + } + } + return Optional.empty(); + } public static boolean isSingleNode(Object node) { if (node instanceof BArray arrayNode) { @@ -230,6 +305,11 @@ public static boolean isSingleNode(Object node) { public static BXml getElement(BString name, BXml children, BMap namespaces, BMap options, BMap attributes) { + return getElement(name, children, namespaces, options, attributes, PredefinedTypes.TYPE_ANYDATA); + } + + public static BXml getElement(BString name, BXml children, BMap namespaces, + BMap options, BMap attributes, Type type) { String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); String userAttributePrefix = options.get(Constants.USER_ATTRIBUTE_PREFIX).toString(); BXml element; @@ -242,7 +322,7 @@ public static BXml getElement(BString name, BXml children, BMap Date: Fri, 15 Nov 2024 23:14:12 +0530 Subject: [PATCH 35/58] Add toxml implementation for sequence arrays --- ...sd_sequence_array_test_with_parse_type.bal | 10 ++- .../lib/data/xmldata/utils/ToXmlUtils.java | 62 ++++++++++++------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 1c4ef7c0..9deccd27 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XsdSequenceArrayWithXmlValue record {| @Sequence { minOccurs: 1, @@ -35,10 +38,15 @@ function testXsdSequenceArrayWithXmlValue() returns error? { test:assertTrue(v is error); test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); + xmlValue = xml `1311.114`; + v = parseAsType(xmlValue); + test:assertTrue(v is error); + test:assertTrue((v).message().includes("Element salary not found in seq_XsdSequenceArrayWithXmlValue"), (v).message()); + xmlValue = xml `131415.111.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); + test:assertTrue((v).message().includes("Element age occurs more than the max allowed times in seq_XsdSequenceArrayWithXmlValue"), (v).message()); } type XsdSequenceArrayWithXmlValue2 record {| diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 8d006f0d..2e8ff33c 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -55,11 +55,16 @@ public static BXml fromModifiedRecordToXml(Object jsonValue, BMap elementNamesMap = DataUtils.getElementNameMap(referredType); + ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); + boolean isSequenceField = sequenceFieldNames.contains(keyStr); + ArrayList modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); - String localJsonKeyPart = key.getValue().contains(":") - ? key.getValue().substring(key.getValue().indexOf(":") + 1) : key.getValue(); + String localJsonKeyPart = keyStr.contains(":") + ? keyStr.substring(keyStr.indexOf(":") + 1) : keyStr; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); @@ -72,7 +77,8 @@ public static BXml fromModifiedRecordToXml(Object jsonValue, BMap mapValue) { public static BXml traverseNode(Object jNode, BMap allNamespaces, BMap parentNamespaces, BMap options, Object keyObj, Type type) { - return traverseNode(jNode, allNamespaces, parentNamespaces, options, keyObj, type, false); + return traverseNode(jNode, allNamespaces, parentNamespaces, options, keyObj, type, false, false); + } + + public static BXml traverseNode(Object jNode, BMap allNamespaces, + BMap parentNamespaces, + BMap options, Object keyObj, Type type, boolean isParentSequencee) { + return traverseNode(jNode, allNamespaces, parentNamespaces, options, + keyObj, type, isParentSequencee, false); } public static BXml traverseNode(Object jNode, BMap allNamespaces, BMap parentNamespaces, BMap options, - Object keyObj, Type type, boolean isParentSequence) { + Object keyObj, Type type, boolean isParentSequence, boolean isParentSequenceArray) { BMap namespacesOfElem; BXml xNode = ValueCreator.createXmlValue(""); String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); @@ -145,14 +160,15 @@ public static BXml traverseNode(Object jNode, BMap allNamespac BString[] orderedRecordKeysIfXsdSequencePresent = DataUtils .getOrderedRecordKeysIfXsdSequencePresent(mapNode, DataUtils.getXsdSequencePriorityOrder(referredType, isParentSequence)); - for (Map.Entry entry : mapNode.entrySet()) { - BString k = entry.getKey(); - Object value = entry.getValue(); - String jsonKey = k.getValue().trim(); + for (BString k : orderedRecordKeysIfXsdSequencePresent) { + Object value = mapNode.get(k); + String keyStr = k.getValue(); + String jsonKey = keyStr.trim(); String localJsonKeyPart = jsonKey.contains(":") ? jsonKey.substring(jsonKey.indexOf(":") + 1) : jsonKey; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); + boolean isSequenceField = sequenceFieldNames.contains(recordKey); if (jsonKey.startsWith(attributePrefix)) { continue; @@ -167,16 +183,15 @@ public static BXml traverseNode(Object jNode, BMap allNamespac if (value instanceof BArray) { childElement = traverseNode(value, allNamespaces, namespacesOfElem, options, k, - getChildElementType(referredType, recordKey)); + getChildElementType(referredType, recordKey), isSequenceField, isSequenceField); xNode = Concat.concat(xNode, childElement); -// xNode = Concat.concat(xNode, isNotContainXsdModelGroup -// ? childElement : childElement.children()); } else { childElement = getElement(k, traverseNode(value, allNamespaces, namespacesOfElem, options, null, - getChildElementType(referredType, recordKey)), allNamespaces, options, + getChildElementType(referredType, recordKey), + isSequenceField, isSequenceField), allNamespaces, options, getAttributesMap(value, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isNotContainXsdModelGroup + xNode = Concat.concat(xNode, isNotContainXsdModelGroup || isParentSequenceArray ? childElement : childElement.children()); } } @@ -193,23 +208,22 @@ public static BXml traverseNode(Object jNode, BMap allNamespac arrayEntryTagKey = options.get(Constants.ARRAY_ENTRY_TAG).toString(); } - boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains( - arrayEntryTagKey.contains(":") ? - arrayEntryTagKey.substring(arrayEntryTagKey.indexOf(":") + 1) - : arrayEntryTagKey); - namespacesOfElem = getNamespacesMap(i, options, parentNamespaces); addNamespaces(allNamespaces, namespacesOfElem); if (options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { childElement = getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, options, keyObj, referredType), + traverseNode(i, allNamespaces, namespacesOfElem, + options, keyObj, getChildElementType(referredType, null), + isParentSequence, isParentSequenceArray), allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isNotContainXsdModelGroup ? childElement : childElement.children()); + xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } else { childElement = getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, options, null, referredType), + traverseNode(i, allNamespaces, namespacesOfElem, + options, null, getChildElementType(referredType, null), + isParentSequence, isParentSequenceArray), allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isNotContainXsdModelGroup ? childElement : childElement.children()); + xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } } } else { From e600ee7a4f72c1502546d91f2d8a5fd6a6a3ff93 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 16 Nov 2024 22:24:17 +0530 Subject: [PATCH 36/58] Add proper error messages for xsd validation errors --- ballerina/tests/xsd_choice_array_test.bal | 16 +- .../xsd_choice_array_test_with_parse_type.bal | 16 +- ballerina/tests/xsd_choice_test.bal | 54 +- ...sd_choice_test_with_element_annotation.bal | 30 +- ...ith_element_annotation_with_parse_type.bal | 30 +- .../tests/xsd_choice_test_with_parse_type.bal | 54 +- .../xsd_choice_with_name_annotations.bal | 30 +- ..._with_name_annotations_with_parse_type.bal | 30 +- ballerina/tests/xsd_element_test.bal | 28 +- .../xsd_element_test_with_parse_type.bal | 28 +- ballerina/tests/xsd_sequence_array_test.bal | 25 +- ...sd_sequence_array_test_with_parse_type.bal | 29 +- ..._sequence_test_with_element_annotation.bal | 40 +- ...ith_element_annotation_with_parse_type.bal | 41 +- ...xsd_sequence_test_with_name_annotation.bal | 31 +- ...t_with_name_annotation_with_parse_type.bal | 31 +- ...equence_test_with_namespace_annotation.bal | 31 +- ...h_namespace_annotation_with_parse_type.bal | 31 +- ballerina/tests/xsd_sequence_tests.bal | 68 +-- .../xsd_sequence_tests_with_parse_type.bal | 79 +-- ballerina/xml_api.bal | 566 +++++++++--------- .../lib/data/xmldata/utils/Constants.java | 3 + .../lib/data/xmldata/utils/DataUtils.java | 1 - .../xmldata/utils/DiagnosticErrorCode.java | 12 +- .../lib/data/xmldata/utils/ToXmlUtils.java | 238 +++----- .../lib/data/xmldata/utils/XsdUtils.java | 10 +- .../lib/data/xmldata/xml/XSDValidator.java | 20 +- .../lib/data/xmldata/xml/XmlParser.java | 6 +- .../lib/data/xmldata/xml/XmlTraversal.java | 3 +- .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 9 +- .../lib/data/xmldata/xml/xsd/ElementInfo.java | 6 +- .../data/xmldata/xml/xsd/SequenceInfo.java | 27 +- native/src/main/resources/error.properties | 27 + 33 files changed, 791 insertions(+), 859 deletions(-) diff --git a/ballerina/tests/xsd_choice_array_test.bal b/ballerina/tests/xsd_choice_array_test.bal index 2092bb13..78a424d8 100644 --- a/ballerina/tests/xsd_choice_array_test.bal +++ b/ballerina/tests/xsd_choice_array_test.bal @@ -33,7 +33,7 @@ function testXsdChoiceArray() returns error? { xmlStr = string `1311.11414.11515.1`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArray' occurs more than the max allowed times"); } type XsdChoiceArray2 record {| @@ -80,12 +80,12 @@ function testXsdChoiceArray2() returns error? { xmlStr = string `13131311.11415`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray2_2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArray2_2' occurs more than the max allowed times"); xmlStr = string `13131313`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArray2' occurs more than the max allowed times"); xmlStr = string `1313`; v = parseString(xmlStr); @@ -94,7 +94,7 @@ function testXsdChoiceArray2() returns error? { xmlStr = string `1313`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray2' not present in XML"), (v).message()); + test:assertEquals((v).message(), "required field 'choice_XsdChoiceArray2' not present in XML"); } type XSDChoiceArrayRecord13 record { @@ -202,12 +202,12 @@ function testXSDChoiceArrayRecord4() returns error? { xmlStr = string `22123233123`; v2 = parseString(xmlStr); test:assertTrue(v2 is error); - test:assertTrue((v2).message().includes("choice_XSDChoiceArrayRecord13_2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceArrayRecord13_2' occurs less than the min required times"); xmlStr = string `221232331123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is error); - test:assertTrue((v2).message().includes("value2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'value2' occurs less than the min required times"); } type XsdChoiceArray5 record {| @@ -239,12 +239,12 @@ function testXsdChoiceArray5() returns error? { xmlStr = string `1311.11414.11515.11515.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray5 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XsdChoiceArray5' occurs more than the max allowed times"); xmlStr = string `13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XsdChoiceArray5 Element occurs less than the min required times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XsdChoiceArray5' occurs less than the min required times"); } type XSDChoiceArrayRecord6 record { diff --git a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal index 46ed9c0c..f9be6bee 100644 --- a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal @@ -33,7 +33,7 @@ function testXsdChoiceArrayWithXmlValue() returns error? { xmlValue = xml `1311.11414.11515.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue' occurs more than the max allowed times"); } type XsdChoiceArrayWithXmlValue2 record {| @@ -80,12 +80,12 @@ function testXsdChoiceArrayWithXmlValue2() returns error? { xmlValue = xml `13131311.11415`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2_2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue2_2' occurs more than the max allowed times"); xmlValue = xml `13131313`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue2' occurs more than the max allowed times"); xmlValue = xml `1313`; v = parseAsType(xmlValue); @@ -94,7 +94,7 @@ function testXsdChoiceArrayWithXmlValue2() returns error? { xmlValue = xml `1313`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue2' not present in XML"), (v).message()); + test:assertEquals((v).message(), "required field 'choice_XsdChoiceArrayWithXmlValue2' not present in XML"); } type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13 record { @@ -136,12 +136,12 @@ function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord4() retur xmlValue = xml `22123233123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is error); - test:assertTrue((v2).message().includes("choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2' occurs less than the min required times"); xmlValue = xml `221232331123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is error); - test:assertTrue((v2).message().includes("value2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'value2' occurs less than the min required times"); } type XsdChoiceArrayWithXmlValue5 record {| @@ -173,12 +173,12 @@ function testXsdChoiceArrayWithXmlValue5() returns error? { xmlValue = xml `1311.11414.11515.11515.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue5 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue5' occurs more than the max allowed times"); xmlValue = xml `13`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XsdChoiceArrayWithXmlValue5 Element occurs less than the min required times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue5' occurs less than the min required times"); } type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6 record { diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 27681ba2..43555270 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -29,17 +29,17 @@ function testXsdChoice() returns error? { xmlStr = string `1011.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecord' occurs more than the max allowed times"); xmlStr = string `11.111.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecord' occurs more than the max allowed times"); xmlStr = string ``; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecord Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecord' occurs less than the min required times"); } type XSDChoiceRecordP2 record {| @@ -93,12 +93,12 @@ function testXsdChoiceP1() returns error? { xmlStr = string `1011.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceP1Record Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceP1Record' occurs more than the max allowed times"); xmlStr = string `11.111.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceP1Record Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceP1Record' occurs more than the max allowed times"); xmlStr = string ``; v = parseString(xmlStr); @@ -122,12 +122,12 @@ function testXsdChoiceP2() returns error? { xmlStr = string `10101011.1ABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecordP2' occurs more than the max allowed times"); xmlStr = string `10ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecordP2' occurs more than the max allowed times"); } type XSDChoiceRecord2 record {| @@ -162,12 +162,12 @@ function testXsdChoice2() returns error? { xmlStr = string `11.1103`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecord2' occurs more than the max allowed times"); xmlStr = string `10311.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceRecord2 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceRecord2' occurs more than the max allowed times"); } type XSDChoiceRecord3 record {| @@ -200,12 +200,12 @@ function testXsdChoice3() returns error? { xmlStr = string `10311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord3' occurs more than the max allowed times"); xmlStr = string `31011.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord3 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord3' occurs more than the max allowed times"); } type XSDChoiceRecord4 record {| @@ -237,12 +237,12 @@ function testXsdChoice4() returns error? { xmlStr = string `10311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord4' occurs more than the max allowed times"); xmlStr = string `31011.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord4 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord4' occurs more than the max allowed times"); } type XSDChoiceRecord5 record {| @@ -277,12 +277,12 @@ function testXsdChoice5() returns error? { xmlStr = string `103311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord5 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord5' occurs more than the max allowed times"); xmlStr = string `33`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord5' not present in XML"), (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceRecord5' not present in XML"); } type XSDChoiceRecord6 record {| @@ -323,17 +323,17 @@ function testXsdChoice6() returns error? { xmlStr = string `SD33`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord6_1' not present in XML"), msg = (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceRecord6_1' not present in XML"); xmlStr = string `33SD`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceRecord6_1' not present in XML"), msg = (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceRecord6_1' not present in XML"); xmlStr = string `SDsuccess33`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord6_2 Element occurs more than the max allowed times"), msg = (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord6_2' occurs more than the max allowed times"); } type XSDChoiceRecord7 record {| @@ -429,22 +429,22 @@ function testXsdChoice8() returns error? { xmlStr = string `SDAB10SuccessFail11.12`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord8_2' occurs more than the max allowed times"); xmlStr = string `10SuccessFail11.12`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord8_1' occurs more than the max allowed times"); xmlStr = string `102`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_2 Element occurs less than the min required times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord8_2' occurs less than the min required times"); xmlStr = string `SuccessFail2`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord8_1 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord8_1' occurs less than the min required times"); } type XSDChoiceRecord9 record { @@ -507,12 +507,12 @@ function testXsdChoice9() returns error? { xmlStr = string `11`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord9_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord9_1' occurs more than the max allowed times"); xmlStr = string `11`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value1' occurs more than the max allowed times"); } type XSDChoiceRecord10 record { @@ -616,15 +616,15 @@ function testXsdChoice10() returns error? { xmlStr = string `112`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord10_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord10_1' occurs more than the max allowed times"); xmlStr = string `122`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceRecord10_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceRecord10_2' occurs more than the max allowed times"); xmlStr = string `112`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value2' occurs more than the max allowed times"); } diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation.bal b/ballerina/tests/xsd_choice_test_with_element_annotation.bal index 64c695ef..5b9a3da4 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation.bal @@ -53,7 +53,7 @@ function testXsdChoiceWithElementAnnotation() returns error? { xmlStr = string `ABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABAB`; v = parseString(xmlStr); @@ -62,12 +62,12 @@ function testXsdChoiceWithElementAnnotation() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABC`; v = parseString(xmlStr); @@ -80,22 +80,22 @@ function testXsdChoiceWithElementAnnotation() returns error? { xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA1' occurs more than the max allowed times"); xmlStr = string `ABCABCABCABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'seq_EA1' occurs more than the max allowed times"); } type XsdChoiceWithElementAnnotation2 record { @@ -145,12 +145,12 @@ function testXsdChoiceWithElementAnnotation2() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCABCD`; v = parseString(xmlStr); @@ -171,7 +171,7 @@ function testXsdChoiceWithElementAnnotation2() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -180,27 +180,27 @@ function testXsdChoiceWithElementAnnotation2() returns error? { xmlStr = string `ABCABCCD`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdChoiceWithElementAnnotation3 record { diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal index 0a1df5ba..07f3ef02 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal @@ -32,7 +32,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABAB`; v = parseAsType(xmlValue); @@ -41,12 +41,12 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); @@ -59,22 +59,22 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA1' occurs more than the max allowed times"); xmlValue = xml `ABCABCABCABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'seq_EA1' occurs more than the max allowed times"); } type XsdChoiceWithElementAnnotationWithXmlValue2 record { @@ -93,12 +93,12 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); @@ -119,7 +119,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -128,27 +128,27 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCCD`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdChoiceWithElementAnnotationWithXmlValue3 record { diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index 41e8697a..e5aeabc3 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -29,17 +29,17 @@ function testXsdChoiceWithXmlValue() returns error? { xmlValue = xml `1011.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); xmlValue = xml `11.111.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); xmlValue = xml ``; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times"); } type XSDChoiceWithXmlValueRecordP2 record {| @@ -93,12 +93,12 @@ function testXsdChoiceWithXmlValueP1() returns error? { xmlValue = xml `1011.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueP1Record Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueP1Record' occurs more than the max allowed times"); xmlValue = xml `11.111.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueP1Record Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueP1Record' occurs more than the max allowed times"); xmlValue = xml ``; v = parseAsType(xmlValue); @@ -122,12 +122,12 @@ function testXsdChoiceWithXmlValueP2() returns error? { xmlValue = xml `10101011.1ABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecordP2' occurs more than the max allowed times"); xmlValue = xml `10ABC11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecordP2 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecordP2' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord2 record {| @@ -162,12 +162,12 @@ function testXsdChoiceWithXmlValue2() returns error? { xmlValue = xml `11.1103`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord2 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); xmlValue = xml `10311.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("choice_XSDChoiceWithXmlValueRecord2 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord3 record {| @@ -200,12 +200,12 @@ function testXsdChoiceWithXmlValue3() returns error? { xmlValue = xml `10311.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord3 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord3' occurs more than the max allowed times"); xmlValue = xml `31011.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord3 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord3' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord4 record {| @@ -237,12 +237,12 @@ function testXsdChoiceWithXmlValue4() returns error? { xmlValue = xml `10311.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord4 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord4' occurs more than the max allowed times"); xmlValue = xml `31011.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord4 Element occurs more than the max allowed times")); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord4' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord5 record {| @@ -277,12 +277,12 @@ function testXsdChoiceWithXmlValue5() returns error? { xmlValue = xml `103311.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord5 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord5' occurs more than the max allowed times"); xmlValue = xml `33`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord5' not present in XML"), (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceWithXmlValueRecord5' not present in XML"); } type XSDChoiceWithXmlValueRecord6 record {| @@ -323,17 +323,17 @@ function testXsdChoiceWithXmlValue6() returns error? { xmlValue = xml `SD33`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"), msg = (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"); xmlValue = xml `33SD`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"), msg = (v2).message()); + test:assertEquals((v2).message(), "required field 'choice_XSDChoiceWithXmlValueRecord6_1' not present in XML"); xmlValue = xml `SDsuccess33`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord6_2 Element occurs more than the max allowed times"), msg = (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord6_2' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord7 record {| @@ -424,22 +424,22 @@ function testXsdChoiceWithXmlValue8() returns error? { xmlValue = xml `SDAB10SuccessFail11.12`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord8_2' occurs more than the max allowed times"); xmlValue = xml `10SuccessFail11.12`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord8_1' occurs more than the max allowed times"); xmlValue = xml `102`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord8_2' occurs less than the min required times"); xmlValue = xml `SuccessFail2`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord8_1 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord8_1' occurs less than the min required times"); } type XSDChoiceWithXmlValueRecord9 record { @@ -472,12 +472,12 @@ function testXsdChoiceWithXmlValue9() returns error? { xmlValue = xml `11`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord9_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord9_1' occurs more than the max allowed times"); xmlValue = xml `11`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value1' occurs more than the max allowed times"); } type XSDChoiceWithXmlValueRecord10 record { @@ -515,15 +515,15 @@ function testXsdChoiceWithXmlValue10() returns error? { xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord10_1 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times"); xmlValue = xml `122`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("choice_XSDChoiceWithXmlValueRecord10_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_2' occurs more than the max allowed times"); xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value2' occurs more than the max allowed times"); } diff --git a/ballerina/tests/xsd_choice_with_name_annotations.bal b/ballerina/tests/xsd_choice_with_name_annotations.bal index 2209015f..b3e4c953 100644 --- a/ballerina/tests/xsd_choice_with_name_annotations.bal +++ b/ballerina/tests/xsd_choice_with_name_annotations.bal @@ -62,7 +62,7 @@ function testXsdChoiceWithNameAnnotation() returns error? { xmlStr = string `ABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlStr = string `ABAB`; v = parseString(xmlStr); @@ -71,12 +71,12 @@ function testXsdChoiceWithNameAnnotation() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABC`; v = parseString(xmlStr); @@ -89,22 +89,22 @@ function testXsdChoiceWithNameAnnotation() returns error? { xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A1' occurs more than the max allowed times"); xmlStr = string `ABCABCABCABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'seq_EA1' occurs more than the max allowed times"); } type XsdChoiceWithNameAnnotation2 record { @@ -157,12 +157,12 @@ function testXsdChoiceWithNameAnnotation2() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABCABCD`; v = parseString(xmlStr); @@ -183,7 +183,7 @@ function testXsdChoiceWithNameAnnotation2() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -192,25 +192,25 @@ function testXsdChoiceWithNameAnnotation2() returns error? { xmlStr = string `ABCABCCD`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal index ebd7a840..0246583c 100644 --- a/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal @@ -32,7 +32,7 @@ function testXsdChoiceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlValue = xml `ABAB`; v = parseAsType(xmlValue); @@ -41,12 +41,12 @@ function testXsdChoiceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); @@ -59,22 +59,22 @@ function testXsdChoiceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A1' occurs more than the max allowed times"); xmlValue = xml `ABCABCABCABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_EA1 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'seq_EA1' occurs more than the max allowed times"); } type XsdChoiceWithNameAnnotationWithXmlValue2 record { @@ -93,12 +93,12 @@ function testXsdChoiceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); @@ -119,7 +119,7 @@ function testXsdChoiceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -128,25 +128,25 @@ function testXsdChoiceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCCD`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_element_test.bal b/ballerina/tests/xsd_element_test.bal index b4b039a0..844f017d 100644 --- a/ballerina/tests/xsd_element_test.bal +++ b/ballerina/tests/xsd_element_test.bal @@ -48,7 +48,7 @@ function testXsdElement2() returns error? { xmlStr = "John"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs less than the min required times")); + test:assertEquals((rec).message(), "'age' occurs less than the min required times"); xmlStr = "25"; rec = parseString(xmlStr); @@ -57,22 +57,22 @@ function testXsdElement2() returns error? { xmlStr = "1112131415"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "11Abc12131415"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "AbcAbcAbcAbcAbc1112131415"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "11AbcAbc12Abc13Abc1415"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); } type ElementRecord3 record { @@ -115,7 +115,7 @@ function testXsdElement3() returns error? { xmlStr = "John123"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs less than the min required times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs less than the min required times"); xmlStr = "12353"; rec = parseString(xmlStr); @@ -124,22 +124,22 @@ function testXsdElement3() returns error? { xmlStr = "1211131314153"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "1211Abc131314153"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "12AbcAbcAbcAbcAbc11131314153"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = "1211AbcAbc13Abc13Abc14153"; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); } type ElementRecord4 record { @@ -187,20 +187,20 @@ function testXsdElement4() returns error? { xmlStr = string `JohnJaneJimAnnaDoeSmithBrown1234202530`; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("name Element occurs less than the min required times")); + test:assertEquals((rec).message(), "'name' occurs less than the min required times"); xmlStr = string `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("name Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'name' occurs more than the max allowed times"); xmlStr = string `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("lastName Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'lastName' occurs more than the max allowed times"); xmlStr = string `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; rec = parseString(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("firstName Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'firstName' occurs more than the max allowed times"); } \ No newline at end of file diff --git a/ballerina/tests/xsd_element_test_with_parse_type.bal b/ballerina/tests/xsd_element_test_with_parse_type.bal index 7de7677a..27db56f9 100644 --- a/ballerina/tests/xsd_element_test_with_parse_type.bal +++ b/ballerina/tests/xsd_element_test_with_parse_type.bal @@ -48,7 +48,7 @@ function testXsdElementWithXmlValue2() returns error? { xmlStr = xml `John`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs less than the min required times")); + test:assertEquals((rec).message(), "'age' occurs less than the min required times"); xmlStr = xml `25`; rec = parseAsType(xmlStr); @@ -57,22 +57,22 @@ function testXsdElementWithXmlValue2() returns error? { xmlStr = xml `1112131415`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `11Abc12131415`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `AbcAbcAbcAbcAbc1112131415`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `11AbcAbc12Abc13Abc1415`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); } type ElementRecordWithXmlValue3 record { @@ -115,7 +115,7 @@ function testXsdElementWithXmlValue3() returns error? { xmlStr = xml `John123`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs less than the min required times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs less than the min required times"); xmlStr = xml `12353`; rec = parseAsType(xmlStr); @@ -124,22 +124,22 @@ function testXsdElementWithXmlValue3() returns error? { xmlStr = xml `1211131314153`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `1211Abc131314153`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `12AbcAbcAbcAbcAbc11131314153`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); xmlStr = xml `1211AbcAbc13Abc13Abc14153`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("age Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); } type ElementRecordWithXmlValue4 record { @@ -187,20 +187,20 @@ function testXsdElementWithXmlValue4() returns error? { xmlStr = xml `JohnJaneJimAnnaDoeSmithBrown1234202530`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("name Element occurs less than the min required times")); + test:assertEquals((rec).message(), "'name' occurs less than the min required times"); xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("name Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'name' occurs more than the max allowed times"); xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("lastName Element occurs more than the max allowed times")); + test:assertEquals((rec).message(), "'lastName' occurs more than the max allowed times"); xmlStr = xml `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; rec = parseAsType(xmlStr); test:assertTrue(rec is error); - test:assertTrue((rec).message().includes("firstName Element occurs more than the max allowed times"), (rec).message()); + test:assertEquals((rec).message(), "'firstName' occurs more than the max allowed times"); } \ No newline at end of file diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 2a65f8d7..09c9835e 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -32,7 +32,7 @@ function testXsdSequenceArray() returns error? { xmlStr = string `1311.11414.11515.1`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArray Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArray' occurs more than the max allowed times"); } type XsdSequenceArray2 record {| @@ -85,12 +85,12 @@ function testXsdSequenceArray2() returns error? { xmlStr = string `1311.11311.11415.11311.11415.1`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArray2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArray2' occurs more than the max allowed times"); xmlStr = string `1311.11415.11311.11311.11415.1`; v = parseString(xmlStr); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArray2_2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArray2_2' occurs more than the max allowed times"); } type XSDSequenceArrayRecord13 record { @@ -227,37 +227,37 @@ function testXSDSequenceArrayRecord4() returns error? { xmlStr = string `123123123123123123123123123123123123123123123123123123123123123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("seq_XSDSequenceArrayRecord13_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'seq_XSDSequenceArrayRecord13_2' occurs more than the max allowed times"); xmlStr = string `123123123123123123123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value3 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value3' occurs more than the max allowed times"); xmlStr = string `123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceArrayRecord13_2'"); xmlStr = string `123123123123121233123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); xmlStr = string `123123123123123132`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); xmlStr = string `132123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); xmlStr = string `123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field5 is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'field5' is not in the correct order in 'seq_XSDSequenceArrayRecord13_2'"); } type XsdSequenceArray5 record {| @@ -296,12 +296,12 @@ function testXsdSequenceArray5() returns error? { xmlStr = string `1311.11414.11515.11515.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_XsdSequenceArray5 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'seq_XsdSequenceArray5' occurs more than the max allowed times"); xmlStr = string `1311.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_XsdSequenceArray5 Element occurs less than the min required times"), msg = (v).message()); + test:assertEquals((v).message(), "'seq_XsdSequenceArray5' occurs less than the min required times"); } type XSDSequenceArrayRecord6 record { @@ -378,7 +378,6 @@ type Seq2_Array_6 record { @test:Config {groups: ["xsd", "xsd_sequence"]} function testXSDSequenceArrayRecord6() returns error? { - // TODO: Refactor sequence elements into a array string xmlStr = string `1111111111111111`; XSDSequenceArrayRecord6|Error v2 = parseString(xmlStr); test:assertEquals(v2, {"seq_XSDSequenceArrayRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 9deccd27..454abe6f 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -36,17 +36,17 @@ function testXsdSequenceArrayWithXmlValue() returns error? { xmlValue = xml `1311.11414.11515.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue' occurs more than the max allowed times"); xmlValue = xml `1311.114`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("Element salary not found in seq_XsdSequenceArrayWithXmlValue"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XsdSequenceArrayWithXmlValue'"); xmlValue = xml `131415.111.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("Element age occurs more than the max allowed times in seq_XsdSequenceArrayWithXmlValue"), (v).message()); + test:assertEquals((v).message(), "'age' occurs more than the max allowed times in 'seq_XsdSequenceArrayWithXmlValue'"); } type XsdSequenceArrayWithXmlValue2 record {| @@ -99,12 +99,12 @@ function testXsdSequenceArrayWithXmlValue2() returns error? { xmlValue = xml `1311.11311.11415.11311.11415.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue2' occurs more than the max allowed times"); xmlValue = xml `1311.11415.11311.11311.11415.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue2_2 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue2_2' occurs more than the max allowed times"); } type XSDSequenceArrayWithXmlValueRecord13 record { @@ -160,37 +160,37 @@ function testXSDSequenceArrayWithXmlValueRecord4() returns error? { xmlValue = xml `123123123123123123123123123123123123123123123123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("seq_XSDSequenceArrayWithXmlValueRecord13_2 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'seq_XSDSequenceArrayWithXmlValueRecord13_2' occurs more than the max allowed times"); xmlValue = xml `123123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("value3 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'value3' occurs more than the max allowed times"); xmlValue = xml `123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceArrayWithXmlValueRecord13_2'"); xmlValue = xml `123123123123121233123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); xmlValue = xml `123123123123123132`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); xmlValue = xml `132123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); xmlValue = xml `123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field5 is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'field5' is not in the correct order in 'seq_XSDSequenceArrayWithXmlValueRecord13_2'"); } type XsdSequenceArrayWithXmlValue5 record {| @@ -229,12 +229,12 @@ function testXsdSequenceArrayWithXmlValue5() returns error? { xmlValue = xml `1311.11414.11515.11515.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue5 Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue5' occurs more than the max allowed times"); xmlValue = xml `1311.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("seq_XsdSequenceArrayWithXmlValue5 Element occurs less than the min required times"), msg = (v).message()); + test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue5' occurs less than the min required times"); } type XSDSequenceArrayWithXmlValueRecord6 record { @@ -269,7 +269,6 @@ type Seq_XSDSequenceArrayWithXmlValueRecord6_2 record { @test:Config {groups: ["xsd", "xsd_sequence"]} function testXSDSequenceArrayWithXmlValueRecord6() returns error? { - // TODO: Refactor sequence elements into a array xml xmlValue = xml `1111111111111111`; XSDSequenceArrayWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {"seq_XSDSequenceArrayWithXmlValueRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayWithXmlValueRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index fe4ac221..5187f3a4 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -47,7 +47,7 @@ function testXsdSequenceWithElementAnnotation() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1'"); xmlStr = string `ABCABCABABAB`; v = parseString(xmlStr); @@ -68,7 +68,7 @@ function testXsdSequenceWithElementAnnotation() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -77,27 +77,27 @@ function testXsdSequenceWithElementAnnotation() returns error? { xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithElementAnnotation2 record { @@ -150,12 +150,12 @@ function testXsdSequenceWithElementAnnotation2() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCABCD`; v = parseString(xmlStr); @@ -176,7 +176,7 @@ function testXsdSequenceWithElementAnnotation2() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -185,27 +185,27 @@ function testXsdSequenceWithElementAnnotation2() returns error? { xmlStr = string `ABCABCCD`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithElementAnnotation3 record { @@ -381,25 +381,25 @@ function testXsdSequenceWithElementAnnotation3() returns error? { xmlStr = string `33123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("seq_XsdSequenceWithElementAnnotation3_2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'seq_XsdSequenceWithElementAnnotation3_2' occurs less than the min required times"); xmlStr = string `2312312323123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field3 not found in seq_XsdSequenceWithElementAnnotation3_1"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field3' is not found in 'seq_XsdSequenceWithElementAnnotation3_1'"); xmlStr = string `231231231232312312`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'i' not present in XML"), (v2).message()); + test:assertEquals((v2).message(), "required field 'i' not present in XML"); xmlStr = string `123123123123123123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("field5 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'field5' occurs more than the max allowed times"); xmlStr = string `1231222223123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("b Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'b' occurs more than the max allowed times"); } diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal index 08357acd..ebf113fd 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -1,6 +1,5 @@ import ballerina/test; -// TODO: Add tests with attributes type XsdSequenceWithElementAnnotationWithXmlValue record { @Sequence { minOccurs: 0, @@ -47,7 +46,7 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_Xml_Value"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_Xml_Value'"); xmlValue = xml `ABCABCABABAB`; v = parseAsType(xmlValue); @@ -68,7 +67,7 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -77,27 +76,27 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithElementAnnotationWithXmlValue2 record { @@ -150,12 +149,12 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); @@ -176,7 +175,7 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -185,27 +184,27 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCCD`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithElementAnnotationWithXmlValue3 record { @@ -292,25 +291,25 @@ function testXsdSequenceWithElementAnnotationWithXmlValue3() returns error? { xmlValue = xml `33123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 Element occurs less than the min required times"), (v2).message()); + test:assertEquals((v2).message(), "'seq_XsdSequenceWithElementAnnotationWithXmlValue3_2' occurs less than the min required times"); xmlValue = xml `2312312323123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field3 not found in seq_XsdSequenceWithElementAnnotationWithXmlValue3_1"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field3' is not found in 'seq_XsdSequenceWithElementAnnotationWithXmlValue3_1'"); xmlValue = xml `231231231232312312`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("required field 'i' not present in XML"), (v2).message()); + test:assertEquals((v2).message(), "required field 'i' not present in XML"); xmlValue = xml `123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("field5 Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'field5' occurs more than the max allowed times"); xmlValue = xml `1231222223123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("b Element occurs more than the max allowed times"), (v2).message()); + test:assertEquals((v2).message(), "'b' occurs more than the max allowed times"); } diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal index 21647af1..c6856a84 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal @@ -1,6 +1,5 @@ import ballerina/test; -// TODO: Add tests with attributes type XsdSequenceWithNameAnnotation record { @Sequence { minOccurs: 0, @@ -50,7 +49,7 @@ function testXsdSequenceWithNameAnnotation() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NameAnnotation"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_NameAnnotation'"); xmlStr = string `ABCABCABABAB`; v = parseString(xmlStr); @@ -71,7 +70,7 @@ function testXsdSequenceWithNameAnnotation() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -80,27 +79,27 @@ function testXsdSequenceWithNameAnnotation() returns error? { xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } type XsdSequenceWithNameAnnotation2 record { @@ -157,12 +156,12 @@ function testXsdSequenceWithNameAnnotation2() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCABCABCD`; v = parseString(xmlStr); @@ -183,7 +182,7 @@ function testXsdSequenceWithNameAnnotation2() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -192,25 +191,25 @@ function testXsdSequenceWithNameAnnotation2() returns error? { xmlStr = string `ABCABCCD`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal index 0904ee45..02abb02c 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal @@ -1,6 +1,5 @@ import ballerina/test; -// TODO: Add tests with attributes type XsdSequenceWithNameAnnotationWithXmlValue record { @Sequence { minOccurs: 0, @@ -50,7 +49,7 @@ function testXsdSequenceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NameAnnotationWithXmlValue"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_NameAnnotationWithXmlValue'"); xmlValue = xml `ABCABCABABAB`; v = parseAsType(xmlValue); @@ -71,7 +70,7 @@ function testXsdSequenceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -80,27 +79,27 @@ function testXsdSequenceWithNameAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } type XsdSequenceWithNameAnnotationWithXmlValue2 record { @@ -157,12 +156,12 @@ function testXsdSequenceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); @@ -183,7 +182,7 @@ function testXsdSequenceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'A3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -192,25 +191,25 @@ function testXsdSequenceWithNameAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCCD`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("A3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'A3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal index a8e7671c..5d5f5605 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -1,6 +1,5 @@ import ballerina/test; -// TODO: Add tests with attributes type XsdSequenceWithNamespaceAnnotation record { @Sequence { minOccurs: 0, @@ -59,7 +58,7 @@ function testXsdSequenceWithNamespaceAnnotation() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NamespaceAnnotation"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_NamespaceAnnotation'"); xmlStr = string `ABCABCABABAB`; v = parseString(xmlStr); @@ -80,7 +79,7 @@ function testXsdSequenceWithNamespaceAnnotation() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -89,27 +88,27 @@ function testXsdSequenceWithNamespaceAnnotation() returns error? { xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithNamespaceAnnotation2 record { @@ -175,12 +174,12 @@ function testXsdSequenceWithNamespaceAnnotation2() returns error? { xmlStr = string `ABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCABCABCD`; v = parseString(xmlStr); @@ -201,7 +200,7 @@ function testXsdSequenceWithNamespaceAnnotation2() returns error? { xmlStr = string `ABABABABABAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = string `ABCABAB`; v = parseString(xmlStr); @@ -210,25 +209,25 @@ function testXsdSequenceWithNamespaceAnnotation2() returns error? { xmlStr = string `ABCABCCD`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `AB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = string `ABCAB`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal index e3a2d510..1bd34968 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal @@ -1,6 +1,5 @@ import ballerina/test; -// TODO: Add tests with attributes type XsdSequenceWithNamespaceAnnotationWithXmlValue record { @Sequence { minOccurs: 0, @@ -59,7 +58,7 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { xmlStr = xml `ABCABC`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element EA3 not found in seq_EA1_NamespaceAnnotationWithXmlValue"), (v).message()); + test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_NamespaceAnnotationWithXmlValue'"); xmlStr = xml `ABCABCABABAB`; v = parseAsType(xmlStr); @@ -80,7 +79,7 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { xmlStr = xml `ABABABABABAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = xml `ABCABAB`; v = parseAsType(xmlStr); @@ -89,27 +88,27 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { xmlStr = xml `ABCABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `AB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } type XsdSequenceWithNamespaceAnnotationWithXmlValue2 record { @@ -175,12 +174,12 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue2() returns error? { xmlStr = xml `ABCABC`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required times"), (v).message()); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCABCABCD`; v = parseAsType(xmlStr); @@ -201,7 +200,7 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue2() returns error? { xmlStr = xml `ABABABABABAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs more than the max allowed times")); + test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); xmlStr = xml `ABCABAB`; v = parseAsType(xmlStr); @@ -210,25 +209,25 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue2() returns error? { xmlStr = xml `ABCABCCD`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `AB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("EA3 Element occurs less than the min required")); + test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index f9f79862..c980a21f 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -31,26 +31,22 @@ function testXsdSequence() returns error? { xmlStr = string `11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - // TODO: Change error messageas - // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); - test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecord"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecord'"); xmlStr = string `13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary not found in seq_XSDSequenceRecord"), msg = (v).message()); + test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord'"); xmlStr = string ``; v = parseString(xmlStr); test:assertTrue(v is Error); - - // TODO: Change the error message in validate Fields function - test:assertTrue((v).message().includes("required field 'seq_XSDSequenceRecord' not present in XML"), msg = (v).message()); + test:assertEquals((v).message(), ("required field 'seq_XSDSequenceRecord' not present in XML"), msg = (v).message()); xmlStr = string `11.113`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecord"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecord'"); // TODO: Create an issue // xmlStr = string `13`; @@ -108,27 +104,27 @@ function testXsdSequenceP2() returns error? { xmlStr = string `13ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'name' is not in the correct order in 'seq_XSDSequenceRecordP2'"); xmlStr = string `1313ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'name' is not in the correct order in 'seq_XSDSequenceRecordP2'"); xmlStr = string `1311.1ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordP2'"); xmlStr = string `1313131311.1ABC11.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("age Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'age' occurs more than the max allowed times"); xmlStr = string `11.1ABC13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordP2'"); xmlStr = string `13131311.1ABCABC`; v = parseString(xmlStr); @@ -137,12 +133,12 @@ function testXsdSequenceP2() returns error? { xmlStr = string `13131311.1ABCABCABC`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("name Element occurs more than the max allowed"), msg = (v).message()); + test:assertEquals((v).message(), "'name' occurs more than the max allowed times"); xmlStr = string `131311.1ABC13`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary, name not found in seq_XSDSequenceRecordP2"), msg = (v).message()); + test:assertEquals((v).message(), ("Element(s) 'salary, name' is not found in 'seq_XSDSequenceRecordP2'"), msg = (v).message()); } // TODO: Test with open records. @@ -153,7 +149,6 @@ type XSDSequenceRecord2 record {| } Seq_XSDSequenceRecord2 seq_XSDSequenceRecord2; - // TODO: After adding XSD validation for traverse, check union fields as well int num; |}; @@ -188,7 +183,7 @@ function testXsdSequence2() returns error? { xmlStr = string `13311.1`; v = parseString(xmlStr); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary not found in")); + test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord2'"); } type XSDSequenceRecord3 record {| @@ -233,7 +228,7 @@ function testXsdSequence3() returns error? { xmlStr = string `13311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in")); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord3'"); } type XSDSequenceRecord4 record {| @@ -276,7 +271,7 @@ function testXsdSequence4() returns error? { xmlStr = string `13311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in")); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord4'"); } type XSDSequenceRecord5 record {| @@ -330,7 +325,7 @@ function testXsdSequence5() returns error? { xmlStr = string `133311.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord5'"); } type XSDSequenceRecord6 record {| @@ -417,32 +412,32 @@ function testXsdSequence6() returns error? { xmlStr = string `SD13success11.1`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecord6_2'"); xmlStr = string `13success11.1SD`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord6_1'"); xmlStr = string `successSD1311.133`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'status' is not in the correct order in 'seq_XSDSequenceRecord6_2'"); xmlStr = string `SDsuccess11.11333`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecord6_1'"); xmlStr = string `SDsuccess11.13133`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), (v2).message()); + test:assertEquals((v2).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecord6_1'"); xmlStr = string `SDsuccess11313.13`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord6_1'"); } type XSDSequenceRecord7 record {| @@ -635,12 +630,12 @@ function testXsdSequence9() returns error? { xmlStr = string `SD13success11.12`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecord9_2'"); xmlStr = string `13success11.1SD2`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord9_1'"); } type XSDSequenceRecord10 record {| @@ -737,12 +732,12 @@ function testXsdSequence10() returns error? { xmlStr = string `SDAB13SuccessFail11.12`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecord10_2'"); xmlStr = string `13SuccessFail11.1SDAB2`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord10_1'"); } type XSDSequenceRecord11 record {| @@ -845,7 +840,7 @@ function testXsdSequence11() returns error? { xmlStr = string `3SDABSuccessFail31311.113SuccessFail11.1SDAB2`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecord11_1'"); } type XSDSequenceRecord12 record { @@ -1039,26 +1034,25 @@ function testXsdSequence13() returns error? { xmlStr = string `123123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceRecord13_2'"); xmlStr = string `123123123123121233123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); xmlStr = string `123123123123123132`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); xmlStr = string `132123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); xmlStr = string `123123123123123123`; v2 = parseString(xmlStr); test:assertTrue(v2 is Error); - // TODO: Fix the error message - test:assertTrue((v2).message().includes("Element field5 is not in the correct order in seq_XSDSequenceRecord13_2"), msg = (v2).message()); + test:assertEquals((v2).message(), ("Element 'field5' is not in the correct order in 'seq_XSDSequenceRecord13_2'"), msg = (v2).message()); } diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index ef955d2f..7ce0e210 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -35,32 +35,22 @@ function testXsdSequenceWithXmlValue() returns error? { xmlValue = xml `11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - // TODO: Change error messageas - // test:assertTrue((v).message().includes("Element age not found in"), msg = (v).message()); - test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); xmlValue = xml `13`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary not found in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); + test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue'"); xmlValue = xml ``; v = parseAsType(xmlValue); test:assertTrue(v is Error); - - // TODO: Change the error message in validate Fields function - test:assertTrue((v).message().includes("required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML"), msg = (v).message()); + test:assertEquals((v).message(), ("required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML"), msg = (v).message()); xmlValue = xml `11.113`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in seq_XSDSequenceRecordWithXmlValue"), msg = (v).message()); - - // TODO: Create an issue - // xmlValue = xml `13`; - // v = parseAsType(xmlValue); - // test:assertTrue(v is Error); - // test:assertTrue((v).message().includes("Element age is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); } @Name { @@ -117,27 +107,27 @@ function testXsdSequenceWithXmlValueP2() returns error? { xmlValue = xml `13ABC11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'name' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValueP2'"); xmlValue = xml `1313ABC11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element name is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'name' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValueP2'"); xmlValue = xml `1311.1ABC11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValueP2'"); xmlValue = xml `1313131311.1ABC11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("age Element occurs more than the max allowed times"), msg = (v).message()); + test:assertEquals((v).message(), "'age' occurs more than the max allowed times"); xmlValue = xml `11.1ABC13`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary is not in the correct order in"), msg = (v).message()); + test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValueP2'"); xmlValue = xml `13131311.1ABCABC`; v = parseAsType(xmlValue); @@ -146,15 +136,14 @@ function testXsdSequenceWithXmlValueP2() returns error? { xmlValue = xml `13131311.1ABCABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("name Element occurs more than the max allowed"), msg = (v).message()); + test:assertEquals((v).message(), "'name' occurs more than the max allowed times"); xmlValue = xml `131311.1ABC13`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary, name not found in seq_XSDSequenceRecordWithXmlValueP2"), msg = (v).message()); + test:assertEquals((v).message(), ("Element(s) 'salary, name' is not found in 'seq_XSDSequenceRecordWithXmlValueP2'"), msg = (v).message()); } -// TODO: Test with open records. @Name { value: "Root" } @@ -164,8 +153,6 @@ type XSDSequenceRecordWithXmlValue2 record {| maxOccurs: 1 } Seq_XSDSequenceRecordWithXmlValue2 seq_XSDSequenceRecordWithXmlValue2; - - // TODO: After adding XSD validation for traverse, check union fields as well int num; |}; @@ -202,7 +189,7 @@ function testXsdSequenceWithXmlValue2() returns error? { xmlValue = xml `13311.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); - test:assertTrue((v).message().includes("Element salary not found in")); + test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue2'"); } @Name { @@ -214,8 +201,7 @@ type XSDSequenceRecordWithXmlValue3 record {| maxOccurs: 1 } Seq_XSDSequenceRecordWithXmlValue3 seq_XSDSequenceRecordWithXmlValue3; - - // TODO: After adding XSD validation for traverse, check union fields as well + record{int n;} num; |}; @@ -252,7 +238,7 @@ function testXsdSequenceWithXmlValue3() returns error? { xmlValue = xml `13311.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in")); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue3'"); } @Name { @@ -300,7 +286,7 @@ function testXsdSequenceWithXmlValue4() returns error? { xmlValue = xml `13311.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in")); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue4'"); } @Name { @@ -360,7 +346,7 @@ function testXsdSequenceWithXmlValue5() returns error? { xmlValue = xml `1333`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue5'"); } @Name { @@ -454,32 +440,32 @@ function testXsdSequenceWithXmlValue6() returns error? { xmlValue = xml `SD13success11.1`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecordWithXmlValue6_2'"); xmlValue = xml `13success11.1SD`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue6_1'"); xmlValue = xml `successSD1311.133`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'status' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue6_2'"); xmlValue = xml `SDsuccess11.11333`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue6_1'"); xmlValue = xml `SDsuccess11.13133`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary is not in the correct order in"), (v2).message()); + test:assertEquals((v2).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue6_1'"); xmlValue = xml `SDsuccess11313.13`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue6_1'"); } @Name { @@ -682,12 +668,12 @@ function testXsdSequenceWithXmlValue9() returns error? { xmlValue = xml `SD13success11.12`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecordWithXmlValue9_2'"); xmlValue = xml `13success11.1SD2`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue9_1'"); } @Name { @@ -782,12 +768,12 @@ function testXsdSequenceWithXmlValue10() returns error? { xmlValue = xml `SDAB13SuccessFail11.12`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element status not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'status' is not found in 'seq_XSDSequenceRecordWithXmlValue10_2'"); xmlValue = xml `13SuccessFail11.1SDAB2`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue10_1'"); } @Name { @@ -888,7 +874,7 @@ function testXsdSequenceWithXmlValue11() returns error? { xmlValue = xml `3SDABSuccessFail31311.113SuccessFail11.1SDAB2`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element salary not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue11_1'"); } @Name { @@ -974,26 +960,25 @@ function testXsdSequenceWithXmlValue13() returns error? { xmlValue = xml `123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element field6 not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceRecordWithXmlValue13_2'"); xmlValue = xml `123123123123121233123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element f not found in"), (v2).message()); + test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); xmlValue = xml `123123123123123132`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element i is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); xmlValue = xml `132123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - test:assertTrue((v2).message().includes("Element c is not in the correct order in"), msg = (v2).message()); + test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); xmlValue = xml `123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); - // TODO: Fix the error message - test:assertTrue((v2).message().includes("Element field5 is not in the correct order in seq_XSDSequenceRecordWithXmlValue13_2"), msg = (v2).message()); + test:assertEquals((v2).message(), ("Element 'field5' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue13_2'"), msg = (v2).message()); } diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index f88b0da8..fc8d0ff1 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -177,25 +177,25 @@ public isolated function toXml(map mapValue, Options options = {}) retu return convertMapXml(jsonValue); } else if jsonValue is json[] { jsonOptions.rootTag = jsonValue[1].toString(); - return fromModifiedRecordToXml(jsonValue[0], jsonOptions, inputType); + return fromRecordToXml(jsonValue[0], jsonOptions, inputType); } - return fromModifiedRecordToXml(jsonValue.toJson(), jsonOptions, inputType); + return fromRecordToXml(jsonValue.toJson(), jsonOptions, inputType); } -// isolated function convertMapXml(map|map mapValue) returns xml { -// xml xNode = xml ``; - // foreach [string, xml|xml[]] [key, xmlVal] in mapValue.entries() { - // xml|xml[] values = xmlVal; - // if values is xml[] { - // foreach xml value in values { - // xNode += xml:createElement(key, {}, value); - // } - // } else { - // xNode += xml:createElement(key, {}, values); - // } - // } -// return xml:createElement("root", {}, xNode); -// } +isolated function convertMapXml(map|map mapValue) returns xml { + xml xNode = xml ``; + foreach [string, xml|xml[]] [key, xmlVal] in mapValue.entries() { + xml|xml[] values = xmlVal; + if values is xml[] { + foreach xml value in values { + xNode += xml:createElement(key, {}, value); + } + } else { + xNode += xml:createElement(key, {}, values); + } + } + return xml:createElement("root", {}, xNode); +} isolated function getModifiedRecord(map mapValue, string textFieldName, typedesc<(map|json)> inputType) returns json|record {}|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.DataUtils"} external; @@ -219,257 +219,257 @@ public type JsonOptions record {| # # + jsonValue - The JSON source to be converted to XML # + options - The `xmldata:JsonOptions` record for JSON to XML conversion properties -// # + return - XML representation of the given JSON if the JSON is successfully converted or else an `xmldata:Error`. -// public isolated function fromJson(json jsonValue, JsonOptions options = {}) returns xml|Error { -// string? rootTag = options.rootTag; -// map allNamespaces = {}; -// if !isSingleNode(jsonValue) { -// addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); -// return getElement(rootTag ?: "root", -// check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, -// check getAttributesMap(jsonValue, options, allNamespaces)); -// } - -// map|error jMap = jsonValue.ensureType(); -// if jMap is map { -// if jMap.length() == 0 { -// return xml ``; -// } - -// json value = jMap.toArray()[0]; -// addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); -// if value is json[] { -// return getElement(rootTag ?: "root", -// check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), -// allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); -// } - -// string key = jMap.keys()[0]; -// if key == options.textFieldName { -// return xml:createText(value.toString()); -// } -// xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), -// allNamespaces, options, -// check getAttributesMap(value, options, allNamespaces)); -// if rootTag is string { -// return xml:createElement(rootTag, {}, output); -// } -// return output; -// } -// return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); -// } - -// isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, -// string? 'key = ()) returns xml|Error { -// map namespacesOfElem = {}; -// string attributePrefix = options.attributePrefix; -// xml xNode = xml ``; -// if jNode is map { -// foreach [string, json] [k, value] in jNode.entries() { -// string jsonKey = k.trim(); -// if jsonKey.startsWith(attributePrefix) { -// continue; -// } - -// if jsonKey == options.textFieldName { -// xNode += xml:createText(value.toString()); -// } else { -// namespacesOfElem = check getNamespacesMap(value, options, parentNamespaces); -// addNamespaces(allNamespaces, namespacesOfElem); -// if value is json[] { -// xNode += check traverseNode(value, allNamespaces, namespacesOfElem, options, jsonKey); -// } else { -// xNode += -// check getElement(jsonKey, check traverseNode(value, allNamespaces, namespacesOfElem, options), -// allNamespaces, options, -// check getAttributesMap(value, options, allNamespaces, parentNamespaces)); -// } -// } -// } -// } else if jNode is json[] { -// foreach var i in jNode { -// string arrayEntryTagKey = EMPTY_STRING; -// if 'key is string { -// arrayEntryTagKey = 'key; -// } else if options.arrayEntryTag != EMPTY_STRING { -// arrayEntryTagKey = options.arrayEntryTag; -// } -// namespacesOfElem = check getNamespacesMap(i, options, parentNamespaces); -// addNamespaces(allNamespaces, namespacesOfElem); -// if options.arrayEntryTag == EMPTY_STRING { -// xNode += check getElement(arrayEntryTagKey, -// check traverseNode(i, allNamespaces, namespacesOfElem, options, 'key), -// allNamespaces, options, -// check getAttributesMap(i, options, allNamespaces, parentNamespaces)); -// } else { -// xNode += check getElement(arrayEntryTagKey, -// check traverseNode(i, allNamespaces, namespacesOfElem, options), -// allNamespaces, options, -// check getAttributesMap(i, options, allNamespaces, parentNamespaces)); -// } -// } -// } else { -// xNode = xml:createText(jNode.toString()); -// } -// return xNode; -// } - -// isolated function isSingleNode(json node) returns boolean { -// map|error jMap = node.ensureType(); -// return node !is json[] && (jMap !is map || jMap.length() <= 1); -// } - -// isolated function getElement(string name, xml children, map namespaces, JsonOptions options, -// map attributes = {}) returns xml|Error { -// string attributePrefix = options.attributePrefix; -// string userAttributePrefix = options.userAttributePrefix; -// xml:Element element; -// int? index = name.indexOf(":"); -// if index is int { -// string prefix = name.substring(0, index); - -// string elementName; -// if userAttributePrefix !is EMPTY_STRING { -// elementName = removeUserAttributePrefix(name, userAttributePrefix, index); -// } else { -// elementName = name.substring(index + 1, name.length()); -// } - -// string namespaceUrl = attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); -// if namespaceUrl == EMPTY_STRING { -// namespaceUrl = namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); -// if namespaceUrl != EMPTY_STRING { -// attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = namespaceUrl; -// } -// } -// if namespaceUrl == EMPTY_STRING { -// element = xml:createElement(elementName, attributes, children); -// } else { -// element = xml:createElement(string `{${namespaceUrl}}${elementName}`, attributes, children); -// } -// } else { -// if name.startsWith(attributePrefix) { -// return error("attribute cannot be an object or array"); -// } -// map newAttributes = attributes; -// if newAttributes.hasKey(string `{${XMLNS_NAMESPACE_URI}}`) { -// string value = newAttributes.get(string `{${XMLNS_NAMESPACE_URI}}`); -// _ = newAttributes.remove(string `{${XMLNS_NAMESPACE_URI}}`); -// newAttributes[XMLNS] = value; -// } -// if userAttributePrefix !is EMPTY_STRING { -// element = xml:createElement(removeUserAttributePrefix(name, userAttributePrefix, ()), newAttributes, children); -// } else { -// element = xml:createElement(name, newAttributes, children); -// } -// } -// return element; -// } - -// isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string { -// int? usrAttIndex = name.indexOf(userAttributePrefix); -// if usrAttIndex is int { -// return name.substring(usrAttIndex + 1, name.length()); -// } - -// if index is int { -// return name.substring(index + 1, name.length()); -// } -// return name; -// } - -// isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, -// map parentNamespaces = {}) returns map|Error { -// map attributes = parentNamespaces.clone(); -// map|error attr = jTree.ensureType(); -// string attributePrefix = options.attributePrefix; -// if attr !is map { -// return attributes; -// } - -// foreach [string, json] [k, v] in attr.entries() { -// if !k.startsWith(attributePrefix) { -// continue; -// } - -// if v is map || v is json[] { -// return error("attribute cannot be an object or array"); -// } - -// int? index = k.indexOf(":"); -// if index is int { -// string suffix = k.substring(index + 1); -// if k.startsWith(attributePrefix + XMLNS) { -// attributes[string `{${XMLNS_NAMESPACE_URI}}${suffix}`] = v.toString(); -// } else { -// int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); -// string prefix = k.substring(startIndex, index); -// string namespaceUrl = namespaces.get(string `{${XMLNS_NAMESPACE_URI}}${prefix}`); -// attributes[string `{${namespaceUrl}}${suffix}`] = v.toString(); -// } -// } else { -// if k == attributePrefix + XMLNS { -// attributes[XMLNS] = v.toString(); -// } else { -// int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); -// attributes[k.substring(startIndex)] = v.toString(); -// } -// } -// } -// return attributes; -// } - -// isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int { -// int startIndex = 1; -// if attributePrefix !is ATTRIBUTE_PREFIX { -// return startIndex; -// } - -// int? location = userAttributePrefix is EMPTY_STRING ? 'key.indexOf("_") : 'key.indexOf(userAttributePrefix); -// if location is int { -// startIndex = location + 1; -// } -// return startIndex; -// } - -// isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) -// returns map|Error { -// map namespaces = parentNamespaces.clone(); -// map|error attr = jTree.ensureType(); -// string attributePrefix = options.attributePrefix; -// if attr !is map { -// return namespaces; -// } - -// foreach [string, json] [k, v] in attr.entries() { -// if !k.startsWith(attributePrefix) { -// continue; -// } - -// if v is map|json[] { -// return error("attribute cannot be an object or array."); -// } - -// if !k.startsWith(attributePrefix + XMLNS) { -// continue; -// } - -// int? index = k.indexOf(":"); -// if index is int { -// string prefix = k.substring(index + 1); -// namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString(); -// } else { -// namespaces[string `{${XMLNS_NAMESPACE_URI}}`] = v.toString(); -// } -// } -// return namespaces; -// } - -// isolated function addNamespaces(map allNamespaces, map namespaces) { -// foreach [string, string] ['key, namespace] in namespaces.entries() { -// allNamespaces['key] = namespace; -// } -// } +# + return - XML representation of the given JSON if the JSON is successfully converted or else an `xmldata:Error`. +public isolated function fromJson(json jsonValue, JsonOptions options = {}) returns xml|Error { + string? rootTag = options.rootTag; + map allNamespaces = {}; + if !isSingleNode(jsonValue) { + addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); + return getElement(rootTag ?: "root", + check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, + check getAttributesMap(jsonValue, options, allNamespaces)); + } + + map|error jMap = jsonValue.ensureType(); + if jMap is map { + if jMap.length() == 0 { + return xml ``; + } + + json value = jMap.toArray()[0]; + addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); + if value is json[] { + return getElement(rootTag ?: "root", + check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), + allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); + } + + string key = jMap.keys()[0]; + if key == options.textFieldName { + return xml:createText(value.toString()); + } + xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), + allNamespaces, options, + check getAttributesMap(value, options, allNamespaces)); + if rootTag is string { + return xml:createElement(rootTag, {}, output); + } + return output; + } + return jsonValue is null ? xml `` : xml:createText(jsonValue.toString()); +} + +isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, + string? 'key = ()) returns xml|Error { + map namespacesOfElem = {}; + string attributePrefix = options.attributePrefix; + xml xNode = xml ``; + if jNode is map { + foreach [string, json] [k, value] in jNode.entries() { + string jsonKey = k.trim(); + if jsonKey.startsWith(attributePrefix) { + continue; + } + + if jsonKey == options.textFieldName { + xNode += xml:createText(value.toString()); + } else { + namespacesOfElem = check getNamespacesMap(value, options, parentNamespaces); + addNamespaces(allNamespaces, namespacesOfElem); + if value is json[] { + xNode += check traverseNode(value, allNamespaces, namespacesOfElem, options, jsonKey); + } else { + xNode += + check getElement(jsonKey, check traverseNode(value, allNamespaces, namespacesOfElem, options), + allNamespaces, options, + check getAttributesMap(value, options, allNamespaces, parentNamespaces)); + } + } + } + } else if jNode is json[] { + foreach var i in jNode { + string arrayEntryTagKey = EMPTY_STRING; + if 'key is string { + arrayEntryTagKey = 'key; + } else if options.arrayEntryTag != EMPTY_STRING { + arrayEntryTagKey = options.arrayEntryTag; + } + namespacesOfElem = check getNamespacesMap(i, options, parentNamespaces); + addNamespaces(allNamespaces, namespacesOfElem); + if options.arrayEntryTag == EMPTY_STRING { + xNode += check getElement(arrayEntryTagKey, + check traverseNode(i, allNamespaces, namespacesOfElem, options, 'key), + allNamespaces, options, + check getAttributesMap(i, options, allNamespaces, parentNamespaces)); + } else { + xNode += check getElement(arrayEntryTagKey, + check traverseNode(i, allNamespaces, namespacesOfElem, options), + allNamespaces, options, + check getAttributesMap(i, options, allNamespaces, parentNamespaces)); + } + } + } else { + xNode = xml:createText(jNode.toString()); + } + return xNode; +} + +isolated function isSingleNode(json node) returns boolean { + map|error jMap = node.ensureType(); + return node !is json[] && (jMap !is map || jMap.length() <= 1); +} + +isolated function getElement(string name, xml children, map namespaces, JsonOptions options, + map attributes = {}) returns xml|Error { + string attributePrefix = options.attributePrefix; + string userAttributePrefix = options.userAttributePrefix; + xml:Element element; + int? index = name.indexOf(":"); + if index is int { + string prefix = name.substring(0, index); + + string elementName; + if userAttributePrefix !is EMPTY_STRING { + elementName = removeUserAttributePrefix(name, userAttributePrefix, index); + } else { + elementName = name.substring(index + 1, name.length()); + } + + string namespaceUrl = attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); + if namespaceUrl == EMPTY_STRING { + namespaceUrl = namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`].toString(); + if namespaceUrl != EMPTY_STRING { + attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = namespaceUrl; + } + } + if namespaceUrl == EMPTY_STRING { + element = xml:createElement(elementName, attributes, children); + } else { + element = xml:createElement(string `{${namespaceUrl}}${elementName}`, attributes, children); + } + } else { + if name.startsWith(attributePrefix) { + return error("attribute cannot be an object or array"); + } + map newAttributes = attributes; + if newAttributes.hasKey(string `{${XMLNS_NAMESPACE_URI}}`) { + string value = newAttributes.get(string `{${XMLNS_NAMESPACE_URI}}`); + _ = newAttributes.remove(string `{${XMLNS_NAMESPACE_URI}}`); + newAttributes[XMLNS] = value; + } + if userAttributePrefix !is EMPTY_STRING { + element = xml:createElement(removeUserAttributePrefix(name, userAttributePrefix, ()), newAttributes, children); + } else { + element = xml:createElement(name, newAttributes, children); + } + } + return element; +} + +isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string { + int? usrAttIndex = name.indexOf(userAttributePrefix); + if usrAttIndex is int { + return name.substring(usrAttIndex + 1, name.length()); + } + + if index is int { + return name.substring(index + 1, name.length()); + } + return name; +} + +isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, + map parentNamespaces = {}) returns map|Error { + map attributes = parentNamespaces.clone(); + map|error attr = jTree.ensureType(); + string attributePrefix = options.attributePrefix; + if attr !is map { + return attributes; + } + + foreach [string, json] [k, v] in attr.entries() { + if !k.startsWith(attributePrefix) { + continue; + } + + if v is map || v is json[] { + return error("attribute cannot be an object or array"); + } + + int? index = k.indexOf(":"); + if index is int { + string suffix = k.substring(index + 1); + if k.startsWith(attributePrefix + XMLNS) { + attributes[string `{${XMLNS_NAMESPACE_URI}}${suffix}`] = v.toString(); + } else { + int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); + string prefix = k.substring(startIndex, index); + string namespaceUrl = namespaces.get(string `{${XMLNS_NAMESPACE_URI}}${prefix}`); + attributes[string `{${namespaceUrl}}${suffix}`] = v.toString(); + } + } else { + if k == attributePrefix + XMLNS { + attributes[XMLNS] = v.toString(); + } else { + int startIndex = getStartIndex(attributePrefix, options.userAttributePrefix, k); + attributes[k.substring(startIndex)] = v.toString(); + } + } + } + return attributes; +} + +isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int { + int startIndex = 1; + if attributePrefix !is ATTRIBUTE_PREFIX { + return startIndex; + } + + int? location = userAttributePrefix is EMPTY_STRING ? 'key.indexOf("_") : 'key.indexOf(userAttributePrefix); + if location is int { + startIndex = location + 1; + } + return startIndex; +} + +isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) + returns map|Error { + map namespaces = parentNamespaces.clone(); + map|error attr = jTree.ensureType(); + string attributePrefix = options.attributePrefix; + if attr !is map { + return namespaces; + } + + foreach [string, json] [k, v] in attr.entries() { + if !k.startsWith(attributePrefix) { + continue; + } + + if v is map|json[] { + return error("attribute cannot be an object or array."); + } + + if !k.startsWith(attributePrefix + XMLNS) { + continue; + } + + int? index = k.indexOf(":"); + if index is int { + string prefix = k.substring(index + 1); + namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString(); + } else { + namespaces[string `{${XMLNS_NAMESPACE_URI}}`] = v.toString(); + } + } + return namespaces; +} + +isolated function addNamespaces(map allNamespaces, map namespaces) { + foreach [string, string] ['key, namespace] in namespaces.entries() { + allNamespaces['key] = namespace; + } +} # Validates an XML document against a provided XML schema. # @@ -496,35 +496,5 @@ public type JsonOptions record {| public function validate(string|typedesc schema, xml xmlValue) returns boolean = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; -public isolated function isSingleNode(json node) returns boolean - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -isolated function getNamespacesMap(json jTree, JsonOptions options, map parentNamespaces = {}) - returns map|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -public isolated function addNamespaces(map allNamespaces, map namespaces) - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -public isolated function getStartIndex(string attributePrefix, string userAttributePrefix, string 'key) returns int - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -isolated function getAttributesMap(json jTree, JsonOptions options, map namespaces, - map parentNamespaces = {}) returns map|Error - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -isolated function removeUserAttributePrefix(string name, string userAttributePrefix, int? index) returns string - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -isolated function getElement(string name, xml children, map namespaces, JsonOptions options, - map attributes = {}) returns xml|Error - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -// isolated function traverseNode(json jNode, map allNamespaces, map parentNamespaces, JsonOptions options, -// string? 'key = ()) returns xml|Error -// = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -isolated function convertMapXml(map|map mapValue) returns xml - = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; - -public isolated function fromModifiedRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error +public isolated function fromRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java index f6ab4656..1b6144d7 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java @@ -39,6 +39,9 @@ private Constants() {} public static final int DEFAULT_TYPE_FLAG = 2049; public static final String FIELD = "$field$."; + public static final String ROOT_TAG = "rootTag"; + public static final String ROOT = "root"; + public static final String EMPTY_STRING = ""; public static final String NAMESPACE = "Namespace"; public static final String UNDERSCORE = "_"; public static final String COLON = ":"; 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 38775d0d..3c5a2398 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 @@ -1224,7 +1224,6 @@ public static HashMap getElementNameMap(Type type) { } return names; } - return names; } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java index 7f093046..f48d4be3 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java @@ -43,7 +43,17 @@ public enum DiagnosticErrorCode { XML_PARSE_ERROR("XML_ERROR_016", "xml.parse.error"), UNDEFINED_FIELD("XML_ERROR_0017", "undefined.field"), CANNOT_CONVERT_SOURCE_INTO_EXP_TYPE("XML_ERROR_0018", "cannot.convert.source.into.expected.type"), - FIELD_CANNOT_CAST_INTO_TYPE("XML_ERROR_0019", "field.cannot.convert.into.type"); + FIELD_CANNOT_CAST_INTO_TYPE("XML_ERROR_0019", "field.cannot.convert.into.type"), + ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES("XML_ERROR_0020", "element.occurs.more.than.max.allowed.times"), + ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES("XML_ERROR_0021", "element.occurs.less.than.min.required.times"), + INVALID_ELEMENT_FOUND("XML_ERROR_0022", "invalid.element.found"), + REQUIRED_ELEMENT_NOT_FOUND("XML_ERROR_0023", "required.element.not.found"), + ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES_IN_SEQUENCES( + "XML_ERROR_0024", "element.occurs.more.than.max.allowed.times.in.sequence"), + INCORRECT_ELEMENT_ORDER("XML_ERROR_0025", "incorrect.element.order"), + INVALID_SEQUENCE_ANNOTATION("XML_ERROR_0026", "invalid.sequence.annotation"), + INVALID_CHOICE_ANNOTATION("XML_ERROR_0027", "invalid.choice.annotation"), + INVALID_XSD_ANNOTATION("XML_ERROR_0027", "invalid.xsd.annotation"); String diagnosticId; String messageKey; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 2e8ff33c..43ff9b7f 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -32,76 +32,69 @@ public class ToXmlUtils { private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attribute_"); private static final BString XMLNS = StringUtils.fromString("xmlns"); - public static BXml fromModifiedRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { + public static BXml fromRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { Type type = typed.getDescribingType(); Type referredType = TypeUtils.getReferredType(type); - Object rootTag = options.get(StringUtils.fromString("rootTag")); + Object rootTag = options.get(StringUtils.fromString(Constants.ROOT_TAG)); BMap allNamespaces = getEmptyStringMap(); + BString rootTagBstring = StringUtils.fromString(rootTag == null ? Constants.EMPTY_STRING : rootTag.toString()); - if (!isSingleNode(jsonValue)) { + if (!isSingleRecordMember(jsonValue)) { addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, getEmptyStringMap())); - return getElement(rootTag == null ? StringUtils.fromString("root") - : StringUtils.fromString(rootTag.toString()), - traverseNode(jsonValue, allNamespaces, getEmptyStringMap(), options, - null, TypeUtils.getReferredType(type)), + return getElementFromRecordMember(rootTag == null ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, + traverseRecordAndGenerateXml(jsonValue, allNamespaces, + getEmptyStringMap(), options, null, referredType, false, false), allNamespaces, options, getAttributesMap(jsonValue, options, allNamespaces, getEmptyStringMap())); } try { - BMap jMap = (BMap) ValueUtils.convert(jsonValue, - TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + BMap jMap = (BMap) ValueUtils + .convert(jsonValue, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + if (jMap.isEmpty()) { - return ValueCreator.createXmlValue(""); + return ValueCreator.createXmlValue(Constants.EMPTY_STRING); } BString key = jMap.getKeys()[0]; String keyStr = key.getValue(); - HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); boolean isSequenceField = sequenceFieldNames.contains(keyStr); - ArrayList modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); - String localJsonKeyPart = keyStr.contains(":") - ? keyStr.substring(keyStr.indexOf(":") + 1) : keyStr; - + String localJsonKeyPart = keyStr.contains(Constants.COLON) + ? keyStr.substring(keyStr.indexOf(Constants.COLON) + 1) : keyStr; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); - boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); + boolean isContainsModelGroup = modelGroupRelatedFieldNames.contains(recordKey); Object value = ToArray.toArray(jMap).getValues()[0]; - addNamespaces(allNamespaces, getNamespacesMap(value, options, - getEmptyStringMap())); + addNamespaces(allNamespaces, getNamespacesMap(value, options, getEmptyStringMap())); if (value instanceof BArray) { - return getElement(rootTag == null ? StringUtils.fromString("root") - : StringUtils.fromString(rootTag.toString()), - traverseNode(value, allNamespaces, getEmptyStringMap(), options, jMap.getKeys()[0], - getChildElementType(TypeUtils.getReferredType(type), keyStr), - isSequenceField, isSequenceField), - allNamespaces, options, getAttributesMap(value, options, allNamespaces, - getEmptyStringMap())); + return getElementFromRecordMember(rootTag == null + ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, + traverseRecordAndGenerateXml(value, allNamespaces, getEmptyStringMap(), options, key, + getChildElementType(referredType, keyStr), isSequenceField, isSequenceField), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); } - if (key.equals(options.get(StringUtils.fromString("textFieldName")))) { + if (key.equals(options.get(Constants.TEXT_FIELD_NAME))) { return CreateText.createText(StringUtils.fromString(value.toString())); } - BXml output = getElement(jMap.getKeys()[0], - traverseNode(value, allNamespaces, getEmptyStringMap(), - options, null, - getChildElementType(TypeUtils.getReferredType(type), recordKey), - isSequenceField, isSequenceField), + + BXml output = getElementFromRecordMember(key, + traverseRecordAndGenerateXml(value, allNamespaces, getEmptyStringMap(), options, null, + getChildElementType(referredType, recordKey), isSequenceField, isSequenceField), allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); - if (!isNotContainXsdModelGroup) { + if (isContainsModelGroup) { output = output.children(); } if (rootTag != null) { - return CreateElement.createElement(StringUtils.fromString(rootTag.toString()), - getEmptyStringMap(), output); + return CreateElement.createElement(rootTagBstring, getEmptyStringMap(), output); } return output; } catch (BError e) { - return jsonValue == null ? ValueCreator.createXmlValue("") + return jsonValue == null ? ValueCreator.createXmlValue(Constants.EMPTY_STRING) : CreateText.createText(StringUtils.fromString(jsonValue.toString())); } } @@ -110,64 +103,30 @@ private static BMap getEmptyStringMap() { return (BMap) ((BMap) ValueCreator.createMapValue()); } - public static BXml convertMapXml(BMap mapValue) { - BXml xNode = ValueCreator.createXmlValue(""); - for (Map.Entry entry : mapValue.entrySet()) { - BString key = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof BArray arrayNode) { - for (Object i : arrayNode.getValues()) { - if (i == null) { - continue; - } - xNode = Concat.concat(xNode, CreateElement.createElement(key, getEmptyStringMap(), (BXml) i)); - } - } else { - xNode = Concat.concat(xNode, CreateElement.createElement(key, getEmptyStringMap(), (BXml) value)); - } - } - return CreateElement.createElement(StringUtils.fromString("root"), getEmptyStringMap(), xNode); - } - - public static BXml traverseNode(Object jNode, BMap allNamespaces, - BMap parentNamespaces, BMap options, Object keyObj, Type type) { - return traverseNode(jNode, allNamespaces, parentNamespaces, options, keyObj, type, false, false); - } - - public static BXml traverseNode(Object jNode, BMap allNamespaces, - BMap parentNamespaces, - BMap options, Object keyObj, Type type, boolean isParentSequencee) { - return traverseNode(jNode, allNamespaces, parentNamespaces, options, - keyObj, type, isParentSequencee, false); - } - - public static BXml traverseNode(Object jNode, BMap allNamespaces, + public static BXml traverseRecordAndGenerateXml(Object jNode, BMap allNamespaces, BMap parentNamespaces, BMap options, Object keyObj, Type type, boolean isParentSequence, boolean isParentSequenceArray) { BMap namespacesOfElem; - BXml xNode = ValueCreator.createXmlValue(""); + BXml xNode = ValueCreator.createXmlValue(Constants.EMPTY_STRING); String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); Type referredType = TypeUtils.getReferredType(type); HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); ArrayList modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); - BXml childElement; if (jNode instanceof BMap jMap) { BMap mapNode = (BMap) jMap; + BString[] orderedRecordKeysIfXsdSequencePresent = DataUtils.getOrderedRecordKeysIfXsdSequencePresent( + mapNode, DataUtils.getXsdSequencePriorityOrder(referredType, isParentSequence)); - BString[] orderedRecordKeysIfXsdSequencePresent = DataUtils - .getOrderedRecordKeysIfXsdSequencePresent(mapNode, - DataUtils.getXsdSequencePriorityOrder(referredType, isParentSequence)); for (BString k : orderedRecordKeysIfXsdSequencePresent) { Object value = mapNode.get(k); - String keyStr = k.getValue(); - String jsonKey = keyStr.trim(); - String localJsonKeyPart = jsonKey.contains(":") - ? jsonKey.substring(jsonKey.indexOf(":") + 1) : jsonKey; + String jsonKey = k.getValue().trim(); + String localJsonKeyPart = jsonKey.contains(Constants.COLON) ? + jsonKey.substring(jsonKey.indexOf(Constants.COLON) + 1) : jsonKey; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); - boolean isNotContainXsdModelGroup = !modelGroupRelatedFieldNames.contains(recordKey); + boolean isContainsModelGroup = modelGroupRelatedFieldNames.contains(recordKey); boolean isSequenceField = sequenceFieldNames.contains(recordKey); if (jsonKey.startsWith(attributePrefix)) { @@ -181,18 +140,17 @@ public static BXml traverseNode(Object jNode, BMap allNamespac addNamespaces(allNamespaces, namespacesOfElem); if (value instanceof BArray) { - childElement = traverseNode(value, - allNamespaces, namespacesOfElem, options, k, + childElement = traverseRecordAndGenerateXml(value, allNamespaces, namespacesOfElem, options, k, getChildElementType(referredType, recordKey), isSequenceField, isSequenceField); xNode = Concat.concat(xNode, childElement); } else { - childElement = getElement(k, - traverseNode(value, allNamespaces, namespacesOfElem, options, null, - getChildElementType(referredType, recordKey), - isSequenceField, isSequenceField), allNamespaces, options, - getAttributesMap(value, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isNotContainXsdModelGroup || isParentSequenceArray - ? childElement : childElement.children()); + childElement = getElementFromRecordMember(k, traverseRecordAndGenerateXml( + value, allNamespaces, namespacesOfElem, options, null, + getChildElementType(referredType, recordKey), isSequenceField, isSequenceField), + allNamespaces, options, getAttributesMap( + value, options, allNamespaces, parentNamespaces)); + xNode = Concat.concat(xNode, !isContainsModelGroup || isParentSequenceArray ? childElement + : childElement.children()); } } } @@ -201,7 +159,7 @@ public static BXml traverseNode(Object jNode, BMap allNamespac if (i == null) { continue; } - String arrayEntryTagKey = ""; + String arrayEntryTagKey = Constants.EMPTY_STRING; if (keyObj instanceof BString key) { arrayEntryTagKey = key.getValue(); } else if (!options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { @@ -211,20 +169,19 @@ public static BXml traverseNode(Object jNode, BMap allNamespac namespacesOfElem = getNamespacesMap(i, options, parentNamespaces); addNamespaces(allNamespaces, namespacesOfElem); if (options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { - childElement = getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, + childElement = getElementFromRecordMember(StringUtils.fromString(arrayEntryTagKey), + traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, options, keyObj, getChildElementType(referredType, null), isParentSequence, isParentSequenceArray), allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } else { - childElement = getElement(StringUtils.fromString(arrayEntryTagKey), - traverseNode(i, allNamespaces, namespacesOfElem, + childElement = getElementFromRecordMember(StringUtils.fromString(arrayEntryTagKey), + traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, options, null, getChildElementType(referredType, null), isParentSequence, isParentSequenceArray), allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); - xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } + xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } } else { xNode = CreateText.createText(StringUtils.fromString(StringUtils.getStringValue(jNode))); @@ -263,8 +220,8 @@ private static Type getChildElementType(Type type, String recordKey) { if (fields.containsKey(recordKey)) { return fields.get(recordKey).getFieldType(); } - Optional fieldName = getFieldFromRecordNameAnnotation(fields, recordKey); + Optional fieldName = getFieldFromRecordNameAnnotation(fields, recordKey); if (!(fieldName.isEmpty()) && fields.containsKey(fieldName.get())) { return fields.get(fieldName.get()).getFieldType(); } else { @@ -272,11 +229,9 @@ private static Type getChildElementType(Type type, String recordKey) { throw DiagnosticLog.createXmlError("Invalid xml provided"); } } - return type; } catch (Exception e) { - int a = 1; - throw e; + throw DiagnosticLog.createXmlError("Invalid xml provided"); } } @@ -297,9 +252,8 @@ private static Optional getFieldFromRecordNameAnnotation(Map namespaces, + public static BXml getElementFromRecordMember(BString name, BXml children, BMap namespaces, BMap options, BMap attributes) { - return getElement(name, children, namespaces, options, attributes, PredefinedTypes.TYPE_ANYDATA); + return getElementFromRecordMember( + name, children, namespaces, options, attributes, PredefinedTypes.TYPE_ANYDATA); } - public static BXml getElement(BString name, BXml children, BMap namespaces, + public static BXml getElementFromRecordMember(BString name, BXml children, BMap namespaces, BMap options, BMap attributes, Type type) { String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); String userAttributePrefix = options.get(Constants.USER_ATTRIBUTE_PREFIX).toString(); BXml element; - String nameStr = name.getValue(); - - int index = nameStr.indexOf(":"); + int index = nameStr.indexOf(Constants.COLON); if (index != -1) { String prefix = nameStr.substring(0, index); - String elementName; + if (!userAttributePrefix.isEmpty()) { elementName = removeUserAttributePrefix(StringUtils.fromString(nameStr), StringUtils.fromString(userAttributePrefix), (long) index).getValue(); @@ -343,20 +296,18 @@ public static BXml getElement(BString name, BXml children, BMap newAttributes = attributes; - if (newAttributes.containsKey(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}"))) { - String value = newAttributes.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}")).toString(); - newAttributes.remove(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}")); + if (newAttributes.containsKey(StringUtils.fromString(getXmlnsNameUrI()))) { + String value = newAttributes.get(StringUtils.fromString(getXmlnsNameUrI())).toString(); + newAttributes.remove(StringUtils.fromString(getXmlnsNameUrI())); newAttributes.put(XMLNS, StringUtils.fromString(value)); } - if (!userAttributePrefix.equals("")) { - element = CreateElement.createElement( - removeUserAttributePrefix(StringUtils.fromString(nameStr), - StringUtils.fromString(userAttributePrefix), null), newAttributes, children); + if (!userAttributePrefix.equals(Constants.EMPTY_STRING)) { + element = CreateElement.createElement(removeUserAttributePrefix(StringUtils.fromString(nameStr), + StringUtils.fromString(userAttributePrefix), null), newAttributes, children); } else { element = CreateElement.createElement(StringUtils.fromString(nameStr), newAttributes, children); } - } return element; } @@ -389,8 +338,8 @@ public static BXml getElement(BString name, BXml children, BMap getAttributesMap(Object jsonTree, BMap parentNamespaces) { BMap attributes = (BMap) parentNamespaces.copy(new HashMap<>()); try { - BMap attr = (BMap) ValueUtils - .convert(jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + BMap attr = (BMap) ValueUtils.convert( + jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); for (Map.Entry entry : attr.entrySet()) { @@ -422,20 +371,17 @@ public static BMap getAttributesMap(Object jsonTree, DiagnosticLog.createXmlError("attribute cannot be an object or array."); } - int index = key.indexOf(":"); + int index = key.indexOf(Constants.COLON); if (index != -1) { String suffix = key.substring(index + 1); if (key.startsWith(attributePrefix + XMLNS)) { - attributes.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + suffix), + attributes.put(StringUtils.fromString(getXmlnsNameUrI() + suffix), StringUtils.fromString(StringUtils.getStringValue(value))); } else { - Long startIndex = - getStartIndex(StringUtils.fromString(attributePrefix), StringUtils - .fromString(options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), - StringUtils.fromString(key)); + Long startIndex = getStartIndex(StringUtils.fromString(attributePrefix), StringUtils.fromString( + options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), StringUtils.fromString(key)); String prefix = key.substring(startIndex.intValue(), index); - BString namespaceUrl = - namespaces.get(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + prefix)); + BString namespaceUrl = namespaces.get(StringUtils.fromString(getXmlnsNameUrI() + prefix)); attributes.put(StringUtils.fromString("{" + namespaceUrl + "}" + suffix), StringUtils.fromString(StringUtils.getStringValue(value))); } @@ -443,9 +389,8 @@ public static BMap getAttributesMap(Object jsonTree, if (key.equals(attributePrefix + XMLNS)) { attributes.put(XMLNS, StringUtils.fromString(StringUtils.getStringValue(value))); } else { - Long startIndex = - getStartIndex(StringUtils.fromString(attributePrefix), - StringUtils.fromString(options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), + Long startIndex = getStartIndex(StringUtils.fromString(attributePrefix), + StringUtils.fromString(options.get(Constants.USER_ATTRIBUTE_PREFIX).toString()), StringUtils.fromString(key)); attributes.put(StringUtils.fromString(key.substring(startIndex.intValue())), StringUtils.fromString(StringUtils.getStringValue(value))); @@ -468,8 +413,8 @@ public static Long getStartIndex(BString attributePrefix, BString userAttributeP return (long) startIndex; } - int location = userAttributePrefixStr.equals("") - ? keyStr.indexOf("_") : keyStr.indexOf(userAttributePrefixStr); + int location = userAttributePrefixStr.equals(Constants.EMPTY_STRING) ? keyStr.indexOf("_") + : keyStr.indexOf(userAttributePrefixStr); if (location != -1) { startIndex = location + 1; } @@ -480,11 +425,8 @@ public static BMap getNamespacesMap(Object jsonTree, BMap options, BMap parentNamespaces) { BMap namespaces = (BMap) parentNamespaces.copy(new HashMap<>()); - try { - Object jsonTreeObject = ValueUtils - .convert(jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); - + Object jsonTreeObject = ValueUtils.convert(jsonTree, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); BMap attr = (BMap) jsonTreeObject; String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); @@ -496,24 +438,20 @@ public static BMap getNamespacesMap(Object jsonTree, } if (value instanceof BMap || value instanceof BArray) { - // TODO: Add error messages throw DiagnosticLog.createXmlError("attribute cannot be an object or array."); } - // TODO: Make this as a constant if (!key.getValue().startsWith(attributePrefix + XMLNS)) { continue; } - int index = key.getValue().indexOf(":"); + int index = key.getValue().indexOf(Constants.COLON); if (index != -1) { String prefix = key.getValue().substring(index + 1); - - // TODO: Add constants - namespaces.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}" + prefix), + namespaces.put(StringUtils.fromString(getXmlnsNameUrI() + prefix), StringUtils.fromString(StringUtils.getStringValue(value))); } else { - namespaces.put(StringUtils.fromString("{" + XMLNS_NAMESPACE_URI + "}"), + namespaces.put(StringUtils.fromString(getXmlnsNameUrI()), StringUtils.fromString(StringUtils.getStringValue(value))); } } @@ -523,6 +461,10 @@ public static BMap getNamespacesMap(Object jsonTree, } } + private static String getXmlnsNameUrI() { + return "{" + XMLNS_NAMESPACE_URI + "}"; + } + public static void addNamespaces(BMap allNamespaces, BMap namespaces) { for (Map.Entry entry: namespaces.entrySet()) { allNamespaces.put(entry.getKey(), entry.getValue()); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java index d2a770ef..2867d629 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java @@ -72,17 +72,14 @@ public static void handleModuleXsdSequenceAnnotations(Map field } else if (fieldType instanceof ArrayType arrayType) { Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); if (elementType instanceof RecordType recType) { - //TODO: Define a separate function parserData.xsdModelGroupInfo.peek().put(fieldName, new SequenceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_SEQUENCE_ANNOTATION, fieldName, fieldType); } } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_SEQUENCE_ANNOTATION, fieldName, fieldType); } } @@ -95,8 +92,7 @@ public static void handleModuleChoiceSequenceAnnotations(Map fi parserData.xsdModelGroupInfo.peek().put(fieldName, new ChoiceInfo(fieldName, fieldAnnotationValue, recType, parserData.xmlElementInfo)); } else { - throw new RuntimeException("Cannot include Sequence annotation into " - + fieldName + " of type " + fieldType); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_CHOICE_ANNOTATION, fieldName, fieldType); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java index 8394f2aa..6d9a1d79 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java @@ -18,9 +18,12 @@ package io.ballerina.lib.data.xmldata.xml; +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.api.values.BXml; import org.w3c.dom.Document; import org.xml.sax.InputSource; @@ -41,7 +44,7 @@ public static boolean validate(Object xsd, BXml xml) { if (xsd instanceof BString) { return validateXmlFromXsdFile(xsd.toString(), xml); } - return validateXsdFromXsdRecord((BMap) xsd); + return validateXsdFromXsdRecord((BTypedesc) xsd, xml); } private static boolean validateXmlFromXsdFile(String xsdFilePath, BXml xml) { @@ -62,7 +65,16 @@ private static boolean validateXmlFromXsdFile(String xsdFilePath, BXml xml) { } } - private static boolean validateXsdFromXsdRecord(BMap xsdRecord) { - return false; + private static boolean validateXsdFromXsdRecord(BTypedesc xsdRecord, BXml xml) { + try { + Object result = XmlTraversal.traverse(xml, + ValueCreator.createMapValue(PredefinedTypes.TYPE_STRING), xsdRecord); + if (result instanceof BError) { + return false; + } + return true; + } catch (Exception e) { + return false; + } } } 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 0dabc1ab..120c13cb 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 @@ -495,8 +495,6 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser } private void validateRequiredFields(XmlParserData xmlParserData) { - // TODO: Validate Sequence and Choice Fields - Map remainingFields = xmlParserData.fieldHierarchy.peek().getMembers(); for (Field field : remainingFields.values()) { if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.REQUIRED)) { @@ -514,7 +512,6 @@ private void validateRequiredFields(XmlParserData xmlParserData) { private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData) { QualifiedName elemQName = getElementName(xmlStreamReader, xmlParserData.useSemanticEquality); - // TODO: Check + 1, when seq present updateElementOccurrence(xmlParserData, elemQName); validateModelGroupStack(xmlParserData, elemQName, true); @@ -593,7 +590,6 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, HashMap modelGroupInfo, XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { if (modelGroupInfo != null) { - // TODO: Optimize this with stream.findFirst() for (Map.Entry entry : modelGroupInfo.entrySet()) { ModelGroupInfo modelGroupValue = entry.getValue(); String key = entry.getKey(); @@ -647,7 +643,7 @@ private void updateNextRecordForXsd(XmlParserData xmlParserData, return; } } - throw new RuntimeException("Not implemented"); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_XSD_ANNOTATION, fieldName, fieldType); } private void initializeNextValueBasedOnExpectedType(String fieldName, Type fieldType, Object temp, diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java index ecfb838c..88aa762d 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java @@ -920,7 +920,6 @@ private void validateElementInXsdSequenceOrElement(QualifiedName elemQName, XmlAnalyzerData xmlAnalyzerData, QualifiedNameMap visitedFields, QualifiedNameMap fieldMap) { if (modelGroupInfo != null) { - // TODO: Optimize this with stream.findFirst() for (Map.Entry entry : modelGroupInfo.entrySet()) { ModelGroupInfo modelGroupValue = entry.getValue(); String key = entry.getKey(); @@ -980,7 +979,7 @@ private void updateNextRecordForXsd(XmlAnalyzerData xmlAnalyzerData, String fiel return; } } - throw new RuntimeException("Not implemented"); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_XSD_ANNOTATION, fieldName, fieldType); } private void validateModelGroupStackForRootElement(XmlAnalyzerData analyzerData) { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 471b1115..0d708109 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -2,6 +2,8 @@ import io.ballerina.lib.data.xmldata.utils.Constants; import io.ballerina.lib.data.xmldata.utils.DataUtils; +import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; +import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; @@ -55,7 +57,7 @@ public ChoiceInfo(String fieldName, BMap element, RecordType fi public void updateOccurrences() { this.occurrences++; if (occurrences > maxOccurs) { - throw new RuntimeException(fieldName + " Element occurs more than the max allowed times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, fieldName); } } @@ -118,6 +120,7 @@ public void visit(String element, boolean isStartElement) { this.visitedElements.add(element); updateOccurrences(); } + if (remainingElementCount.get(element) == 0) { remainingElementCount.putAll(maxElementCount); visitedElements.remove(element); @@ -125,7 +128,7 @@ public void visit(String element, boolean isStartElement) { return; } - throw new RuntimeException("Unexpected element " + xmlElementNameMap.get(element) + " found in " + fieldName); + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_ELEMENT_FOUND, xmlElementNameMap.get(element), fieldName); } @Override @@ -146,7 +149,7 @@ public boolean predictStartNewModelGroup(String element) { public void validateMinOccurrences() { if (this.occurrences < this.minOccurs) { - throw new RuntimeException(fieldName + " Element occurs less than the min required times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, fieldName); } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index cd648b76..3a85b2d9 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -1,6 +1,8 @@ package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; +import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; +import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; @@ -33,7 +35,7 @@ public ElementInfo(String name, String fieldName, BMap element) public void updateOccurrences() { this.occurrences++; if (this.occurrences > this.maxOccurs) { - throw new RuntimeException(name + " Element occurs more than the max allowed times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, name); } } @@ -43,7 +45,7 @@ public void validate() { private void validateMinOccurrences() { if (!isInsideChoice && this.occurrences < this.minOccurs) { - throw new RuntimeException(name + " Element occurs less than the min required times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, name); } } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index a8ff419f..2a7f3664 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -2,6 +2,8 @@ import io.ballerina.lib.data.xmldata.utils.Constants; import io.ballerina.lib.data.xmldata.utils.DataUtils; +import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; +import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; @@ -17,8 +19,6 @@ public class SequenceInfo implements ModelGroupInfo { public long minOccurs; public long maxOccurs; public int occurrences; - - // TODO: Update to a hashset private final Map remainingElementCount = new HashMap<>(); private final Map minimumElementCount = new HashMap<>(); private final Map maxElementCount = new HashMap<>(); @@ -57,13 +57,13 @@ public SequenceInfo(String fieldName, BMap element, RecordType public void updateOccurrences() { this.occurrences++; if (this.occurrences > this.maxOccurs) { - throw new RuntimeException(fieldName + " Element occurs more than the max allowed times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, fieldName); } } public void validateMinOccurrences() { if (this.occurrences < this.minOccurs) { - throw new RuntimeException(fieldName + " Element occurs less than the min required times"); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, fieldName); } } @@ -127,7 +127,8 @@ public boolean predictStartNewModelGroup(String element) { private void validateCompletedSequences() { if (!isCompleted && !containsAllOptionalElements()) { - throw new RuntimeException("Element " + getUnvisitedElements() + " not found in " + fieldName); + throw DiagnosticLog.error(DiagnosticErrorCode + .REQUIRED_ELEMENT_NOT_FOUND, getUnvisitedElements(), fieldName); } updateOccurrences(); } @@ -154,21 +155,21 @@ private void checkElementOrderAndUpdateElementOccurences(String element) { while (!nextElement.equals(element)) { if (!elementOptionality.get(nextElement)) { - throw new RuntimeException("Element " + xmlElementNameMap.get(element) + - " is not in the correct order in " + fieldName); + throw DiagnosticLog.error(DiagnosticErrorCode.INCORRECT_ELEMENT_ORDER, + xmlElementNameMap.get(element), fieldName); } currentIndex++; nextElement = allElements.get(currentIndex); if (currentIndex == this.elementCount) { - throw new RuntimeException("Element " + xmlElementNameMap.get(element) + - " is not in the correct order in " + fieldName); + throw DiagnosticLog.error(DiagnosticErrorCode.INCORRECT_ELEMENT_ORDER, + xmlElementNameMap.get(element), fieldName); } } if (remainingElementCount.get(nextElement) == 0) { - throw new RuntimeException("Element " + xmlElementNameMap.get(element) + - " occurs more than the max allowed times in " + fieldName); + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES_IN_SEQUENCES, + xmlElementNameMap.get(nextElement), fieldName); } else { remainingElementCount.put(element, remainingElementCount.get(nextElement) - 1); int elementCount = maxElementCount.get(element) - remainingElementCount.get(element); @@ -203,8 +204,8 @@ private String getUnvisitedElements() { private void updateUnvisitedElementsBasedOnPriorityOrder(RecordType fieldType) { this.allElements.addAll(DataUtils.getXsdSequencePriorityOrder(fieldType, true).entrySet().stream() - .sorted(Map.Entry.comparingByValue()) // Sort by Long values in priority order - .map(Map.Entry::getKey) // Get xml element name from + .sorted(Map.Entry.comparingByValue()) + .map(Map.Entry::getKey) .toList()); this.currentIndex = 0; diff --git a/native/src/main/resources/error.properties b/native/src/main/resources/error.properties index 65e56eef..07df7758 100644 --- a/native/src/main/resources/error.properties +++ b/native/src/main/resources/error.properties @@ -76,3 +76,30 @@ error.cannot.convert.source.into.expected.type=\ error.field.cannot.convert.into.type=\ field ''{0}'' cannot be converted into the type ''{1}'' + +error.element.occurs.more.than.max.allowed.times=\ + ''{0}'' occurs more than the max allowed times + +error.element.occurs.less.than.min.required.times=\ + ''{0}'' occurs less than the min required times + +error.invalid.element.found=\ + Unexpected element ''{0}'' found in ''{1}'' + +error.required.element.not.found=\ + Element(s) ''{0}'' is not found in ''{1}'' + +error.incorrect.element.order=\ + Element ''{0}'' is not in the correct order in ''{1}'' + +error.element.occurs.more.than.max.allowed.times.in.sequence=\ + ''{0}'' occurs more than the max allowed times in ''{1}'' + +error.invalid.sequence.annotation=\ + Cannot include Sequence annotation into ''{0}'' of type ''{1}'' + +error.invalid.choice.annotation=\ + Cannot include Choice annotation into ''{0}'' of type ''{1}'' + +error.invalid.xsd.annotation=\ + Cannot include xsd annotation into ''{0}'' of type ''{1}'' \ No newline at end of file From e206653d45a94d2b51f557c4334900bafc24c411 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sat, 16 Nov 2024 23:33:24 +0530 Subject: [PATCH 37/58] Add tests for invalid xsd annotations --- .../tests/xsd_test_with_invalid_records.bal | 81 +++++++++++++++++++ .../lib/data/xmldata/utils/ToXmlUtils.java | 1 - 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 ballerina/tests/xsd_test_with_invalid_records.bal diff --git a/ballerina/tests/xsd_test_with_invalid_records.bal b/ballerina/tests/xsd_test_with_invalid_records.bal new file mode 100644 index 00000000..50833f8f --- /dev/null +++ b/ballerina/tests/xsd_test_with_invalid_records.bal @@ -0,0 +1,81 @@ +import ballerina/test; + +type XSDSequenceInvalidRecord record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + int a; +|}; + +type XSDSequenceInvalidRecord2 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + int[] a; +|}; + +type StringArr string[][]; + +type XSDSequenceInvalidRecord3 record {| + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + StringArr a; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithInvalidRecord() returns error? { + string xmlStr = string `1311.1`; + XSDSequenceInvalidRecord|Error v = parseString(xmlStr); + test:assertEquals((v).message(), "Cannot include Sequence annotation into 'a' of type 'int'"); + + XSDSequenceInvalidRecord2|Error v2 = parseString(xmlStr); + test:assertEquals((v2).message(), "Cannot include Sequence annotation into 'a' of type 'int[]'"); + + XSDSequenceInvalidRecord3|Error v3 = parseString(xmlStr); + test:assertEquals((v3).message(), "Cannot include Sequence annotation into 'a' of type 'string[][]'"); +} + +type XSDChoiceInvalidRecord record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + int a; +|}; + +type XSDChoiceInvalidRecord2 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + int[] a; +|}; + +type XSDChoiceInvalidRecord3 record {| + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + StringArr a; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceWithInvalidRecord() returns error? { + string xmlStr = string `1311.1`; + XSDChoiceInvalidRecord|Error v = parseString(xmlStr); + test:assertEquals((v).message(), "Cannot include Choice annotation into 'a' of type 'int'"); + + XSDChoiceInvalidRecord2|Error v2 = parseString(xmlStr); + test:assertEquals((v2).message(), "Cannot include Choice annotation into 'a' of type 'int[]'"); + + XSDChoiceInvalidRecord3|Error v3 = parseString(xmlStr); + test:assertEquals((v3).message(), "Cannot include Choice annotation into 'a' of type 'string[][]'"); + + xmlStr = string `1311.1`; + XSDChoiceInvalidRecord|Error v4 = parseString(xmlStr); + test:assertEquals((v4).message(), "Cannot include Choice annotation into 'a' of type 'int'"); +} diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 43ff9b7f..cd63b568 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -209,7 +209,6 @@ private static ArrayList getSequenceFieldNames(Type expType, private static Type getChildElementType(Type type, String recordKey) { - try { if (type instanceof ArrayType arrayType) { return TypeUtils.getReferredType(arrayType.getElementType()); From f0c7210cf1ea803849cba36a9ec9714f5ad040c4 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 17 Nov 2024 00:34:39 +0530 Subject: [PATCH 38/58] Add toXml implementation for sequence arrays --- ...sd_sequence_array_test_with_parse_type.bal | 22 ++++++++++++++++--- .../xsd_sequence_tests_with_parse_type.bal | 12 ++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 454abe6f..f4e891f4 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -49,6 +49,9 @@ function testXsdSequenceArrayWithXmlValue() returns error? { test:assertEquals((v).message(), "'age' occurs more than the max allowed times in 'seq_XsdSequenceArrayWithXmlValue'"); } +@Name { + value: "Root" +} type XsdSequenceArrayWithXmlValue2 record {| @Sequence { minOccurs: 1, @@ -95,6 +98,7 @@ function testXsdSequenceArrayWithXmlValue2() returns error? { xmlValue = xml `1311.11415.11311.11415.1`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue2: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}], seq_XsdSequenceArrayWithXmlValue2_2: [{age2: 13, salary2: 11.1}, {age2: 14, salary2: 15.1}]}); + test:assertEquals(toXml(check v), xml `1311.11415.11311.11415.1`); xmlValue = xml `1311.11311.11415.11311.11415.1`; v = parseAsType(xmlValue); @@ -107,6 +111,9 @@ function testXsdSequenceArrayWithXmlValue2() returns error? { test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue2_2' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDSequenceArrayWithXmlValueRecord13 record { @Sequence { minOccurs: 1, @@ -148,14 +155,17 @@ function testXSDSequenceArrayWithXmlValueRecord4() returns error? { xml xmlValue = xml `123123123123123123`; XSDSequenceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + test:assertEquals(toXml(check v2), xml `123123123123123123`); xmlValue = xml `123123123123123123123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); + test:assertEquals(toXml(check v2), xml `123123123123123123123123123123123123123123123123123123`); xmlValue = xml `123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}, {a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}]}}]}); + test:assertEquals(toXml(check v2), xml `123123123123123123123123123123123123`); xmlValue = xml `123123123123123123123123123123123123123123123123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); @@ -221,10 +231,12 @@ function testXsdSequenceArrayWithXmlValue5() returns error? { xmlValue = xml `1311.11415.1`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue5: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}]}); + test:assertEquals(toXml(check v), xml `1311.11415.1`); - xmlValue = xml `1311.11414.11515.1`; + xmlValue = xml `1311.11414.11515.1`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XsdSequenceArrayWithXmlValue5: [{age: 13, salary: 11.1}, {age: 14, salary: 14.1}, {age: 15, salary: 15.1}]}); + test:assertEquals(toXml(check v), xml `1311.11414.11515.1`); xmlValue = xml `1311.11414.11515.11515.1`; v = parseAsType(xmlValue); @@ -237,6 +249,9 @@ function testXsdSequenceArrayWithXmlValue5() returns error? { test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue5' occurs less than the min required times"); } +@Name { + value: "Root" +} type XSDSequenceArrayWithXmlValueRecord6 record { @Sequence { minOccurs: 2, @@ -269,7 +284,8 @@ type Seq_XSDSequenceArrayWithXmlValueRecord6_2 record { @test:Config {groups: ["xsd", "xsd_sequence"]} function testXSDSequenceArrayWithXmlValueRecord6() returns error? { - xml xmlValue = xml `1111111111111111`; + xml xmlValue = xml `1211111111111111`; XSDSequenceArrayWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); - test:assertEquals(v2, {"seq_XSDSequenceArrayWithXmlValueRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayWithXmlValueRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); + test:assertEquals(v2, {"seq_XSDSequenceArrayWithXmlValueRecord6_1":[{"field1":{"value1":[{"a":"1"},{"a":"2"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}},{"field1":{"value1":[{"a":"1"},{"a":"1"}]},"field2":{"value2":[{"d":"1"},{"d":"1"}]}}],"seq_XSDSequenceArrayWithXmlValueRecord6_2":[{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}},{"field4":{"value1":[{"a":"1"},{"a":"1"}]},"field5":{"value2":[{"d":"1"},{"d":"1"}]}}]}); + test:assertEquals(toXml(check v2), xml `1211111111111111`); } diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index 7ce0e210..0c5ab11c 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -634,6 +634,7 @@ function testXsdSequenceWithXmlValue9() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess32`); xmlValue = xml `SDsuccess331311.12`; v2 = parseAsType(xmlValue); @@ -644,6 +645,7 @@ function testXsdSequenceWithXmlValue9() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess32`); xmlValue = xml `33SDsuccess1311.12`; v2 = parseAsType(xmlValue); @@ -654,6 +656,7 @@ function testXsdSequenceWithXmlValue9() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess32`); xmlValue = xml `SDsuccess1311.1332`; v2 = parseAsType(xmlValue); @@ -664,6 +667,7 @@ function testXsdSequenceWithXmlValue9() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue9_2.status, "success"); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDsuccess32`); xmlValue = xml `SD13success11.12`; v2 = parseAsType(xmlValue); @@ -734,6 +738,7 @@ function testXsdSequenceWithXmlValue10() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail32`); xmlValue = xml `SDABSuccessFail331311.12`; v2 = parseAsType(xmlValue); @@ -744,6 +749,7 @@ function testXsdSequenceWithXmlValue10() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail32`); xmlValue = xml `33SDABSuccessFail1311.12`; v2 = parseAsType(xmlValue); @@ -754,6 +760,7 @@ function testXsdSequenceWithXmlValue10() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail32`); xmlValue = xml `SDABSuccessFail1311.1332`; v2 = parseAsType(xmlValue); @@ -764,6 +771,7 @@ function testXsdSequenceWithXmlValue10() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue10_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail32`); xmlValue = xml `SDAB13SuccessFail11.12`; v2 = parseAsType(xmlValue); @@ -850,6 +858,7 @@ function testXsdSequenceWithXmlValue11() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail3231311.1SDABSuccessFail3`); xmlValue = xml `3SDABSuccessFail31311.1SDABSuccessFail331311.12`; v2 = parseAsType(xmlValue); @@ -860,6 +869,7 @@ function testXsdSequenceWithXmlValue11() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail3231311.1SDABSuccessFail3`); xmlValue = xml `33SDABSuccessFail1311.13SDABSuccessFail31311.12`; v2 = parseAsType(xmlValue); @@ -870,6 +880,7 @@ function testXsdSequenceWithXmlValue11() returns error? { test:assertEquals((check v2).test.seq_XSDSequenceRecordWithXmlValue11_2.status, {value1: "Success", value2: "Fail"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `31311.1SDABSuccessFail3231311.1SDABSuccessFail3`); xmlValue = xml `3SDABSuccessFail31311.113SuccessFail11.1SDAB2`; v2 = parseAsType(xmlValue); @@ -904,6 +915,7 @@ function testXsdSequenceWithXmlValue12() returns error? { xml xmlValue = xml `123123123`; XSDSequenceRecordWithXmlValue12|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue12_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {a: "1", b: "2", c: "3"}}, field3: {value3: {a: "1", b: "2", c: "3"}}}}); + test:assertEquals(toXml(check v2), xml `123123123`); } @Name { From 52de9edc4567d1efb0862e378ed0a1ed3f98c337 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 17 Nov 2024 01:08:52 +0530 Subject: [PATCH 39/58] Add toXml implementation for sequence with elements --- ...ith_element_annotation_with_parse_type.bal | 27 +++++++++++++++++++ .../xsd_sequence_tests_with_parse_type.bal | 6 ++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal index ebf113fd..4f0b2672 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XsdSequenceWithElementAnnotationWithXmlValue record { @Sequence { minOccurs: 0, @@ -51,19 +54,29 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABCABABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA1_Xml_Value: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + test:assertEquals(toXml(check v, {}), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB"]}}); + // // TODO: Check + // test:assertEquals(toXml(check v), xmlValue); + xmlValue = xml `ABCABABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); + // // TODO: Check + // test:assertEquals(toXml(check v), xmlValue); + xmlValue = xml `ABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{EA3: ["AB", "AB"]}}); + //TODO: Check and + // test:assertEquals(toXml(check v), xmlValue); + xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); @@ -99,6 +112,9 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } +@Name { + value: "Root" +} type XsdSequenceWithElementAnnotationWithXmlValue2 record { @Sequence { minOccurs: 0, @@ -159,6 +175,7 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA2_With_Xml_Value: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + test:assertEquals(toXml(check v, {}), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -207,6 +224,9 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } +@Name { + value: "Root" +} type XsdSequenceWithElementAnnotationWithXmlValue3 record { @Sequence { minOccurs: 1, @@ -275,18 +295,25 @@ function testXsdSequenceWithElementAnnotationWithXmlValue3() returns error? { xmlValue = xml `123123123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}, {value3: {g: "1", h: "2", i: "3"}}]}}); + test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123123123`); xmlValue = xml `123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}]}}); + // TODO: Check + // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123`); xmlValue = xml `123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + // TODO: Check + // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123123123`); xmlValue = xml `2312312312323123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1", "2", "3"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); + // TODO: Check + // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123123123`); xmlValue = xml `33123`; v2 = parseAsType(xmlValue); diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index 0c5ab11c..6ff3a7a8 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -30,7 +30,7 @@ function testXsdSequenceWithXmlValue() returns error? { test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValue: {age: 13, salary: 11.1}}); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.salary, 11.1); - test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValue)), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `11.1`; v = parseAsType(xmlValue); @@ -97,12 +97,12 @@ function testXsdSequenceWithXmlValueP2() returns error? { xmlValue = xml `1311.1ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13], salary: 11.1, name: ["ABC"]}}); - test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValueP2)), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13131311.1ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_XSDSequenceRecordWithXmlValueP2: {age: [13, 13, 13], salary: 11.1, name: ["ABC"]}}); - test:assertEquals(toXml(check v.ensureType(XSDSequenceRecordWithXmlValueP2)), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13ABC11.1`; v = parseAsType(xmlValue); From 798bdcfd7cd9ad26ff7c1ad3aada19c9ecb5b451 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 17 Nov 2024 01:32:01 +0530 Subject: [PATCH 40/58] Add toXML API test for sequences --- .../tests/xsd_choice_test_with_parse_type.bal | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index e5aeabc3..0ca2bea8 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord record {| @Choice { minOccurs: 1, @@ -21,10 +24,12 @@ function testXsdChoiceWithXmlValue() returns error? { xmlValue = xml `10`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {age: 10}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `10.5`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {salary: 10.5}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `1011.1`; v = parseAsType(xmlValue); @@ -42,6 +47,9 @@ function testXsdChoiceWithXmlValue() returns error? { test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecordP2 record {| @Choice { minOccurs: 1, @@ -64,6 +72,9 @@ type Choice_XSDChoiceWithXmlValueRecordP2 record {| string[] name?; |}; +@Name { + value: "Root" +} type XSDChoiceWithXmlValueP1Record record {| @Choice { minOccurs: 0, @@ -85,10 +96,12 @@ function testXsdChoiceWithXmlValueP1() returns error? { xmlValue = xml `10`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueP1Record: {age: 10}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `10.5`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueP1Record: {salary: 10.5}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `1011.1`; v = parseAsType(xmlValue); @@ -110,14 +123,17 @@ function testXsdChoiceWithXmlValueP2() returns error? { xml xmlValue = xml `10`; XSDChoiceWithXmlValueRecordP2|Error v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {age: [10]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {name: ["ABC"]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `11.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecordP2: {salary: 11.1}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `10101011.1ABC`; v = parseAsType(xmlValue); @@ -130,6 +146,9 @@ function testXsdChoiceWithXmlValueP2() returns error? { test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecordP2' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord2 record {| @Choice { minOccurs: 1, @@ -152,12 +171,14 @@ function testXsdChoiceWithXmlValue2() returns error? { test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord2: {age: 10}, num: 3}); test:assertEquals((check v).choice_XSDChoiceWithXmlValueRecord2.age, 10); test:assertEquals((check v).num, 3); + test:assertEquals(toXml(check v), xml `103`); xmlValue = xml `11.13`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord2: {salary: 11.1}, num: 3}); test:assertEquals((check v).choice_XSDChoiceWithXmlValueRecord2.salary, 11.1); test:assertEquals((check v).num, 3); + test:assertEquals(toXml(check v), xml `11.13`); xmlValue = xml `11.1103`; v = parseAsType(xmlValue); @@ -170,6 +191,9 @@ function testXsdChoiceWithXmlValue2() returns error? { test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord3 record {| @Choice { minOccurs: 1, @@ -192,10 +216,12 @@ function testXsdChoiceWithXmlValue3() returns error? { test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord3: {age: 10}, num: {n: 3}}); test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord3.age, 10); test:assertEquals((check v2).num, {n: 3}); + test:assertEquals(toXml(check v2), xml `103`); xmlValue = xml `11.13`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord3: {salary: 11.1}, num: {n: 3}}); + test:assertEquals(toXml(check v2), xml `11.13`); xmlValue = xml `10311.1`; v2 = parseAsType(xmlValue); @@ -208,6 +234,9 @@ function testXsdChoiceWithXmlValue3() returns error? { test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord3' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord4 record {| record{record {int n;} n;} num; @Choice { @@ -229,10 +258,12 @@ function testXsdChoiceWithXmlValue4() returns error? { test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord4: {age: 10}, num: {n: {n: 3}}}); test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord4.age, 10); test:assertEquals((check v2).num, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `11.13`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord4: {salary: 11.1}, num: {n: {n: 3}}}); + test:assertEquals(toXml(check v2), xml `311.1`); xmlValue = xml `10311.1`; v2 = parseAsType(xmlValue); @@ -245,6 +276,9 @@ function testXsdChoiceWithXmlValue4() returns error? { test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord4' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord5 record {| record{record {int n;} n;} num; @Choice { @@ -265,14 +299,17 @@ function testXsdChoiceWithXmlValue5() returns error? { xml xmlValue = xml `3310`; XSDChoiceWithXmlValueRecord5|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {age: 10}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals(toXml(check v2), xml `3103`); xmlValue = xml `311.13`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals(toXml(check v2), xml `311.13`); xmlValue = xml `11.133`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord5: {salary: 11.1}, num: {n: {n: 3}}, num2: {n: {n: 3}}}); + test:assertEquals(toXml(check v2), xml `311.13`); xmlValue = xml `103311.1`; v2 = parseAsType(xmlValue); @@ -285,6 +322,9 @@ function testXsdChoiceWithXmlValue5() returns error? { test:assertEquals((v2).message(), "required field 'choice_XSDChoiceWithXmlValueRecord5' not present in XML"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord6 record {| record{record {int n;} n;} num; @Choice { @@ -319,6 +359,7 @@ function testXsdChoiceWithXmlValue6() returns error? { test:assertEquals((check v2).choice_XSDChoiceWithXmlValueRecord6_2.status, "success"); test:assertEquals((check v2).num, {n: {n: 3}}); test:assertEquals((check v2).num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `310success3`); xmlValue = xml `SD33`; v2 = parseAsType(xmlValue); @@ -336,6 +377,9 @@ function testXsdChoiceWithXmlValue6() returns error? { test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord6_2' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord7 record {| @Choice { minOccurs: 1, @@ -364,8 +408,12 @@ function testXsdChoiceWithXmlValue7() returns error? { xml xmlValue = xml `success11.1`; XSDChoiceWithXmlValueRecord7|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord7_1: {salary: 11.1}, choice_XSDChoiceWithXmlValueRecord7_2: {status: "success"}}); + test:assertEquals(toXml(check v2), xml `11.1success`); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord8 record {| XSDChoiceWithXmlValueRecord8P test; int a; @@ -409,6 +457,7 @@ function testXsdChoiceWithXmlValue8() returns error? { test:assertEquals((check v2).test.choice_XSDChoiceWithXmlValueRecord8_2.name, {value1: "SD", value2: "AB"}); test:assertEquals((check v2).test.num, {n: {n: 3}}); test:assertEquals((check v2).test.num2, {n: {n: 3}}); + test:assertEquals(toXml(check v2), xml `310SDAB32`); xmlValue = xml `SuccessFail3311.12`; v2 = parseAsType(xmlValue); @@ -442,6 +491,9 @@ function testXsdChoiceWithXmlValue8() returns error? { test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord8_1' occurs less than the min required times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord9 record { @Choice { minOccurs: 1, @@ -464,10 +516,12 @@ function testXsdChoiceWithXmlValue9() returns error? { xmlValue = xml `1`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord9_1: {field2: {value2: {a: "1"}}}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `1`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord9_1: {field3: {value3: {c: "1"}}}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `11`; v2 = parseAsType(xmlValue); @@ -480,6 +534,9 @@ function testXsdChoiceWithXmlValue9() returns error? { test:assertEquals((v2).message(), "'value1' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XSDChoiceWithXmlValueRecord10 record { @Choice { minOccurs: 0, @@ -511,6 +568,7 @@ function testXsdChoiceWithXmlValue10() returns error? { xml xmlValue = xml `12`; XSDChoiceWithXmlValueRecord10|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {"d": "2"}}}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `112`; v2 = parseAsType(xmlValue); From 927adf81cd7ecf8dfa2cffed940623d35a9e7f98 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 17 Nov 2024 19:20:21 +0530 Subject: [PATCH 41/58] Add toXml tests for choice arrays --- .../xsd_choice_array_test_with_parse_type.bal | 26 +++++++++++++++++++ ...ith_element_annotation_with_parse_type.bal | 14 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal index f9be6bee..21c1bd2a 100644 --- a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XsdChoiceArrayWithXmlValue record {| @Choice { minOccurs: 1, @@ -21,14 +24,17 @@ function testXsdChoiceArrayWithXmlValue() returns error? { xmlValue = xml `1311.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {age: [13], salary: [11.1]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `1312`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {age: [13, 12]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `11.112.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue: {salary: [11.1, 12.1]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `1311.11414.11515.1`; v = parseAsType(xmlValue); @@ -36,6 +42,9 @@ function testXsdChoiceArrayWithXmlValue() returns error? { test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XsdChoiceArrayWithXmlValue2 record {| @Choice { minOccurs: 1, @@ -68,14 +77,17 @@ function testXsdChoiceArrayWithXmlValue2() returns error? { xmlValue = xml `1311.11311.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13], salary: [11.1]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13], salary2: [11.1]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13131313`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13, 13]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13, 13]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13131311.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue2: {age: [13, 13]}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13], salary2: [11.1]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `13131311.11415`; v = parseAsType(xmlValue); @@ -97,6 +109,9 @@ function testXsdChoiceArrayWithXmlValue2() returns error? { test:assertEquals((v).message(), "required field 'choice_XsdChoiceArrayWithXmlValue2' not present in XML"); } +@Name { + value: "Root" +} type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13 record { @Choice { minOccurs: 1, @@ -128,10 +143,12 @@ function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord4() retur xml xmlValue = xml `123123123123123123`; XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1: {field1: [{value1: {a: ["1"], b:["2"], c: ["3"]}}],field2: [{value2: {d: ["1"], e: ["2"], f: ["3"]}}], field3: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2: {field4: [{value1: {a: ["1"], b: ["2"], c: ["3"]}}], field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `22123233123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_1: {field1: [{value1: {b:["2", "2"]}}, {value1: {a: ["1"], b:["2"], c: ["3"]}}], field3: [{value3: {h: ["2"], i: ["3", "3"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord13_2: {field5: [{value2: {d: ["1"], e: ["2"], f:["3"]}}, {value2: {d: ["1"], e: ["2"], f:["3"]}}], field6: [{value3: {g: ["1"], h: ["2"], i: ["3"]}}]}}); + test:assertEquals(toXml(check v2), xmlValue); xmlValue = xml `22123233123`; v2 = parseAsType(xmlValue); @@ -144,6 +161,9 @@ function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord4() retur test:assertEquals((v2).message(), "'value2' occurs less than the min required times"); } +@Name { + value: "Root" +} type XsdChoiceArrayWithXmlValue5 record {| @Choice { minOccurs: 2, @@ -165,10 +185,12 @@ function testXsdChoiceArrayWithXmlValue5() returns error? { xmlValue = xml `1311.114`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue5: {age: [13, 14], salary: [11.1]}}); + test:assertEquals(toXml(check v), xml `131411.1`); xmlValue = xml `1311.1`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XsdChoiceArrayWithXmlValue5: {age: [13], salary: [11.1]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `1311.11414.11515.11515.1`; v = parseAsType(xmlValue); @@ -181,6 +203,9 @@ function testXsdChoiceArrayWithXmlValue5() returns error? { test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue5' occurs less than the min required times"); } +@Name { + value: "Root" +} type XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6 record { @Choice { minOccurs: 2, @@ -210,4 +235,5 @@ function testXSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6() retur xml xmlValue = xml `1111111111111111`; XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_1: {field1: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field2: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1", "1"]}}]}, choice_XSDChoiceArrayWithXmlValueXsdChoiceArrayWithXmlValueRecord6_2: {field4: [{value1: {a: ["1", "1"]}}, {value1: {a:["1", "1"]}}], field5: [{value2: {d: ["1", "1"]}}, {value2: {d: ["1","1"]}}]}}); + test:assertEquals(toXml(check v2), xml `1111111111111111`); } diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal index 07f3ef02..89268ea4 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal @@ -1,5 +1,8 @@ import ballerina/test; +@Name { + value: "Root" +} type XsdChoiceWithElementAnnotationWithXmlValue record { @Choice { minOccurs: 0, @@ -16,6 +19,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml ``; v = parseAsType(xmlValue); @@ -28,6 +32,8 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); + // TODO: Empty element + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABABABABAB`; v = parseAsType(xmlValue); @@ -77,6 +83,9 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { test:assertEquals((v).message(), "'seq_EA1' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type XsdChoiceWithElementAnnotationWithXmlValue2 record { @Choice { minOccurs: 0, @@ -103,6 +112,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -151,6 +161,9 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); } +@Name { + value: "Root" +} type XsdChoiceWithElementAnnotationWithXmlValue3 record { @Choice { minOccurs: 1, @@ -213,4 +226,5 @@ function testXsdChoiceWithElementAnnotationWithXmlValue3() returns error? { xmlValue = xml `ABCABCABCABCABCABCABCABCABC`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdChoiceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["ABC"]}}, {value1: {a: ["ABC", "ABC"]}}, {value1: {a: ["ABC", "ABC", "ABC"]}}]}, seq_XsdChoiceWithElementAnnotationWithXmlValue3_2: {field5: [{value2: {d: "ABC"}}, {value2: {d: "ABC"}}, {value2: {d: "ABC"}}]}}); + test:assertEquals(toXml(check v2), xmlValue); } From ed315337f968d712b08526d3986f0777ec520ab8 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Sun, 17 Nov 2024 21:51:57 +0530 Subject: [PATCH 42/58] Add toxml test for choice with element annotations --- ...choice_test_with_element_annotation_with_parse_type.bal | 7 ++++++- .../tests/xsd_sequence_array_test_with_parse_type.bal | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal index 89268ea4..52439e06 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal @@ -32,7 +32,6 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1":{"EA2": "ABC"}}); - // TODO: Empty element test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABABABABAB`; @@ -43,6 +42,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1":{EA3: ["AB", "AB"]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); @@ -61,6 +61,7 @@ function testXsdChoiceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABC`; v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA1: {EA1: ["ABC"]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); @@ -113,18 +114,22 @@ function testXsdChoiceWithElementAnnotationWithXmlValue2() returns error? { v = parseAsType(xmlValue); test:assertEquals(v, {seq_EA2: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "CD"]}}}); test:assertEquals(toXml(check v), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2": {EA: {EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index f4e891f4..f85b5400 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -176,9 +176,12 @@ function testXSDSequenceArrayWithXmlValueRecord4() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'value3' occurs more than the max allowed times"); +} - xmlValue = xml `123123123123123123123`; - v2 = parseAsType(xmlValue); +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXSDSequenceArrayWithXmlValueRecord4P2() returns error? { + xml xmlValue = xml `123123123123123123123`; + XSDSequenceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceArrayWithXmlValueRecord13_2'"); From 25170dccede592db378bc7329a025e10f2d0ad93 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 18 Nov 2024 09:56:25 +0530 Subject: [PATCH 43/58] Add license headers for xsd related files --- .../resources/xsd_tests/schemas/schema_1.xsd | 2 +- .../resources/xsd_tests/schemas/schema_2.xsd | 2 +- .../resources/xsd_tests/schemas/schema_3.xsd | 2 +- .../resources/xsd_tests/schemas/schema_4.xsd | 2 +- .../resources/xsd_tests/schemas/schema_5.xsd | 2 +- .../xml_values/schema_1_invalid_xml.xml | 2 +- .../xml_values/schema_1_valid_xml.xml | 2 +- .../xml_values/schema_2_invalid_xml.xml | 2 +- .../xml_values/schema_2_valid_xml.xml | 2 +- .../xml_values/schema_3_invalid_xml.xml | 2 +- .../xml_values/schema_3_valid_xml.xml | 2 +- .../xml_values/schema_4_invalid_xml.xml | 2 +- .../xml_values/schema_4_valid_xml.xml | 2 +- .../xml_values/schema_5_invalid_xml.xml | 2 +- .../xml_values/schema_5_valid_xml.xml | 2 +- ballerina/tests/toXml_test.bal | 18 +++++++-------- ballerina/tests/xsd_choice_array_test.bal | 16 +++++++++++++ .../xsd_choice_array_test_with_parse_type.bal | 16 +++++++++++++ ballerina/tests/xsd_choice_test.bal | 16 +++++++++++++ ...sd_choice_test_with_element_annotation.bal | 16 +++++++++++++ ...ith_element_annotation_with_parse_type.bal | 16 +++++++++++++ .../tests/xsd_choice_test_with_parse_type.bal | 16 +++++++++++++ .../xsd_choice_with_name_annotations.bal | 16 +++++++++++++ ..._with_name_annotations_with_parse_type.bal | 16 +++++++++++++ ballerina/tests/xsd_element_test.bal | 18 ++++++++++++++- .../xsd_element_test_with_parse_type.bal | 18 ++++++++++++++- ballerina/tests/xsd_sequence_array_test.bal | 16 +++++++++++++ ...sd_sequence_array_test_with_parse_type.bal | 16 +++++++++++++ ..._sequence_test_with_element_annotation.bal | 16 +++++++++++++ ...ith_element_annotation_with_parse_type.bal | 16 +++++++++++++ ...xsd_sequence_test_with_name_annotation.bal | 16 +++++++++++++ ...t_with_name_annotation_with_parse_type.bal | 16 +++++++++++++ ...equence_test_with_namespace_annotation.bal | 16 +++++++++++++ ...h_namespace_annotation_with_parse_type.bal | 16 +++++++++++++ ballerina/tests/xsd_sequence_tests.bal | 16 +++++++++++++ .../xsd_sequence_tests_with_parse_type.bal | 16 +++++++++++++ .../tests/xsd_test_with_invalid_records.bal | 16 +++++++++++++ ballerina/tests/xsd_to_xml_tests.bal | 18 ++++++++++++++- .../xsd_validation_with_file_path_tests.bal | 16 +++++++++++++ ballerina/xml_api.bal | 11 +-------- .../lib/data/xmldata/utils/DataUtils.java | 11 +++++---- .../lib/data/xmldata/utils/ToXmlUtils.java | 23 +++++++++++++++++++ .../lib/data/xmldata/utils/XsdUtils.java | 23 +++++++++++++++++++ .../lib/data/xmldata/xml/XSDValidator.java | 5 ++++ .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 23 +++++++++++++++++++ .../lib/data/xmldata/xml/xsd/ElementInfo.java | 23 +++++++++++++++++++ .../data/xmldata/xml/xsd/ModelGroupInfo.java | 23 +++++++++++++++++++ .../data/xmldata/xml/xsd/SequenceInfo.java | 23 +++++++++++++++++++ native/src/main/resources/error.properties | 2 +- 49 files changed, 546 insertions(+), 43 deletions(-) diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd index ef37fa2e..786e70b5 100644 --- a/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_1.xsd @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd index 7d48ea11..bc9fb555 100644 --- a/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_2.xsd @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd index 85d1ffe1..94f57073 100644 --- a/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_3.xsd @@ -8,4 +8,4 @@ - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd index eb70b67c..c18078c9 100644 --- a/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_4.xsd @@ -7,4 +7,4 @@ - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd b/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd index bed75459..3d4dd6a8 100644 --- a/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd +++ b/ballerina/tests/resources/xsd_tests/schemas/schema_5.xsd @@ -7,4 +7,4 @@ - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml index a83247ff..c684f571 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_invalid_xml.xml @@ -1 +1 @@ -Sample \ No newline at end of file +Sample diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml index 22ea32bc..9f606074 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_1_valid_xml.xml @@ -1 +1 @@ -Sample \ No newline at end of file +Sample diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml index 2093bc22..3ac2835b 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_invalid_xml.xml @@ -2,4 +2,4 @@ Sample Title - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml index af52a961..5f57d006 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_2_valid_xml.xml @@ -3,4 +3,4 @@ Sample Title Sample Author - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml index cf0bbd0c..646ec8bd 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_invalid_xml.xml @@ -1,4 +1,4 @@ John Doe 30 - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml index eeeb6cda..f34888a1 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_3_valid_xml.xml @@ -1,4 +1,4 @@ John Doe 30 - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml index 5dfb6647..110e9428 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_invalid_xml.xml @@ -1,4 +1,4 @@ Toyota Yamaha - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml index ff16f172..81605952 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_4_valid_xml.xml @@ -1,3 +1,3 @@ Toyota - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml index 86a091b5..e50c87f2 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_invalid_xml.xml @@ -1,3 +1,3 @@ 12345 - \ No newline at end of file + diff --git a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml index 8310ec08..98e265fc 100644 --- a/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml +++ b/ballerina/tests/resources/xsd_tests/xml_values/schema_5_valid_xml.xml @@ -1,4 +1,4 @@ 12345 Laptop - \ No newline at end of file + diff --git a/ballerina/tests/toXml_test.bal b/ballerina/tests/toXml_test.bal index 5af7c067..47694791 100644 --- a/ballerina/tests/toXml_test.bal +++ b/ballerina/tests/toXml_test.bal @@ -45,20 +45,20 @@ function testToXmlBasic2() returns error? { groups: ["toXml"] } function testToXmlBasic3() returns error? { - // xml xmlval1 = xml `12`; - // Data3 rec1 = check parseAsType(xmlval1); - // xml result1 = check toXml(rec1); - // test:assertTrue(result1 == xmlval1); + xml xmlval1 = xml `12`; + Data3 rec1 = check parseAsType(xmlval1); + xml result1 = check toXml(rec1); + test:assertTrue(result1 == xmlval1); - // xml xmlVal2 = xml `12`; - // Data4 rec2 = check parseAsType(xmlVal2); - // xml result2 = check toXml(rec2); - // test:assertTrue(result2 == xmlVal2); + xml xmlVal2 = xml `12`; + Data4 rec2 = check parseAsType(xmlVal2); + xml result2 = check toXml(rec2); + test:assertTrue(result2 == xmlVal2); xml xmlVal3 = xml `123`; Data5 rec3 = check parseAsType(xmlVal3); xml result3 = check toXml(rec3); - test:assertEquals(result3, xmlVal3); + test:assertTrue(result3 == xmlVal3); } @test:Config { diff --git a/ballerina/tests/xsd_choice_array_test.bal b/ballerina/tests/xsd_choice_array_test.bal index 78a424d8..7899db5b 100644 --- a/ballerina/tests/xsd_choice_array_test.bal +++ b/ballerina/tests/xsd_choice_array_test.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdChoiceArray record {| diff --git a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal index 21c1bd2a..f8abc0fb 100644 --- a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_choice_test.bal b/ballerina/tests/xsd_choice_test.bal index 43555270..24509d64 100644 --- a/ballerina/tests/xsd_choice_test.bal +++ b/ballerina/tests/xsd_choice_test.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XSDChoiceRecord record {| diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation.bal b/ballerina/tests/xsd_choice_test_with_element_annotation.bal index 5b9a3da4..eef7ed88 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdChoiceWithElementAnnotation record { diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal index 52439e06..c37f4d21 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index 0ca2bea8..68926203 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_choice_with_name_annotations.bal b/ballerina/tests/xsd_choice_with_name_annotations.bal index b3e4c953..5b796a14 100644 --- a/ballerina/tests/xsd_choice_with_name_annotations.bal +++ b/ballerina/tests/xsd_choice_with_name_annotations.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdChoiceWithNameAnnotation record { diff --git a/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal index 0246583c..3cdfb6e9 100644 --- a/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_with_name_annotations_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdChoiceWithNameAnnotationWithXmlValue record { diff --git a/ballerina/tests/xsd_element_test.bal b/ballerina/tests/xsd_element_test.bal index 844f017d..08f06976 100644 --- a/ballerina/tests/xsd_element_test.bal +++ b/ballerina/tests/xsd_element_test.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type ElementRecord record { @@ -203,4 +219,4 @@ function testXsdElement4() returns error? { rec = parseString(xmlStr); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'firstName' occurs more than the max allowed times"); -} \ No newline at end of file +} diff --git a/ballerina/tests/xsd_element_test_with_parse_type.bal b/ballerina/tests/xsd_element_test_with_parse_type.bal index 27db56f9..b5bb722d 100644 --- a/ballerina/tests/xsd_element_test_with_parse_type.bal +++ b/ballerina/tests/xsd_element_test_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type ElementRecordWithXmlValue record { @@ -203,4 +219,4 @@ function testXsdElementWithXmlValue4() returns error? { rec = parseAsType(xmlStr); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'firstName' occurs more than the max allowed times"); -} \ No newline at end of file +} diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 09c9835e..0d2bd8b3 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdSequenceArray record {| diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index f85b5400..862e0a35 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index 5187f3a4..6ef734d0 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; // TODO: Add tests with attributes diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal index 4f0b2672..ecd23804 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal index c6856a84..ef40c5cb 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdSequenceWithNameAnnotation record { diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal index 02abb02c..9984e628 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdSequenceWithNameAnnotationWithXmlValue record { diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal index 5d5f5605..a6eacf6c 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdSequenceWithNamespaceAnnotation record { diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal index 1bd34968..03ee89d4 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XsdSequenceWithNamespaceAnnotationWithXmlValue record { diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index c980a21f..d4a6b1ae 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XSDSequenceRecord record {| diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index 6ff3a7a8..1586da42 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { diff --git a/ballerina/tests/xsd_test_with_invalid_records.bal b/ballerina/tests/xsd_test_with_invalid_records.bal index 50833f8f..301222fd 100644 --- a/ballerina/tests/xsd_test_with_invalid_records.bal +++ b/ballerina/tests/xsd_test_with_invalid_records.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; type XSDSequenceInvalidRecord record {| diff --git a/ballerina/tests/xsd_to_xml_tests.bal b/ballerina/tests/xsd_to_xml_tests.bal index c4f0e6b6..ee0ec5b9 100644 --- a/ballerina/tests/xsd_to_xml_tests.bal +++ b/ballerina/tests/xsd_to_xml_tests.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; @Name { @@ -232,4 +248,4 @@ function testToXmlWithXsdProvider() returns [typedesc, record{}, xml][ xml `123123123123123123` ] ]; -} \ No newline at end of file +} diff --git a/ballerina/tests/xsd_validation_with_file_path_tests.bal b/ballerina/tests/xsd_validation_with_file_path_tests.bal index 259533d6..83f39c6b 100644 --- a/ballerina/tests/xsd_validation_with_file_path_tests.bal +++ b/ballerina/tests/xsd_validation_with_file_path_tests.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2023, 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; import ballerina/io; diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index fc8d0ff1..b8d7c16a 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -32,17 +32,8 @@ public type ParticleOccurrence record {| # Defines the configuration for an XML element in the XML schema (XSD). public type ElementConfig record {| *ParticleOccurrence; - # Represents the `form` attribute in the XML schema (XSD) `element` definition. - "qualified"|"unqulified" form = "qualified"; - # Specifies the id of the substitution group for the element. - string substitutionGroupId?; - # Indicates whether the element is abstract. - boolean 'abstract = false; - # Restricts how the element can be extended or restricted. - "extension"|"restriction"|"all"|"substitution" block?; - # Controls whether the element can be further extended or restricted. - "extension"|"restriction"|"all" 'final?; |}; + # Annotation to define schema rules for an XML element in Ballerina. public const annotation ElementConfig Element on type, record field; 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 3c5a2398..935fdc21 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 @@ -227,7 +227,7 @@ public static QualifiedName getFieldNameFromRecord(Map fieldAnn } @SuppressWarnings("unchecked") - public static String getModifiedName(Map fieldAnnotation, String attributeName) { + static String getModifiedName(Map fieldAnnotation, String attributeName) { for (BString key : fieldAnnotation.keySet()) { if (isNameAnnotationKey(key.getValue())) { return ((Map) fieldAnnotation.get(key)).get(Constants.VALUE).toString(); @@ -463,10 +463,6 @@ public static Object getModifiedRecord(BMap input, BString text return input; } -// public static Object convertJsonIntoXml(Object value, BMap jsonOptions, BTypedesc inputType) { -// -// } - @SuppressWarnings("unchecked") private static BMap processArrayValue(BMap input, ArrayType arrayType) { Type elementType = TypeUtils.getReferredType(arrayType.getElementType()); @@ -1258,6 +1254,11 @@ public static BMap getXmlElementModelGroupMap(BTypedesc typed) return xmlModelGroupMap; } + /** + * Holds data required for the processing. + * + * @since 1.1.0 + */ public static class XmlAnalyzerMetaData { public Stack> attributeHierarchy = new Stack<>(); public Stack> arrayIndexes = new Stack<>(); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index cd63b568..e4a58b6b 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -1,3 +1,21 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.utils; import io.ballerina.runtime.api.PredefinedTypes; @@ -27,6 +45,11 @@ import java.util.Map; import java.util.Optional; +/** + * A util class for the record to xml implementation. + * + * @since 1.1.0 + */ public class ToXmlUtils { private static final BString XMLNS_NAMESPACE_URI = StringUtils.fromString("http://www.w3.org/2000/xmlns/"); private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attribute_"); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java index 2867d629..8f3f8bf2 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/XsdUtils.java @@ -1,3 +1,21 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.utils; import io.ballerina.lib.data.xmldata.xml.QualifiedName; @@ -18,6 +36,11 @@ import static io.ballerina.lib.data.xmldata.utils.DataUtils.XmlAnalyzerMetaData; import static io.ballerina.lib.data.xmldata.utils.DataUtils.popMappingTypeStacks; +/** + * A util class for the xsd validation. + * + * @since 1.1.0 + */ public class XsdUtils { public static void initializeXsdInformation(RecordType recordType, XmlAnalyzerMetaData parserData) { BMap annotations = recordType.getAnnotations(); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java index 6d9a1d79..ffe02a58 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java @@ -39,6 +39,11 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +/** + * Represent native APIS for validating XML against a XSD. + * + * @since 1.1.0 + */ public class XSDValidator { public static boolean validate(Object xsd, BXml xml) { if (xsd instanceof BString) { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index 0d708109..e7d48961 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -1,3 +1,21 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; @@ -14,6 +32,11 @@ import java.util.Set; import java.util.Stack; +/** + * Represent xsd choice. + * + * @since 1.1.0 + */ public class ChoiceInfo implements ModelGroupInfo { private final Stack> xmlElementInfo; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index 3a85b2d9..6139c406 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -1,3 +1,21 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; @@ -6,6 +24,11 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +/** + * Represent xsd element. + * + * @since 1.1.0 + */ public class ElementInfo { String name; String fieldName; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index 07806e5d..ca6e44d5 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -1,5 +1,28 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.xml.xsd; +/** + * Represent the xsd model group. + * + * @since 1.1.0 + */ public interface ModelGroupInfo { void validate(); void visit(String element, boolean isStartElement); diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index 2a7f3664..b711cda3 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -1,3 +1,21 @@ +/* + * 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. + */ + package io.ballerina.lib.data.xmldata.xml.xsd; import io.ballerina.lib.data.xmldata.utils.Constants; @@ -14,6 +32,11 @@ import java.util.Map; import java.util.Stack; +/** + * Represent xsd sequence. + * + * @since 1.1.0 + */ public class SequenceInfo implements ModelGroupInfo { public String fieldName; public long minOccurs; diff --git a/native/src/main/resources/error.properties b/native/src/main/resources/error.properties index 07df7758..cbe8da44 100644 --- a/native/src/main/resources/error.properties +++ b/native/src/main/resources/error.properties @@ -102,4 +102,4 @@ error.invalid.choice.annotation=\ Cannot include Choice annotation into ''{0}'' of type ''{1}'' error.invalid.xsd.annotation=\ - Cannot include xsd annotation into ''{0}'' of type ''{1}'' \ No newline at end of file + Cannot include xsd annotation into ''{0}'' of type ''{1}'' From 2ba6216df6b2fbdbd2073af06f29026b31d373f7 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 18 Nov 2024 11:56:59 +0530 Subject: [PATCH 44/58] Add tests for validate and fromJson APIs --- ballerina/tests/test_from_json.bal | 26 ++++++++++ .../tests/xsd_choice_test_with_parse_type.bal | 18 +++++++ .../xsd_sequence_tests_with_parse_type.bal | 33 ++++++++++++ .../xsd_validation_with_file_path_tests.bal | 45 +++++++++------- ballerina/xml_api.bal | 8 +-- .../xmldata/utils/DiagnosticErrorCode.java | 3 +- .../lib/data/xmldata/xml/Native.java | 2 +- .../lib/data/xmldata/xml/XSDValidator.java | 52 +++++++++++++------ native/src/main/resources/error.properties | 3 ++ 9 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 ballerina/tests/test_from_json.bal diff --git a/ballerina/tests/test_from_json.bal b/ballerina/tests/test_from_json.bal new file mode 100644 index 00000000..fdf5588c --- /dev/null +++ b/ballerina/tests/test_from_json.bal @@ -0,0 +1,26 @@ +import ballerina/test; + +@test:Config {groups: ["xsd", "to_xml"], dataProvider: fromJsonDataProvider} +function testFromJson(json value, xml expected) returns error?{ + xml|Error xmlResult = fromJson(value); + test:assertEquals(xmlResult, expected); +} + +function fromJsonDataProvider() returns [json, xml][] { + return [ + [{a: {b: 2, c: 3}}, xml `23`], + [{a: {a: 1}}, xml `1`], + [{a: {d: 4, e: {f: 5, g: "text"}}}, xml `45text`], + [{root: {nested: {value1: "example", value2: 10}}}, xml `example10`], + [{book: {title: "XML Guide", author: "John Doe", year: 2024}}, xml `XML GuideJohn Doe2024`], + [{library: {section: {book1: "Book A", book2: "Book B"}}}, xml `
Book ABook B
`], + [{person: {name: "Alice", details: {age: 30, city: "Wonderland"}}}, xml `Alice
30Wonderland
`], + [{catalog: {item: [{id: 1, name: "Item 1"}, {id: 2, name: "Item 2"}]}}, xml `1Item 12Item 2`], + [{company: {employee: {id: 1001, name: "Bob", department: "Engineering"}}}, xml `1001BobEngineering`], + [{'order: {orderId: 5001, items: {item1: "Widget", item2: "Gadget"}}}, xml `5001WidgetGadget`], + [{menu: {dish: [{name: "Pasta", price: 12.5}, {name: "Salad", price: 8.0}]}}, xml `Pasta12.5Salad8.0`], + [{report: {entries: [{date: "2024-10-01", detail: "Entry 1"}, {date: "2024-10-02", detail: "Entry 2"}]}}, xml `2024-10-01Entry 12024-10-02Entry 2`], + [{shoppingList: {items: [{item: "Apples", quantity: 5}, {item: "Bananas", quantity: 3}]}}, xml `Apples5Bananas3`], + [{conference: {session: [{topic: "AI Trends", speaker: "Dr. Smith"}, {topic: "Web 3.0", speaker: "Jane Doe"}]}}, xml `AI TrendsDr. SmithWeb 3.0Jane Doe`] + ]; +} diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index 68926203..1d66f686 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -41,26 +41,36 @@ function testXsdChoiceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {age: 10}}); test:assertEquals(toXml(check v), xmlValue); + Error? e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + test:assertEquals(e, ()); xmlValue = xml `10.5`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {salary: 10.5}}); test:assertEquals(toXml(check v), xmlValue); + e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + test:assertEquals(e, ()); xmlValue = xml `1011.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); + e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times'"); xmlValue = xml `11.111.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); + e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times'"); xmlValue = xml ``; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times"); + e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times'"); } @Name { @@ -585,19 +595,27 @@ function testXsdChoiceWithXmlValue10() returns error? { XSDChoiceWithXmlValueRecord10|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {"d": "2"}}}}); test:assertEquals(toXml(check v2), xmlValue); + Error? e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + test:assertEquals(e, ()); xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times"); + e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times'"); xmlValue = xml `122`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_2' occurs more than the max allowed times"); + e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord10_2' occurs more than the max allowed times'"); xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'value2' occurs more than the max allowed times"); + e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + test:assertEquals((e).message(), "Invalid XML found: ''value2' occurs more than the max allowed times'"); } diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index 1586da42..e84728ab 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -47,26 +47,40 @@ function testXsdSequenceWithXmlValue() returns error? { test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.salary, 11.1); test:assertEquals(toXml(check v), xmlValue); + Error? e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + test:assertTrue(e is ()); xmlValue = xml `11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); + e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + test:assertTrue(e is Error); + test:assertEquals(( e).message(), "Invalid XML found: 'Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue''"); xmlValue = xml `13`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue'"); + e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + test:assertTrue(e is Error); + test:assertEquals(( e).message(), "Invalid XML found: 'Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue''"); xmlValue = xml ``; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), ("required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML"), msg = (v).message()); + e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + test:assertTrue(e is Error); + test:assertEquals(( e).message(), "Invalid XML found: 'required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML'"); xmlValue = xml `11.113`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); + e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + test:assertTrue(e is Error); + test:assertEquals(( e).message(), "Invalid XML found: 'Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue''"); } @Name { @@ -979,34 +993,53 @@ function testXsdSequenceWithXmlValue13() returns error? { XSDSequenceRecordWithXmlValue13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); test:assertEquals(toXml(check v2), xml `123123123123123123`); + Error? e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is ()); xmlValue = xml `123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); test:assertEquals(toXml(check v2), xml `123123123`); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is ()); xmlValue = xml `123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceRecordWithXmlValue13_2'"); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is Error); + test:assertEquals((e).message(), "Invalid XML found: 'Element(s) 'field6' is not found in 'seq_XSDSequenceRecordWithXmlValue13_2''"); xmlValue = xml `123123123123121233123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is Error); + test:assertEquals((e).message(), "Invalid XML found: 'Element(s) 'f' is not found in 'value2''"); xmlValue = xml `123123123123123132`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is Error); + test:assertEquals((e).message(), "Invalid XML found: 'Element 'i' is not in the correct order in 'value3''"); xmlValue = xml `132123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is Error); + test:assertEquals((e).message(), "Invalid XML found: 'Element 'c' is not in the correct order in 'value1''"); xmlValue = xml `123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), ("Element 'field5' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue13_2'"), msg = (v2).message()); + e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + test:assertTrue(e is Error); + test:assertEquals((e).message(), "Invalid XML found: 'Element 'field5' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue13_2''"); } diff --git a/ballerina/tests/xsd_validation_with_file_path_tests.bal b/ballerina/tests/xsd_validation_with_file_path_tests.bal index 83f39c6b..e6e666cc 100644 --- a/ballerina/tests/xsd_validation_with_file_path_tests.bal +++ b/ballerina/tests/xsd_validation_with_file_path_tests.bal @@ -30,11 +30,12 @@ function testValidateSchema1() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - boolean isValid = validate(xsdPath, validXml); - test:assertTrue(isValid, msg = "Valid XML should pass validation"); + Error? e = validate(xsdPath, validXml); + test:assertTrue(e is (), msg = "Valid XML should pass validation"); - boolean isInvalid = validate(xsdPath, invalidXml); - test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); + e = validate(xsdPath, invalidXml); + test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); + test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @test:Config {groups: ["xsd"]} @@ -46,11 +47,12 @@ function testValidateSchema2() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - boolean isValid = validate(xsdPath, validXml); - test:assertTrue(isValid, msg = "Valid XML should pass validation"); + Error? e = validate(xsdPath, validXml); + test:assertTrue(e is (), msg = "Valid XML should pass validation"); - boolean isInvalid = validate(xsdPath, invalidXml); - test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); + e = validate(xsdPath, invalidXml); + test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); + test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @test:Config {groups: ["xsd"]} @@ -62,11 +64,12 @@ function testValidateSchema3() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - boolean isValid = validate(xsdPath, validXml); - test:assertTrue(isValid, msg = "Valid XML should pass validation"); + Error? e = validate(xsdPath, validXml); + test:assertTrue(e is (), msg = "Valid XML should pass validation"); - boolean isInvalid = validate(xsdPath, invalidXml); - test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); + e = validate(xsdPath, invalidXml); + test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); + test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @test:Config {groups: ["xsd"]} @@ -78,11 +81,12 @@ function testValidateSchema4() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - boolean isValid = validate(xsdPath, validXml); - test:assertTrue(isValid, msg = "Valid XML should pass validation"); + Error? e = validate(xsdPath, validXml); + test:assertTrue(e is (), msg = "Valid XML should pass validation"); - boolean isInvalid = validate(xsdPath, invalidXml); - test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); + e = validate(xsdPath, invalidXml); + test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); + test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @test:Config {groups: ["xsd"]} @@ -94,9 +98,10 @@ function testValidateSchema5() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - boolean isValid = validate(xsdPath, validXml); - test:assertTrue(isValid, msg = "Valid XML should pass validation"); + Error? e = validate(xsdPath, validXml); + test:assertTrue(e is (), msg = "Valid XML should pass validation"); - boolean isInvalid = validate(xsdPath, invalidXml); - test:assertFalse(isInvalid, msg = "Invalid XML should fail validation"); + e = validate(xsdPath, invalidXml); + test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); + test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index b8d7c16a..496fc07d 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -469,7 +469,7 @@ isolated function addNamespaces(map allNamespaces, map namespace # # + schema - A `string` representing the XSD content or a Ballerina record type representing the XSD. # + xmlValue - The XML document that needs to be validated against the schema. -# + return - Returns `true` if the XML is valid according to the schema, otherwise returns `false`. +# + return - Returns `()` if the XML is valid according to the schema, otherwise returns `Error`. # # # Examples # @@ -478,14 +478,14 @@ isolated function addNamespaces(map allNamespaces, map namespace # # `; # xml bookXml = xml `Sample`; -# boolean isValid = validate(xsdContent, bookXml); +# Error? isValid = validate(xsdContent, bookXml); # # // Using Ballerina record to represent XSD # type xsdRecord record {string name;}; -# boolean isValid = validate(xsdRecord, bookXml); +# Error? isValid = validate(xsdRecord, bookXml); # ``` public function validate(string|typedesc schema, xml xmlValue) - returns boolean = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; + returns Error? = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; public isolated function fromRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java index f48d4be3..21fa9fd7 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticErrorCode.java @@ -53,7 +53,8 @@ public enum DiagnosticErrorCode { INCORRECT_ELEMENT_ORDER("XML_ERROR_0025", "incorrect.element.order"), INVALID_SEQUENCE_ANNOTATION("XML_ERROR_0026", "invalid.sequence.annotation"), INVALID_CHOICE_ANNOTATION("XML_ERROR_0027", "invalid.choice.annotation"), - INVALID_XSD_ANNOTATION("XML_ERROR_0027", "invalid.xsd.annotation"); + INVALID_XSD_ANNOTATION("XML_ERROR_0028", "invalid.xsd.annotation"), + INVALID_XML("XML_ERROR_0029", "invalid.xml"); String diagnosticId; String messageKey; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java index 8e1f3ee7..aab06d83 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java @@ -86,7 +86,7 @@ public static Object validate(Object xsd, BXml xml) { try { return XSDValidator.validate(xsd, xml); } catch (Exception e) { - return DiagnosticLog.error(DiagnosticErrorCode.XML_PARSE_ERROR, e.getMessage()); + return DiagnosticLog.error(DiagnosticErrorCode.INVALID_XML, e.getMessage()); } } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java index ffe02a58..1b7cc2dc 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XSDValidator.java @@ -18,22 +18,28 @@ package io.ballerina.lib.data.xmldata.xml; -import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; +import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; +import io.ballerina.lib.data.xmldata.utils.ModuleUtils; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.api.values.BXml; import org.w3c.dom.Document; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import java.io.File; +import java.io.IOException; import java.io.StringReader; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; @@ -45,14 +51,23 @@ * @since 1.1.0 */ public class XSDValidator { - public static boolean validate(Object xsd, BXml xml) { - if (xsd instanceof BString) { - return validateXmlFromXsdFile(xsd.toString(), xml); + private static final String SOURCE_OPTIONS = "SourceOptions"; + private static final String CONTENT_FIELD = "#content"; + private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attributePrefix"); + private static final BString TEXT_FIELD_NAME = StringUtils.fromString("textFieldName"); + public static Object validate(Object xsd, BXml xml) throws ParserConfigurationException, IOException, SAXException { + try { + if (xsd instanceof BString) { + return validateXmlFromXsdFile(xsd.toString(), xml); + } + return validateXsdFromXsdRecord((BTypedesc) xsd, xml); + } catch (Exception e) { + throw e; } - return validateXsdFromXsdRecord((BTypedesc) xsd, xml); } - private static boolean validateXmlFromXsdFile(String xsdFilePath, BXml xml) { + private static Object validateXmlFromXsdFile(String xsdFilePath, BXml xml) + throws ParserConfigurationException, IOException, SAXException { try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); dbFactory.setIgnoringComments(true); // Ignore comments in the XML @@ -64,22 +79,29 @@ private static boolean validateXmlFromXsdFile(String xsdFilePath, BXml xml) { Schema schema = factory.newSchema(new File(xsdFilePath)); Validator validator = schema.newValidator(); validator.validate(source); - return true; + return null; } catch (Exception e) { - return false; + throw e; } } - private static boolean validateXsdFromXsdRecord(BTypedesc xsdRecord, BXml xml) { + private static Object validateXsdFromXsdRecord(BTypedesc xsdRecord, BXml xml) { try { - Object result = XmlTraversal.traverse(xml, - ValueCreator.createMapValue(PredefinedTypes.TYPE_STRING), xsdRecord); - if (result instanceof BError) { - return false; + Object result = XmlTraversal.traverse(xml, getDefaultSourceOptions(), xsdRecord); + if (result instanceof BError e) { + throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_XML, e.getMessage()); } - return true; } catch (Exception e) { - return false; + throw e; } + return null; + } + + private static BMap getDefaultSourceOptions() { + BMap sourceOptions = ValueCreator + .createRecordValue(ModuleUtils.getModule(), SOURCE_OPTIONS); + sourceOptions.put(ATTRIBUTE_PREFIX, StringUtils.fromString("")); + sourceOptions.put(TEXT_FIELD_NAME, StringUtils.fromString(CONTENT_FIELD)); + return sourceOptions; } } diff --git a/native/src/main/resources/error.properties b/native/src/main/resources/error.properties index cbe8da44..f155b3f1 100644 --- a/native/src/main/resources/error.properties +++ b/native/src/main/resources/error.properties @@ -103,3 +103,6 @@ error.invalid.choice.annotation=\ error.invalid.xsd.annotation=\ Cannot include xsd annotation into ''{0}'' of type ''{1}'' + +error.invalid.xml=\ + Invalid XML found: ''{0}'' From 0cd9631d8b2db14687f318158b154c58e212f164 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 18 Nov 2024 14:50:33 +0530 Subject: [PATCH 45/58] Fix issues related to java 21 --- .../java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java | 4 ++-- .../java/io/ballerina/lib/data/xmldata/xml/XmlParser.java | 2 -- .../java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index e4a58b6b..121534c4 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -18,14 +18,14 @@ package io.ballerina.lib.data.xmldata.utils; -import io.ballerina.runtime.api.PredefinedTypes; -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.ArrayType; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.utils.ValueUtils; 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 120c13cb..19e186c0 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 @@ -24,8 +24,6 @@ import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; -import io.ballerina.runtime.api.PredefinedTypes; -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.flags.SymbolFlags; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java index 88aa762d..1340ca28 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlTraversal.java @@ -24,8 +24,6 @@ import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; -import io.ballerina.runtime.api.PredefinedTypes; -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.flags.SymbolFlags; From 3c2e4e7a9529273fbcf47370eb87448688a5138a Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 18 Nov 2024 21:57:03 +0530 Subject: [PATCH 46/58] Add tests for compiler plugin tests with invalid xsd annotations --- ballerina/tests/test_from_json.bal | 14 +- .../xmldata/compiler/CompilerPluginTest.java | 40 +++++ .../sample_package_11/Ballerina.toml | 8 + .../sample_package_11/main.bal | 165 ++++++++++++++++++ .../lib/data/xmldata/compiler/Constants.java | 3 + .../compiler/XmldataDiagnosticCodes.java | 6 +- .../compiler/XmldataRecordFieldValidator.java | 68 +++++++- 7 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal diff --git a/ballerina/tests/test_from_json.bal b/ballerina/tests/test_from_json.bal index fdf5588c..2adb500a 100644 --- a/ballerina/tests/test_from_json.bal +++ b/ballerina/tests/test_from_json.bal @@ -21,6 +21,18 @@ function fromJsonDataProvider() returns [json, xml][] { [{menu: {dish: [{name: "Pasta", price: 12.5}, {name: "Salad", price: 8.0}]}}, xml `Pasta12.5Salad8.0`], [{report: {entries: [{date: "2024-10-01", detail: "Entry 1"}, {date: "2024-10-02", detail: "Entry 2"}]}}, xml `2024-10-01Entry 12024-10-02Entry 2`], [{shoppingList: {items: [{item: "Apples", quantity: 5}, {item: "Bananas", quantity: 3}]}}, xml `Apples5Bananas3`], - [{conference: {session: [{topic: "AI Trends", speaker: "Dr. Smith"}, {topic: "Web 3.0", speaker: "Jane Doe"}]}}, xml `AI TrendsDr. SmithWeb 3.0Jane Doe`] + [{conference: {session: [{topic: "AI Trends", speaker: "Dr. Smith"}, {topic: "Web 3.0", speaker: "Jane Doe"}]}}, xml `AI TrendsDr. SmithWeb 3.0Jane Doe`], + [{project: {tasks: [{title: "Setup Environment", status: "Completed"}, {title: "Develop Module", status: "In Progress"}]}}, xml `Setup EnvironmentCompletedDevelop ModuleIn Progress`], + [{school: {students: [{name: "Emily", grade: "A"}, {name: "Michael", grade: "B"}]}}, xml `EmilyAMichaelB`], + [{portfolio: {stocks: [{symbol: "AAPL", shares: 50}, {symbol: "TSLA", shares: 30}]}}, xml `AAPL50TSLA30`], + [{university: {course: [{name: "Mathematics", credits: 4}, {name: "History", credits: 3}]}}, xml `Mathematics4History3`], + [{research: {papers: [{title: "Quantum Computing", author: "Alice Cooper"}, {title: "Blockchain Advances", author: "John Smith"}]}}, xml `Quantum ComputingAlice CooperBlockchain AdvancesJohn Smith`], + [{movieCollection: {movies: [{title: "Inception", director: "Nolan"}, {title: "Interstellar", director: "Nolan"}]}}, xml `InceptionNolanInterstellarNolan`], + [{library: {books: [{title: "XML Guide", author: "John Doe", year: 2024}, {title: "JSON Primer", author: "Jane Smith", year: 2023}]}}, xml `XML GuideJohn Doe2024JSON PrimerJane Smith2023`], + [{shoppingList: {items: [{item: "Apples", quantity: 5}, {item: "Bananas", quantity: 3}]}}, xml `Apples5Bananas3`], + [{project: {tasks: [{title: "Setup Environment", status: "Completed"}, {title: "Develop Module", status: "In Progress"}]}}, xml `Setup EnvironmentCompletedDevelop ModuleIn Progress`], + [{school: {students: [{name: "Emily", grade: "A"}, {name: "Michael", grade: "B"}]}}, xml `EmilyAMichaelB`], + [{conference: {sessions: [{topic: "AI Trends", speaker: "Dr. Smith"}, {topic: "Web 3.0", speaker: "Jane Doe"}]}}, xml `AI TrendsDr. SmithWeb 3.0Jane Doe`], + [{catalog: {items: [{id: 1, name: "Item 1"}, {id: 2, name: "Item 2"}]}}, xml `1Item 12Item 2`] ]; } diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java index ef12cf53..ff974dcd 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java @@ -125,4 +125,44 @@ public void testCompilerPluginWithAProjectWithSubModule() { Assert.assertEquals(warningDiagnosticsList.get(0).diagnosticInfo().messageFormat(), "invalid annotation attachment: child record does not allow name annotation"); } + + @Test + public void testCompilerPluginWithXsdAnnotation() { + DiagnosticResult diagnosticResult = + CompilerPluginTestUtils.loadPackage("sample_package_11").getCompilation().diagnosticResult(); + List errorDiagnosticsList = diagnosticResult.diagnostics().stream() + .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) + .collect(Collectors.toList()); + Assert.assertEquals(errorDiagnosticsList.size(), 15); + Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(9).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(10).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(11).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(12).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(13).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(14).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + } } diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml new file mode 100644 index 00000000..5b1910a5 --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "admin" +name = "sample_package_11" +version = "0.1.0" +distribution = "2201.10.0" + +[build-options] +observabilityIncluded = true diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal new file mode 100644 index 00000000..c05b251e --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal @@ -0,0 +1,165 @@ +import ballerina/data.xmldata; + +type StringArr string[]; +type recordArr record{}[]; +type Str string; + +type A record { + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + int aSeq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + int[] a2Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + int|string[] a3Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + int|record{}[] a4Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + record{}[] a5Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + record{} a6Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + StringArr a7Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + recordArr a8Seq; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Str a9Seq; +}; + +type A2 record { + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + int aChoice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + int[] a2Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + int|string[] a3Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + int|record{}[] a4Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + record{}[] a5Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + record{} a6Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + StringArr a7Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + recordArr a8Choice; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + Str a9Choice; +}; + +type A4 record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArray[] seq_XsdSequenceArray; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Element { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArray[] seq_XsdSequenceArray2; + + @xmldata:Element { + minOccurs: 1, + maxOccurs: 2 + } + Seq_XsdSequenceArray[] seq_XsdSequenceArray3; + + @xmldata:Element { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Attribute + Seq_XsdSequenceArray[] seq_XsdSequenceArray4; +|}; + +type Seq_XsdSequenceArray record {| + @xmldata:Order { + value: 1 + } + int age; + + @xmldata:Order { + value: 2 + } + float salary; +|}; + diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java index b6c330e8..50dd9a50 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java @@ -6,6 +6,7 @@ * @since 0.1.0 */ public class Constants { + public static final String ELEMENT = "Element"; static final String PARSE_STRING = "parseString"; static final String PARSE_BYTES = "parseBytes"; static final String PARSE_STREAM = "parseStream"; @@ -17,4 +18,6 @@ public class Constants { static final String XMLDATA = "xmldata"; static final String BALLERINA = "ballerina"; static final String DATA_XMLDATA = "data.xmldata"; + static final String SEQUENCE = "Sequence"; + static final String CHOICE = "Choice"; } diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java index 741bf382..50ebc3b1 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java @@ -36,7 +36,11 @@ public enum XmldataDiagnosticCodes { UNSUPPORTED_TYPE("XML_ERROR_203", "unsupported type: the record field does not support the expected type", ERROR), EXPECTED_RECORD_TYPE("XML_ERROR_204", "invalid type: expected a record type", ERROR), NAME_ANNOTATION_NOT_ALLOWED("XML_ERROR_205", - "invalid annotation attachment: child record does not allow name annotation", WARNING); + "invalid annotation attachment: child record does not allow name annotation", WARNING), + INVALID_XSD_MODEL_GROUP_ANNOTATION("XML_ERROR_206", + "invalid xsd annotation: record type or record array type expected", ERROR), + INVALID_ANNOTATIONS("XML_ERROR_207", "A record field cannot contains " + + "sequence/choice/element/attribute annotations simultaneously", ERROR); private final String code; private final String message; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java index d0da173c..85c57c90 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java @@ -155,7 +155,10 @@ private void processFunctionDefinitionNode(FunctionDefinitionNode functionDefini private void validateAnnotationUsageInAllInlineExpectedTypes(TypeSymbol typeSymbol, SyntaxNodeAnalysisContext ctx) { switch (typeSymbol.typeKind()) { - case RECORD -> validateRecordFieldNames((RecordTypeSymbol) typeSymbol, ctx); + case RECORD -> { + validateRecordFieldNames((RecordTypeSymbol) typeSymbol, ctx); + validateXsdModelGroupAnnotations((RecordTypeSymbol) typeSymbol, ctx); + } case UNION -> { for (TypeSymbol memberTSymbol : ((UnionTypeSymbol) typeSymbol).memberTypeDescriptors()) { validateAnnotationUsageInAllInlineExpectedTypes(memberTSymbol, ctx); @@ -174,6 +177,7 @@ private void validateExpectedType(TypeSymbol typeSymbol, Optional loca case RECORD -> { RecordTypeSymbol recordSymbol = (RecordTypeSymbol) typeSymbol; validateRecordFieldNames(recordSymbol, ctx); + validateXsdModelGroupAnnotations(recordSymbol, ctx); processRecordFieldsType(recordSymbol, ctx); } case TYPE_REFERENCE -> validateExpectedType(((TypeReferenceTypeSymbol) typeSymbol).typeDescriptor(), @@ -264,6 +268,68 @@ private void validateRecordTypeDefinition(TypeDefinitionNode typeDefinitionNode, } TypeDefinitionSymbol typeDefinitionSymbol = (TypeDefinitionSymbol) symbol.get(); validateRecordFieldNames((RecordTypeSymbol) typeDefinitionSymbol.typeDescriptor(), ctx); + validateXsdModelGroupAnnotations((RecordTypeSymbol) typeDefinitionSymbol.typeDescriptor(), ctx); + } + + private void validateXsdModelGroupAnnotations(RecordTypeSymbol recordTypeSymbol, SyntaxNodeAnalysisContext ctx) { + for (Map.Entry entry : recordTypeSymbol.fieldDescriptors().entrySet()) { + RecordFieldSymbol fieldSymbol = entry.getValue(); + boolean isUniqueAnnotationHasValue = false; + for (AnnotationAttachmentSymbol annotationAttachmentSymbol : fieldSymbol.annotAttachments()) { + AnnotationSymbol annotationSymbol = annotationAttachmentSymbol.typeDescriptor(); + if (!isAnnotFromXmldata(annotationSymbol)) { + continue; + } + Optional annotName = annotationSymbol.getName(); + if (annotName.isEmpty()) { + continue; + } + String name = annotName.get(); + + if (isModelGroupAnnotation(name) || name.equals(Constants.ELEMENT) + || name.equals(Constants.ATTRIBUTE)) { + if (isUniqueAnnotationHasValue) { + reportDiagnosticInfo(ctx, fieldSymbol.getLocation(), + XmldataDiagnosticCodes.INVALID_ANNOTATIONS); + continue; + } + isUniqueAnnotationHasValue = true; + } + + if (!isModelGroupAnnotation(name)) { + continue; + } + validateXsdModelGroupAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); + } + } + } + + private boolean isModelGroupAnnotation(String annotationName) { + return annotationName.equals(Constants.SEQUENCE) || annotationName.equals(Constants.CHOICE); + } + + private void validateXsdModelGroupAnnotation(TypeSymbol typeSymbol, Optional location, + SyntaxNodeAnalysisContext ctx) { + if (typeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + validateXsdModelGroupAnnotation(((TypeReferenceTypeSymbol) typeSymbol).typeDescriptor(), location, ctx); + return; + } + + if (typeSymbol.typeKind() == TypeDescKind.RECORD) { + return; + } + + if (typeSymbol.typeKind() == TypeDescKind.ARRAY) { + TypeSymbol memberTypeSymbol = ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor(); + if (memberTypeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + memberTypeSymbol = ((TypeReferenceTypeSymbol) memberTypeSymbol).typeDescriptor(); + } + if (memberTypeSymbol.typeKind() == TypeDescKind.RECORD) { + return; + } + } + + reportDiagnosticInfo(ctx, location, XmldataDiagnosticCodes.INVALID_XSD_MODEL_GROUP_ANNOTATION); } private void validateRecordFieldNames(RecordTypeSymbol recordTypeSymbol, SyntaxNodeAnalysisContext ctx) { From 98412afae259c3a9cb78876b74dd18e75711d2ea Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Mon, 18 Nov 2024 23:45:03 +0530 Subject: [PATCH 47/58] Add compiler validation for sequence annotation --- .../xmldata/compiler/CompilerPluginTest.java | 44 +++++++ .../sample_package_1/sample.bal | 16 +++ .../sample_package_10/main.bal | 16 +++ .../sample_package_11/Ballerina.toml | 2 +- .../sample_package_11/main.bal | 16 +++ .../sample_package_12/Ballerina.toml | 8 ++ .../sample_package_12/main.bal | 80 +++++++++++++ .../sample_package_13/Ballerina.toml | 8 ++ .../sample_package_13/main.bal | 107 ++++++++++++++++++ .../sample_package_2/sample.bal | 16 +++ .../sample_package_3/sample.bal | 16 +++ .../sample_package_4/sample.bal | 16 +++ .../sample_package_5/sample.bal | 16 +++ .../sample_package_6/sample.bal | 16 +++ .../sample_package_7/main.bal | 16 +++ .../sample_package_8/main.bal | 16 +++ .../sample_package_9/main.bal | 16 +++ .../lib/data/xmldata/compiler/Constants.java | 1 + .../compiler/XmldataDiagnosticCodes.java | 6 +- .../compiler/XmldataRecordFieldValidator.java | 66 ++++++++++- 20 files changed, 491 insertions(+), 7 deletions(-) create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/Ballerina.toml create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/main.bal create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/Ballerina.toml create mode 100644 compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java index ff974dcd..34875c75 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java @@ -165,4 +165,48 @@ public void testCompilerPluginWithXsdAnnotation() { Assert.assertEquals(errorDiagnosticsList.get(14).diagnosticInfo().messageFormat(), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); } + + @Test + public void testCompilerPluginWithXsdAnnotation2() { + DiagnosticResult diagnosticResult = + CompilerPluginTestUtils.loadPackage("sample_package_12").getCompilation().diagnosticResult(); + List errorDiagnosticsList = diagnosticResult.diagnostics().stream() + .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) + .collect(Collectors.toList()); + Assert.assertEquals(errorDiagnosticsList.size(), 6); + Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + } + + @Test + public void testCompilerPluginWithXsdAnnotation3() { + DiagnosticResult diagnosticResult = + CompilerPluginTestUtils.loadPackage("sample_package_13").getCompilation().diagnosticResult(); + List errorDiagnosticsList = diagnosticResult.diagnostics().stream() + .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) + .collect(Collectors.toList()); + Assert.assertEquals(errorDiagnosticsList.size(), 6); + Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + "Invalid sequence member: Order should be defined in in all fields"); + Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + "Invalid sequence member: Sequence members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + "Invalid sequence member: Order should be defined in in all fields"); + Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + "Invalid sequence member: Sequence members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + "Invalid sequence member: Order should be defined in in all fields"); + Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + "Invalid sequence member: Order should be defined in in all fields"); + } } diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_1/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_1/sample.bal index e29de78d..cd278e46 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_1/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_1/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_10/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_10/main.bal index 393821c9..fbbaae2f 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_10/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_10/main.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml index 5b1910a5..89e123d6 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/Ballerina.toml @@ -1,5 +1,5 @@ [package] -org = "admin" +org = "ballerina" name = "sample_package_11" version = "0.1.0" distribution = "2201.10.0" diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal index c05b251e..c9620ed0 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; type StringArr string[]; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/Ballerina.toml b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/Ballerina.toml new file mode 100644 index 00000000..8beac1d6 --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "ballerina" +name = "sample_package_12" +version = "0.1.0" +distribution = "2201.10.0" + +[build-options] +observabilityIncluded = true diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/main.bal new file mode 100644 index 00000000..abd2c183 --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_12/main.bal @@ -0,0 +1,80 @@ +// 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; +import ballerina/data.xmldata; + +type XSDSequenceInvalidRecord record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + int a; +|}; + +type XSDSequenceInvalidRecord2 record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + int[] a; +|}; + +type StringArr string[][]; + +type XSDSequenceInvalidRecord3 record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + StringArr a; +|}; + +@test:Config {groups: ["xsd", "xsd_sequence"]} +function testXsdSequenceWithInvalidRecord() returns xmldata:Error? { + string xmlStr = string `1311.1`; + XSDSequenceInvalidRecord _ = check xmldata:parseString(xmlStr); +} + +type XSDChoiceInvalidRecord record {| + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + int a; +|}; + +type XSDChoiceInvalidRecord2 record {| + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + int[] a; +|}; + +type XSDChoiceInvalidRecord3 record {| + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + StringArr a; +|}; + +@test:Config {groups: ["xsd", "xsd_choice"]} +function testXsdChoiceWithInvalidRecord() returns xmldata:Error? { + string xmlStr = string `1311.1`; + XSDChoiceInvalidRecord _ = check xmldata:parseString(xmlStr); +} diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/Ballerina.toml b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/Ballerina.toml new file mode 100644 index 00000000..e0acc36f --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "ballerina" +name = "sample_package_13" +version = "0.1.0" +distribution = "2201.10.0" + +[build-options] +observabilityIncluded = true diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal new file mode 100644 index 00000000..9f809139 --- /dev/null +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal @@ -0,0 +1,107 @@ +// 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/data.xmldata; + +type XSDSequenceRecord record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord seq_XSDSequenceRecord; +|}; + +type Seq_XSDSequenceRecord record {| + @xmldata:Order { + value: 1 + } + int age; + + @xmldata:Order { + value: 2 + } + float salary; +|}; + +type XSDSequenceRecord2 record {| + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord2 seq_XSDSequenceRecord2; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord3 seq_XSDSequenceRecord3; + + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord4 seq_XSDSequenceRecord4; +|}; + +type Seq_XSDSequenceRecord2 record {| + @xmldata:Order { + value: 1 + } + int age; + + @xmldata:Order { + value: 2 + } + float salary; + string name; +|}; + +type Seq_XSDSequenceRecord3 record { + @xmldata:Order { + value: 1 + } + int age; + string name; + + @xmldata:Order { + value: 2 + } + float salary; +}; + +type XSDSequenceRecord4 record {| + record { + @xmldata:Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecord4 seq_XSDSequenceRecord; + } nested; +|}; + +type Seq_XSDSequenceRecord4 record { + int age; + @xmldata:Order { + value: 1 + } + int age2; + + @xmldata:Order { + value: 2 + } + float salary; + string address; +}; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_2/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_2/sample.bal index 338506c4..ce750100 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_2/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_2/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_3/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_3/sample.bal index 03694bef..59c4b527 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_3/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_3/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_4/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_4/sample.bal index 2916c14d..555f233c 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_4/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_4/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_5/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_5/sample.bal index 222828dd..030b713e 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_5/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_5/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_6/sample.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_6/sample.bal index 393821c9..fbbaae2f 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_6/sample.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_6/sample.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; @xmldata:Name { diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_7/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_7/main.bal index 0ec56b86..8db31fc5 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_7/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_7/main.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; string xmlStr = string `12`; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_8/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_8/main.bal index e58dd0d8..de3ecd2c 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_8/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_8/main.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; string xmlStr = string `12`; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_9/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_9/main.bal index cb6dbaa7..1502b4a0 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_9/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_9/main.bal @@ -1,3 +1,19 @@ +// 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/data.xmldata; string xmlStr = string `12`; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java index 50dd9a50..4cd968f5 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java @@ -20,4 +20,5 @@ public class Constants { static final String DATA_XMLDATA = "data.xmldata"; static final String SEQUENCE = "Sequence"; static final String CHOICE = "Choice"; + static final String ORDER = "Order"; } diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java index 50ebc3b1..d90a3460 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java @@ -39,7 +39,11 @@ public enum XmldataDiagnosticCodes { "invalid annotation attachment: child record does not allow name annotation", WARNING), INVALID_XSD_MODEL_GROUP_ANNOTATION("XML_ERROR_206", "invalid xsd annotation: record type or record array type expected", ERROR), - INVALID_ANNOTATIONS("XML_ERROR_207", "A record field cannot contains " + + INVALID_SEQUENCE_TYPE("XML_ERROR_207", + "Invalid sequence member: Order should be defined in in all fields", ERROR), + INVALID_SEQUENCE_REST_TYPE("XML_ERROR_207", + "Invalid sequence member: Sequence members should be defined in a closed record", ERROR), + INVALID_ANNOTATIONS("XML_ERROR_208", "A record field cannot contains " + "sequence/choice/element/attribute annotations simultaneously", ERROR); private final String code; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java index 85c57c90..e74d4a54 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java @@ -291,15 +291,71 @@ private void validateXsdModelGroupAnnotations(RecordTypeSymbol recordTypeSymbol, if (isUniqueAnnotationHasValue) { reportDiagnosticInfo(ctx, fieldSymbol.getLocation(), XmldataDiagnosticCodes.INVALID_ANNOTATIONS); - continue; + } else { + isUniqueAnnotationHasValue = true; } - isUniqueAnnotationHasValue = true; } - if (!isModelGroupAnnotation(name)) { - continue; + if (isModelGroupAnnotation(name)) { + validateXsdModelGroupAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); + } + + if (name.equals(Constants.SEQUENCE)) { + validateSequenceAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); + } + } + } + } + + private void validateSequenceAnnotation(TypeSymbol typeSymbol, + Optional location, SyntaxNodeAnalysisContext ctx) { + RecordTypeSymbol recordTypeSymbol = null; + if (typeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + validateSequenceAnnotation(((TypeReferenceTypeSymbol) typeSymbol).typeDescriptor(), location, ctx); + return; + } + + if (typeSymbol.typeKind() == TypeDescKind.RECORD) { + recordTypeSymbol = (RecordTypeSymbol) typeSymbol; + } + + if (typeSymbol.typeKind() == TypeDescKind.ARRAY) { + TypeSymbol memberTypeSymbol = ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor(); + if (memberTypeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + memberTypeSymbol = ((TypeReferenceTypeSymbol) memberTypeSymbol).typeDescriptor(); + } + if (memberTypeSymbol.typeKind() == TypeDescKind.RECORD) { + recordTypeSymbol = (RecordTypeSymbol) memberTypeSymbol; + } + } + + if (recordTypeSymbol != null) { + Optional loctaion = recordTypeSymbol.getLocation(); + recordTypeSymbol.restTypeDescriptor().ifPresent(restTypeSymbol -> { + reportDiagnosticInfo(ctx, loctaion, XmldataDiagnosticCodes.INVALID_SEQUENCE_REST_TYPE); + }); + + for (Map.Entry entry : recordTypeSymbol.fieldDescriptors().entrySet()) { + RecordFieldSymbol fieldSymbol = entry.getValue(); + if (fieldSymbol.annotAttachments().isEmpty()) { + reportDiagnosticInfo(ctx, + fieldSymbol.getLocation(), XmldataDiagnosticCodes.INVALID_SEQUENCE_TYPE); + } + for (AnnotationAttachmentSymbol annotSymbol : fieldSymbol.annotAttachments()) { + AnnotationSymbol annotationSymbol = annotSymbol.typeDescriptor(); + if (!isAnnotFromXmldata(annotationSymbol)) { + continue; + } + Optional annotName = annotationSymbol.getName(); + if (annotName.isEmpty()) { + continue; + } + String name = annotName.get(); + if (!name.equals(Constants.ORDER)) { + reportDiagnosticInfo(ctx, fieldSymbol + .getLocation(), XmldataDiagnosticCodes.INVALID_SEQUENCE_TYPE); + } } - validateXsdModelGroupAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); } } } From b7bb210ab50b4cf44e14bc8700b54b5cff3d7d5e Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 20 Nov 2024 08:48:03 +0530 Subject: [PATCH 48/58] Add testcases for toxml validations --- .../xsd_element_test_with_parse_type.bal | 168 ++++++++++++------ ...sd_sequence_array_test_with_parse_type.bal | 19 ++ ...ith_element_annotation_with_parse_type.bal | 62 +++++-- .../xmldata/compiler/CompilerPluginTest.java | 40 ++++- .../sample_package_11/main.bal | 29 +++ .../sample_package_13/main.bal | 37 ++++ .../compiler/XmldataDiagnosticCodes.java | 6 +- .../compiler/XmldataRecordFieldValidator.java | 34 ++++ .../lib/data/xmldata/utils/DataUtils.java | 120 ++++++++----- .../lib/data/xmldata/utils/ToXmlUtils.java | 156 ++++++++++------ .../lib/data/xmldata/xml/xsd/ChoiceInfo.java | 12 ++ .../lib/data/xmldata/xml/xsd/ElementInfo.java | 7 +- .../data/xmldata/xml/xsd/ModelGroupInfo.java | 3 + .../data/xmldata/xml/xsd/SequenceInfo.java | 12 ++ 14 files changed, 532 insertions(+), 173 deletions(-) diff --git a/ballerina/tests/xsd_element_test_with_parse_type.bal b/ballerina/tests/xsd_element_test_with_parse_type.bal index b5bb722d..af92fec2 100644 --- a/ballerina/tests/xsd_element_test_with_parse_type.bal +++ b/ballerina/tests/xsd_element_test_with_parse_type.bal @@ -16,6 +16,9 @@ import ballerina/test; +@Name { + value: "Root" +} type ElementRecordWithXmlValue record { @Element { maxOccurs: 1, @@ -32,15 +35,20 @@ type ElementRecordWithXmlValue record { @test:Config{groups: ["xsd", "xsd_element"]} function testXsdElementWithXmlValue() returns error? { - xml xmlStr = xml `John25`; - ElementRecordWithXmlValue|Error rec = parseAsType(xmlStr); + xml xmlValue = xml `John25`; + ElementRecordWithXmlValue|Error rec = parseAsType(xmlValue); test:assertEquals(rec, {name: "John", age: 25}); + test:assertEquals(toXml(check rec), xmlValue); - xmlStr = xml `John`; - rec = parseAsType(xmlStr); + xmlValue = xml `John`; + rec = parseAsType(xmlValue); test:assertEquals(rec, {name: "John"}); + test:assertEquals(toXml(check rec), xmlValue); } +@Name { + value: "Root" +} type ElementRecordWithXmlValue2 record { @Element { maxOccurs: 10, @@ -52,45 +60,69 @@ type ElementRecordWithXmlValue2 record { maxOccurs: 3, minOccurs: 1 } - int[] age?; + int[] age; }; @test:Config{groups: ["xsd", "xsd_element"]} function testXsdElementWithXmlValue2() returns error? { - xml xmlStr = xml `John25`; - ElementRecordWithXmlValue2|Error rec = parseAsType(xmlStr); + xml xmlValue; + ElementRecordWithXmlValue2|Error rec; + xml|Error toXmlResult; + + xmlValue = xml `John25`; + rec = parseAsType(xmlValue); test:assertEquals(rec, {name: ["John"], age: [25]}); + test:assertEquals(toXml(check rec), xmlValue); - xmlStr = xml `John`; - rec = parseAsType(xmlStr); + xmlValue = xml `John`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs less than the min required times"); + toXmlResult = toXml({name: ["John"], age: []}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs less than the min required times"); - xmlStr = xml `25`; - rec = parseAsType(xmlStr); + xmlValue = xml `25`; + rec = parseAsType(xmlValue); test:assertEquals(rec, {age: [25]}); + test:assertEquals(toXml(check rec), xmlValue); - xmlStr = xml `1112131415`; - rec = parseAsType(xmlStr); - test:assertTrue(rec is error); + xmlValue = xml `1112131415`; + rec = parseAsType(xmlValue); + test:assertTrue(rec is Error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({age: [11, 12, 13, 14, 15]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `11Abc12131415`; - rec = parseAsType(xmlStr); + xmlValue = xml `11Abc12131415`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({name: ["Abc"], age: [11, 12, 13, 14, 15]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `AbcAbcAbcAbcAbc1112131415`; - rec = parseAsType(xmlStr); + xmlValue = xml `AbcAbcAbcAbcAbc1112131415`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({name: ["Abc", "Abc", "Abc", "Abc", "Abc"], age: [11, 12, 13, 14, 15]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `11AbcAbc12Abc13Abc1415`; - rec = parseAsType(xmlStr); + xmlValue = xml `11AbcAbc12Abc13Abc1415`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({name: ["Abc", "Abc", "Abc", "Abc"], age: [11, 12, 13, 14, 15]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); } +@Name { + value: "Root" +} type ElementRecordWithXmlValue3 record { record { @Element { @@ -103,13 +135,13 @@ type ElementRecordWithXmlValue3 record { maxOccurs: 3, minOccurs: 1 } - int[] age?; + int[] age; @Element { maxOccurs: 3, minOccurs: 2 } - int[] id?; + int[] id; } user; @Element { @@ -121,41 +153,59 @@ type ElementRecordWithXmlValue3 record { @test:Config{groups: ["xsd", "xsd_element"]} function testXsdElementWithXmlValue3() returns error? { - xml xmlStr; + xml xmlValue; ElementRecordWithXmlValue3|Error rec; + xml|Error toXmlResult; - xmlStr = xml `John12353`; - rec = parseAsType(xmlStr); - test:assertEquals(rec, {user: {name: ["John"], age: [35], id: [1, 2]}, status: 3}); + xmlValue = xml `John12353`; + rec = parseAsType(xmlValue); + test:assertEquals(rec, {user: {name: ["John"], id: [1, 2], age: [35]}, status: 3}); + test:assertEquals(toXml(check rec), xml `John35123`); - xmlStr = xml `John123`; - rec = parseAsType(xmlStr); + xmlValue = xml `John123`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs less than the min required times"); + toXmlResult = toXml({user: {name: ["John"], id: [1, 2], age: []}, status: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs less than the min required times"); - xmlStr = xml `12353`; - rec = parseAsType(xmlStr); - test:assertEquals(rec, {user: {age: [35], id: [1, 2]}, status: 3}); + xmlValue = xml `12353`; + rec = parseAsType(xmlValue); + test:assertEquals(rec, {user: {id: [1, 2], age: [35]}, status: 3}); + test:assertEquals(toXml(check rec), xml `35123`); - xmlStr = xml `1211131314153`; - rec = parseAsType(xmlStr); + xmlValue = xml `1211131314153`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({user: {id: [1, 2], age: [11, 13, 13, 14, 15]}, status: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `1211Abc131314153`; - rec = parseAsType(xmlStr); + xmlValue = xml `1211Abc131314153`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({user: {id: [1,2], age: [11, 13, 13, 14, 15], name: ["Abc"]}, status: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `12AbcAbcAbcAbcAbc11131314153`; - rec = parseAsType(xmlStr); + xmlValue = xml `12AbcAbcAbcAbcAbc11131314153`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({user: {id: [1,2], age: [11, 13, 13, 14, 15], name: ["Abc", "Abc", "Abc", "Abc"]}, status: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); - xmlStr = xml `1211AbcAbc13Abc13Abc14153`; - rec = parseAsType(xmlStr); + xmlValue = xml `1211AbcAbc13Abc13Abc14153`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'age' occurs more than the max allowed times"); + toXmlResult = toXml({user: {id: [1,2], age: [11, 13, 13, 14, 15], name: ["Abc", "Abc", "Abc", "Abc"]}, status: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'age' occurs more than the max allowed times"); } type ElementRecordWithXmlValue4 record { @@ -192,31 +242,47 @@ type ElementRecordWithXmlValue4 record { @test:Config{groups: ["xsd", "xsd_element"]} function testXsdElementWithXmlValue4() returns error? { - xml xmlStr = xml `JohnJaneJimDoeSmithBrownJohnJaneJimDoeSmithBrown123202530`; - ElementRecordWithXmlValue4|Error rec = parseAsType(xmlStr); + xml|Error toXmlResult; + + xml xmlValue = xml `JohnJaneJimDoeSmithBrownJohnJaneJimDoeSmithBrown123202530`; + ElementRecordWithXmlValue4|Error rec = parseAsType(xmlValue); test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3], age: [20, 25, 30]}); + test:assertEquals(toXml(check rec), xmlValue); - xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; - rec = parseAsType(xmlStr); + xmlValue = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlValue); test:assertEquals(rec, {name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + test:assertEquals(toXml(check rec), xmlValue); - xmlStr = xml `JohnJaneJimAnnaDoeSmithBrown1234202530`; - rec = parseAsType(xmlStr); + xmlValue = xml `JohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'name' occurs less than the min required times"); + toXmlResult = toXml({name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'name' occurs less than the min required times"); - xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; - rec = parseAsType(xmlStr); + xmlValue = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrown1234202530`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'name' occurs more than the max allowed times"); + toXmlResult = toXml({name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'name' occurs more than the max allowed times"); - xmlStr = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; - rec = parseAsType(xmlStr); + xmlValue = xml `JohnJaneJimAnnaDoeSmithBrownJohnJaneJimAnnaDoeSmithBrownBrownBrownBrownBrownBrownBrown1234202530`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'lastName' occurs more than the max allowed times"); + toXmlResult = toXml({name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown", "Brown", "Brown", "Brown", "Brown", "Brown", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'lastName' occurs more than the max allowed times"); - xmlStr = xml `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; - rec = parseAsType(xmlStr); + xmlValue = xml `JohnJaneJimDoeSmithBrownJohnJaneJimJimJimJimJimJimJimJimDoeSmithBrown123202530`; + rec = parseAsType(xmlValue); test:assertTrue(rec is error); test:assertEquals((rec).message(), "'firstName' occurs more than the max allowed times"); + toXmlResult = toXml({name: [{firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna"], lastName: ["Doe", "Smith", "Brown"]}, {firstName: ["John", "Jane", "Jim", "Anna", "John"], lastName: ["Doe", "Smith", "Brown"]}], status: [1, 2, 3, 4], age: [20, 25, 30]}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'firstName' occurs more than the max allowed times"); } diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 862e0a35..3f9f112e 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -43,6 +43,7 @@ type Seq_XsdSequenceArrayWithXmlValue record {| function testXsdSequenceArrayWithXmlValue() returns error? { xml xmlValue; XsdSequenceArrayWithXmlValue|Error v; + xml|Error toXmlresult; xmlValue = xml `1311.11415.1`; v = parseAsType(xmlValue); @@ -53,6 +54,9 @@ function testXsdSequenceArrayWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue' occurs more than the max allowed times"); + toXmlresult = toXml({seq_XsdSequenceArrayWithXmlValue: [{age: 13, salary: 11.1}, {age: 14, salary: 14.1}, {age: 15, salary: 15.1}]}); + test:assertTrue(toXmlresult is error); + test:assertEquals((toXmlresult).message(), "'seq_XsdSequenceArrayWithXmlValue' occurs more than the max allowed times"); xmlValue = xml `1311.114`; v = parseAsType(xmlValue); @@ -110,6 +114,7 @@ type Seq_XsdSequenceArrayWithXmlValue2_2 record {| function testXsdSequenceArrayWithXmlValue2() returns error? { xml xmlValue; XsdSequenceArrayWithXmlValue2|Error v; + xml|Error toXmlresult; xmlValue = xml `1311.11415.11311.11415.1`; v = parseAsType(xmlValue); @@ -120,11 +125,17 @@ function testXsdSequenceArrayWithXmlValue2() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue2' occurs more than the max allowed times"); + toXmlresult = toXml({seq_XsdSequenceArrayWithXmlValue2: [{age: 13, salary: 11.1}, {age: 13, salary: 11.1}, {age: 14, salary: 15.1}], seq_XsdSequenceArrayWithXmlValue2_2: [{age2: 13, salary2: 11.1}, {age2: 14, salary2: 15.1}]}); + test:assertTrue(toXmlresult is error); + test:assertEquals((toXmlresult).message(), "'seq_XsdSequenceArrayWithXmlValue2' occurs more than the max allowed times"); xmlValue = xml `1311.11415.11311.11311.11415.1`; v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue2_2' occurs more than the max allowed times"); + toXmlresult = toXml({seq_XsdSequenceArrayWithXmlValue2: [{age: 13, salary: 11.1}, {age: 14, salary: 15.1}], seq_XsdSequenceArrayWithXmlValue2_2: [{age2: 13, salary2: 11.1}, {age2: 13, salary2: 11.1}, {age2: 14, salary2: 15.1}]}); + test:assertTrue(toXmlresult is error); + test:assertEquals((toXmlresult).message(), "'seq_XsdSequenceArrayWithXmlValue2_2' occurs more than the max allowed times"); } @Name { @@ -168,6 +179,8 @@ type Seq_XSDSequenceArrayWithXmlValueRecord13_2 record { @test:Config {groups: ["xsd", "xsd_sequence"]} function testXSDSequenceArrayWithXmlValueRecord4() returns error? { + xml|Error toXmlresult; + xml xmlValue = xml `123123123123123123`; XSDSequenceArrayWithXmlValueRecord13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}]}); @@ -187,6 +200,9 @@ function testXSDSequenceArrayWithXmlValueRecord4() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'seq_XSDSequenceArrayWithXmlValueRecord13_2' occurs more than the max allowed times"); + toXmlresult = toXml({seq_XSDSequenceArrayWithXmlValueRecord13_1: [{field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}, {field1: {value1: [{a: "1", b: "2", c: "3"}]}, field2: {value2: [{d: "1", e: "2", f: "3"}]}, field3: {value3: [{g: "1", h: "2", i: "3"}]}}], seq_XSDSequenceArrayWithXmlValueRecord13_2: [{field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}]}}, {field4: {value1: [{a: "1", b: "2", c: "3"}]}, field5: {value2: [{d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}, {d: "1", e: "2", f: "3"}]}, field6: {value3: [{g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}, {g: "1", h: "2", i: "3"}]}}]}); + test:assertTrue(toXmlresult is error); + test:assertEquals((toXmlresult).message(), "'seq_XSDSequenceArrayWithXmlValueRecord13_2' occurs more than the max allowed times"); xmlValue = xml `123123123123123123123123123123123123123`; v2 = parseAsType(xmlValue); @@ -266,6 +282,9 @@ function testXsdSequenceArrayWithXmlValue5() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'seq_XsdSequenceArrayWithXmlValue5' occurs less than the min required times"); + xml|Error toXmlresult = toXml({seq_XsdSequenceArrayWithXmlValue5: [{age: 13, salary: 11.1}]}); + test:assertTrue(toXmlresult is error); + test:assertEquals((toXmlresult).message(), "'seq_XsdSequenceArrayWithXmlValue5' occurs less than the min required times"); } @Name { diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal index ecd23804..00b62a8f 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -61,6 +61,7 @@ type Seq_EA1_Xml_Value record { function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xml xmlValue; XsdSequenceWithElementAnnotationWithXmlValue|Error v; + xml|Error toXmlResult; xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); @@ -75,57 +76,70 @@ function testXsdSequenceWithElementAnnotationWithXmlValue() returns error? { xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB"]}}); - - // // TODO: Check - // test:assertEquals(toXml(check v), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCABABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA2": "ABC", EA3: ["AB", "AB", "AB"]}}); - - // // TODO: Check - // test:assertEquals(toXml(check v), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{EA3: ["AB", "AB"]}}); - - //TODO: Check and - // test:assertEquals(toXml(check v), xmlValue); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA3: ["AB", "AB", "AB", "AB", "AB", "AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA1_Xml_Value":{"EA1": "ABC", EA3: ["AB", "AB"]}}); + test:assertEquals(toXml(check v), xmlValue); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA1: "ABC", EA2: "ABC", EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA2: "ABC", EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA2: "ABC", EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `AB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_Xml_Value: {EA1: "ABC", EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); } @Name { @@ -169,7 +183,7 @@ type Seq_EA2_With_Xml_Value record { @Order { value: 3 } - string[] EA3?; + string[] EA3; } EA; }; @@ -177,16 +191,23 @@ type Seq_EA2_With_Xml_Value record { function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xml xmlValue; XsdSequenceWithElementAnnotationWithXmlValue2|Error v; + xml|Error toXmlResult; xmlValue = xml `ABCABC`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA2_With_Xml_Value: {EA: {EA1: "ABC", EA2: "ABC", EA3: []}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA2_With_Xml_Value: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["AB"]}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCABCABCD`; v = parseAsType(xmlValue); @@ -196,19 +217,25 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v, {}), xmlValue); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {"EA2": "ABC", EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v, {}), xmlValue); xmlValue = xml `ABAB`; v = parseAsType(xmlValue); test:assertEquals(v, {"seq_EA2_With_Xml_Value": {EA: {EA3: ["AB", "AB"]}}}); + test:assertEquals(toXml(check v, {}), xmlValue); xmlValue = xml `ABABABABABAB`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); + toXmlResult = toXml({seq_EA2_With_Xml_Value: {EA: {EA3: ["AB", "AB", "AB", "AB", "AB", "AB"]}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs more than the max allowed times"); xmlValue = xml `ABCABAB`; v = parseAsType(xmlValue); @@ -218,6 +245,9 @@ function testXsdSequenceWithElementAnnotationWithXmlValue2() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA2_With_Xml_Value: {EA: {EA1: "ABC", EA2: "ABC", EA3: ["CD"]}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlValue = xml `ABCAB`; v = parseAsType(xmlValue); @@ -316,20 +346,17 @@ function testXsdSequenceWithElementAnnotationWithXmlValue3() returns error? { xmlValue = xml `123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}]}}); - // TODO: Check - // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123`); + test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123`); xmlValue = xml `123123123123123123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); - // TODO: Check - // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123123123`); + test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123`); xmlValue = xml `2312312312323123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2"], c: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XsdSequenceWithElementAnnotationWithXmlValue3_2: {field4: [{value1: {a: ["1", "2", "3"], b: ["2"], c: "3"}}], field5: [{value2: {d: "1", e: "2", f: "3"}}], field6: [{value3: {g: "1", h: "2", i: "3"}}]}}); - // TODO: Check - // test:assertEquals(toXml(check v2, {}), xml `123123123123123123123123123123`); + test:assertEquals(toXml(check v2, {}), xmlValue); xmlValue = xml `33123`; v2 = parseAsType(xmlValue); @@ -355,4 +382,7 @@ function testXsdSequenceWithElementAnnotationWithXmlValue3() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'b' occurs more than the max allowed times"); + xml|Error toXmlResult = toXml({seq_XsdSequenceWithElementAnnotationWithXmlValue3_1: {field1: [{value1: {a: ["1"], b: ["2"], c: "3"}}, {value1: {a: ["1"], b: ["2", "2", "2", "2", "2"], c: "3"}}], field2: [{value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}, {value2: {d: "1", e: "2", f: "3"}}], field3: {value3: {g: "1", h: "2", i: "3"}}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'b' occurs more than the max allowed times"); } diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java index 34875c75..7ba659da 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java @@ -133,7 +133,7 @@ public void testCompilerPluginWithXsdAnnotation() { List errorDiagnosticsList = diagnosticResult.diagnostics().stream() .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); - Assert.assertEquals(errorDiagnosticsList.size(), 15); + Assert.assertEquals(errorDiagnosticsList.size(), 24); Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), @@ -143,13 +143,13 @@ public void testCompilerPluginWithXsdAnnotation() { Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), - "invalid xsd annotation: record type or record array type expected"); + "Invalid sequence member: Sequence members should be defined in a closed record"); Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), - "invalid xsd annotation: record type or record array type expected"); + "Invalid sequence member: Sequence members should be defined in a closed record"); Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), - "invalid xsd annotation: record type or record array type expected"); + "Invalid sequence member: Sequence members should be defined in a closed record"); Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(9).diagnosticInfo().messageFormat(), @@ -159,10 +159,28 @@ public void testCompilerPluginWithXsdAnnotation() { Assert.assertEquals(errorDiagnosticsList.get(11).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(12).diagnosticInfo().messageFormat(), - "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + "invalid xsd annotation: record type or record array type expected"); Assert.assertEquals(errorDiagnosticsList.get(13).diagnosticInfo().messageFormat(), - "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + "Invalid choice member: Choice members should be defined in a closed record"); Assert.assertEquals(errorDiagnosticsList.get(14).diagnosticInfo().messageFormat(), + "Invalid choice member: Choice members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(15).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(16).diagnosticInfo().messageFormat(), + "Invalid choice member: Choice members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(17).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(18).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(19).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), + "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); + Assert.assertEquals(errorDiagnosticsList.get(13).diagnosticInfo().messageFormat(), + "Invalid choice member: Choice members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); } @@ -195,7 +213,7 @@ public void testCompilerPluginWithXsdAnnotation3() { List errorDiagnosticsList = diagnosticResult.diagnostics().stream() .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); - Assert.assertEquals(errorDiagnosticsList.size(), 6); + Assert.assertEquals(errorDiagnosticsList.size(), 10); Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), "Invalid sequence member: Order should be defined in in all fields"); Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), @@ -208,5 +226,13 @@ public void testCompilerPluginWithXsdAnnotation3() { "Invalid sequence member: Order should be defined in in all fields"); Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), "Invalid sequence member: Order should be defined in in all fields"); + Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), + "Invalid sequence member: Order should be defined in in all fields"); + Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), + "Invalid choice member: Choice members should be defined in a closed record"); + Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), + "invalid xsd annotation: record type or record array type expected"); + Assert.assertEquals(errorDiagnosticsList.get(9).diagnosticInfo().messageFormat(), + "Invalid choice member: Choice members should be defined in a closed record"); } } diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal index c9620ed0..17dca1bd 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal @@ -165,6 +165,28 @@ type A4 record {| } @xmldata:Attribute Seq_XsdSequenceArray[] seq_XsdSequenceArray4; + + @xmldata:Element { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Attribute + Choice_XsdSequenceArray[] Choice_XsdSequenceArray5; + + @xmldata:Element { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 2 + } + @xmldata:Attribute + Choice_XsdSequenceArray2[] Choice_XsdSequenceArray6; |}; type Seq_XsdSequenceArray record {| @@ -179,3 +201,10 @@ type Seq_XsdSequenceArray record {| float salary; |}; +type Choice_XsdSequenceArray record { + int? age; +}; + +type Choice_XsdSequenceArray2 record {| + int? age; +|}; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal index 9f809139..34b59c03 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal @@ -66,6 +66,10 @@ type Seq_XSDSequenceRecord2 record {| value: 2 } float salary; + + @xmldata:Element { + minOccurs: 2 + } string name; |}; @@ -99,9 +103,42 @@ type Seq_XSDSequenceRecord4 record { } int age2; + @xmldata:Element { + minOccurs: 2 + } @xmldata:Order { value: 2 } float salary; + + @xmldata:Element { + minOccurs: 2 + } string address; }; + +type ChoiceRecord record { + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + record {} age; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + int salary; + + @xmldata:Choice { + minOccurs: 1, + maxOccurs: 1 + } + ChoiceRec1 choiceRec1; +}; + +type ChoiceRec1 record { + record {} age?; + int salary?; + string name?; +}; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java index d90a3460..1db0b60a 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataDiagnosticCodes.java @@ -41,9 +41,11 @@ public enum XmldataDiagnosticCodes { "invalid xsd annotation: record type or record array type expected", ERROR), INVALID_SEQUENCE_TYPE("XML_ERROR_207", "Invalid sequence member: Order should be defined in in all fields", ERROR), - INVALID_SEQUENCE_REST_TYPE("XML_ERROR_207", + INVALID_SEQUENCE_REST_TYPE("XML_ERROR_208", "Invalid sequence member: Sequence members should be defined in a closed record", ERROR), - INVALID_ANNOTATIONS("XML_ERROR_208", "A record field cannot contains " + + INVALID_CHOICE_REST_TYPE("XML_ERROR_209", + "Invalid choice member: Choice members should be defined in a closed record", ERROR), + INVALID_ANNOTATIONS("XML_ERROR_210", "A record field cannot contains " + "sequence/choice/element/attribute annotations simultaneously", ERROR); private final String code; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java index e74d4a54..e56dee18 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java @@ -303,10 +303,44 @@ private void validateXsdModelGroupAnnotations(RecordTypeSymbol recordTypeSymbol, if (name.equals(Constants.SEQUENCE)) { validateSequenceAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); } + + if (name.equals(Constants.CHOICE)) { + validateChoiceAnnotation(fieldSymbol.typeDescriptor(), fieldSymbol.getLocation(), ctx); + } } } } + private void validateChoiceAnnotation(TypeSymbol typeSymbol, + Optional location, SyntaxNodeAnalysisContext ctx) { + RecordTypeSymbol recordTypeSymbol = null; + if (typeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + validateChoiceAnnotation(((TypeReferenceTypeSymbol) typeSymbol).typeDescriptor(), location, ctx); + return; + } + + if (typeSymbol.typeKind() == TypeDescKind.RECORD) { + recordTypeSymbol = (RecordTypeSymbol) typeSymbol; + } + + if (typeSymbol.typeKind() == TypeDescKind.ARRAY) { + TypeSymbol memberTypeSymbol = ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor(); + if (memberTypeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + memberTypeSymbol = ((TypeReferenceTypeSymbol) memberTypeSymbol).typeDescriptor(); + } + if (memberTypeSymbol.typeKind() == TypeDescKind.RECORD) { + recordTypeSymbol = (RecordTypeSymbol) memberTypeSymbol; + } + } + + if (recordTypeSymbol != null) { + Optional loctaion = recordTypeSymbol.getLocation(); + recordTypeSymbol.restTypeDescriptor().ifPresent(restTypeSymbol -> { + reportDiagnosticInfo(ctx, loctaion, XmldataDiagnosticCodes.INVALID_CHOICE_REST_TYPE); + }); + } + } + private void validateSequenceAnnotation(TypeSymbol typeSymbol, Optional location, SyntaxNodeAnalysisContext ctx) { RecordTypeSymbol recordTypeSymbol = null; 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 935fdc21..178bb1b5 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 @@ -23,8 +23,10 @@ import io.ballerina.lib.data.xmldata.xml.QualifiedNameFactory; import io.ballerina.lib.data.xmldata.xml.QualifiedNameMap; import io.ballerina.lib.data.xmldata.xml.QualifiedNameSemantic; +import io.ballerina.lib.data.xmldata.xml.xsd.ChoiceInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; @@ -524,6 +526,7 @@ static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap HashMap xsdSequencePriorityOrder) { if (!xsdSequencePriorityOrder.isEmpty()) { return xsdSequencePriorityOrder.entrySet().stream() + .filter(entry -> input.containsKey(StringUtils.fromString(entry.getKey()))) .sorted(Comparator.comparingInt(Map.Entry::getValue)) .map(entry -> StringUtils.fromString(entry.getKey())) .toArray(BString[]::new); @@ -1142,25 +1145,34 @@ public static HashMap getXsdSequencePriorityOrder(Type type, bo return elementPriorityOrder; } - public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordType fieldType, + public static HashMap getFieldNamesWithModelGroupAnnotations(RecordType fieldType, HashMap elementNamesMap) { - ArrayList fieldNamesWithModelGroupAnnotations = new ArrayList<>(); + HashMap fieldNamesWithModelGroupAnnotations = new HashMap<>(); BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); if (key.contains(Constants.FIELD)) { String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + if (elementNamesMap.containsKey(fieldName)) { + fieldName = elementNamesMap.get(fieldName); + } + Map fieldAnnotation = (Map) annotations.get(annotationKey); for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) - || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - if (elementNamesMap.containsKey(fieldName)) { - fieldNamesWithModelGroupAnnotations.add(elementNamesMap.get(fieldName)); - } else { - fieldNamesWithModelGroupAnnotations.add(fieldName); - } + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + SequenceInfo sequenceInfo = new SequenceInfo(fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey), + fieldType, null); + fieldNamesWithModelGroupAnnotations.put(fieldName, sequenceInfo); + } + + if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + ChoiceInfo choiceInfo = new ChoiceInfo(fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey), + fieldType, null); + fieldNamesWithModelGroupAnnotations.put(fieldName, choiceInfo); } } } @@ -1169,6 +1181,35 @@ public static ArrayList getFieldNamesWithModelGroupAnnotations(RecordTyp return fieldNamesWithModelGroupAnnotations; } + public static HashMap getFieldNamesWithElementGroupAnnotations( + RecordType fieldType, HashMap elementNamesMap, String xmlElementName) { + HashMap fieldNamesWithElementInfoAnnotations = new HashMap<>(); + BMap annotations = fieldType.getAnnotations(); + for (BString annotationKey : annotations.getKeys()) { + String key = annotationKey.getValue(); + if (key.contains(Constants.FIELD)) { + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + if (elementNamesMap.containsKey(fieldName)) { + fieldName = elementNamesMap.get(fieldName); + } + + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { + ElementInfo elementInfo = new ElementInfo(xmlElementName, fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey)); + fieldNamesWithElementInfoAnnotations.put(fieldName, elementInfo); + } + } + } + } + } + return fieldNamesWithElementInfoAnnotations; + } + + public static ArrayList getFieldNamesWithSequenceAnnotations(RecordType fieldType, HashMap elementNamesMap) { ArrayList fieldNamesWithModelGroupAnnotations = new ArrayList<>(); @@ -1223,37 +1264,36 @@ public static HashMap getElementNameMap(Type type) { return names; } - public static BMap getXmlElementModelGroupMap(BTypedesc typed) { - Type type = TypeUtils.getReferredType(typed.getDescribingType()); - if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { - return null; - } - - BMap xmlModelGroupMap = ValueCreator - .createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_BOOLEAN)); - RecordType recordType = (RecordType) type; - BMap annotations = recordType.getAnnotations(); - for (BString annotationKey : annotations.getKeys()) { - String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) - || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - xmlModelGroupMap.put(StringUtils.fromString(fieldName), true); - } else { - xmlModelGroupMap.put(StringUtils.fromString(fieldName), false); - } - } - } - } - } - return xmlModelGroupMap; - } - +// public static BMap getXmlElementModelGroupMap(BTypedesc typed) { +// Type type = TypeUtils.getReferredType(typed.getDescribingType()); +// if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { +// return null; +// } +// +// BMap xmlModelGroupMap = ValueCreator +// .createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_BOOLEAN)); +// RecordType recordType = (RecordType) type; +// BMap annotations = recordType.getAnnotations(); +// for (BString annotationKey : annotations.getKeys()) { +// String key = annotationKey.getValue(); +// if (key.contains(Constants.FIELD)) { +// String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); +// Map fieldAnnotation = (Map) annotations.get(annotationKey); +// for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { +// String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); +// if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { +// if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) +// || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { +// xmlModelGroupMap.put(StringUtils.fromString(fieldName), true); +// } else { +// xmlModelGroupMap.put(StringUtils.fromString(fieldName), false); +// } +// } +// } +// } +// } +// return xmlModelGroupMap; +// } /** * Holds data required for the processing. * diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 121534c4..fcb7bdb9 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -18,6 +18,8 @@ package io.ballerina.lib.data.xmldata.utils; +import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; @@ -55,24 +57,34 @@ public class ToXmlUtils { private static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attribute_"); private static final BString XMLNS = StringUtils.fromString("xmlns"); - public static BXml fromRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { - Type type = typed.getDescribingType(); - Type referredType = TypeUtils.getReferredType(type); - Object rootTag = options.get(StringUtils.fromString(Constants.ROOT_TAG)); - BMap allNamespaces = getEmptyStringMap(); - BString rootTagBstring = StringUtils.fromString(rootTag == null ? Constants.EMPTY_STRING : rootTag.toString()); - - if (!isSingleRecordMember(jsonValue)) { - addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, getEmptyStringMap())); - return getElementFromRecordMember(rootTag == null ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, - traverseRecordAndGenerateXml(jsonValue, allNamespaces, - getEmptyStringMap(), options, null, referredType, false, false), - allNamespaces, options, getAttributesMap(jsonValue, options, allNamespaces, getEmptyStringMap())); - } - + public static Object fromRecordToXml(Object jsonValue, BMap options, BTypedesc typed) { try { - BMap jMap = (BMap) ValueUtils - .convert(jsonValue, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + Type type = typed.getDescribingType(); + Type referredType = TypeUtils.getReferredType(type); + Object rootTag = options.get(StringUtils.fromString(Constants.ROOT_TAG)); + BMap allNamespaces = getEmptyStringMap(); + BString rootTagBstring = + StringUtils.fromString(rootTag == null ? Constants.EMPTY_STRING : rootTag.toString()); + + if (!isSingleRecordMember(jsonValue)) { + addNamespaces(allNamespaces, getNamespacesMap(jsonValue, options, getEmptyStringMap())); + return getElementFromRecordMember( + rootTag == null ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, + traverseRecordAndGenerateXml(jsonValue, allNamespaces, + getEmptyStringMap(), options, null, referredType, + false, false, null, null), + allNamespaces, options, + getAttributesMap(jsonValue, options, allNamespaces, getEmptyStringMap())); + } + + BMap jMap = null; + try { + jMap = (BMap) ValueUtils + .convert(jsonValue, TypeCreator.createMapType(PredefinedTypes.TYPE_JSON)); + } catch (BError e) { + return jsonValue == null ? ValueCreator.createXmlValue(Constants.EMPTY_STRING) + : CreateText.createText(StringUtils.fromString(jsonValue.toString())); + } if (jMap.isEmpty()) { return ValueCreator.createXmlValue(Constants.EMPTY_STRING); @@ -83,22 +95,27 @@ public static BXml fromRecordToXml(Object jsonValue, BMap optio HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); boolean isSequenceField = sequenceFieldNames.contains(keyStr); - ArrayList modelGroupRelatedFieldNames = + HashMap modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); + HashMap elementInfoRelatedFieldNames = + getElementInfoRelatedFieldNames(referredType, elementNamesMap); String localJsonKeyPart = keyStr.contains(Constants.COLON) ? keyStr.substring(keyStr.indexOf(Constants.COLON) + 1) : keyStr; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); - boolean isContainsModelGroup = modelGroupRelatedFieldNames.contains(recordKey); + boolean isContainsModelGroup = modelGroupRelatedFieldNames.containsKey(recordKey); + ModelGroupInfo parentModelGroupInfo = modelGroupRelatedFieldNames.get(recordKey); + ElementInfo elementInfo = elementInfoRelatedFieldNames.get(recordKey); Object value = ToArray.toArray(jMap).getValues()[0]; addNamespaces(allNamespaces, getNamespacesMap(value, options, getEmptyStringMap())); if (value instanceof BArray) { return getElementFromRecordMember(rootTag == null - ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, - traverseRecordAndGenerateXml(value, allNamespaces, getEmptyStringMap(), options, key, - getChildElementType(referredType, keyStr), isSequenceField, isSequenceField), - allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); + ? StringUtils.fromString(Constants.ROOT) : rootTagBstring, traverseRecordAndGenerateXml( + value, allNamespaces, getEmptyStringMap(), options, key, getChildElementType( + referredType, keyStr), isSequenceField, isSequenceField, + parentModelGroupInfo, elementInfo), + allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); } if (key.equals(options.get(Constants.TEXT_FIELD_NAME))) { @@ -107,7 +124,8 @@ public static BXml fromRecordToXml(Object jsonValue, BMap optio BXml output = getElementFromRecordMember(key, traverseRecordAndGenerateXml(value, allNamespaces, getEmptyStringMap(), options, null, - getChildElementType(referredType, recordKey), isSequenceField, isSequenceField), + getChildElementType(referredType, recordKey), isSequenceField, + isSequenceField, parentModelGroupInfo, elementInfo), allNamespaces, options, getAttributesMap(value, options, allNamespaces, getEmptyStringMap())); if (isContainsModelGroup) { output = output.children(); @@ -117,8 +135,7 @@ public static BXml fromRecordToXml(Object jsonValue, BMap optio } return output; } catch (BError e) { - return jsonValue == null ? ValueCreator.createXmlValue(Constants.EMPTY_STRING) - : CreateText.createText(StringUtils.fromString(jsonValue.toString())); + return DiagnosticLog.createXmlError(e.getMessage()); } } @@ -127,14 +144,18 @@ private static BMap getEmptyStringMap() { } public static BXml traverseRecordAndGenerateXml(Object jNode, BMap allNamespaces, - BMap parentNamespaces, BMap options, - Object keyObj, Type type, boolean isParentSequence, boolean isParentSequenceArray) { + BMap parentNamespaces, BMap options, Object keyObj, Type type, + boolean isParentSequence, boolean isParentSequenceArray, + ModelGroupInfo parentModelGroupInfo, ElementInfo parentElementInfo) throws BError { BMap namespacesOfElem; BXml xNode = ValueCreator.createXmlValue(Constants.EMPTY_STRING); String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); Type referredType = TypeUtils.getReferredType(type); HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); - ArrayList modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); + HashMap modelGroupRelatedFieldNames = + getModelGroupRelatedFieldNames(referredType, elementNamesMap); + HashMap elementInfoRelatedFieldNames = + getElementInfoRelatedFieldNames(referredType, elementNamesMap); ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); BXml childElement; @@ -149,7 +170,9 @@ public static BXml traverseRecordAndGenerateXml(Object jNode, BMap parentModelGroupInfo.getMaxOccurs()) { + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, + parentModelGroupInfo.getFieldName()); + } + } else { + if (parentElementInfo != null && size > parentElementInfo.maxOccurs) { + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, + parentElementInfo.fieldName); + } + + if (parentElementInfo != null && size < parentElementInfo.minOccurs) { + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, + parentElementInfo.fieldName); + } + } + for (Object i : arrayNode.getValues()) { if (i == null) { continue; @@ -193,16 +239,16 @@ allNamespaces, options, getAttributesMap( addNamespaces(allNamespaces, namespacesOfElem); if (options.get(Constants.ARRAY_ENTRY_TAG).toString().isEmpty()) { childElement = getElementFromRecordMember(StringUtils.fromString(arrayEntryTagKey), - traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, - options, keyObj, getChildElementType(referredType, null), - isParentSequence, isParentSequenceArray), - allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); + traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, + options, keyObj, getChildElementType(referredType, null), + isParentSequence, isParentSequenceArray, parentModelGroupInfo, parentElementInfo), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); } else { childElement = getElementFromRecordMember(StringUtils.fromString(arrayEntryTagKey), - traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, - options, null, getChildElementType(referredType, null), - isParentSequence, isParentSequenceArray), - allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); + traverseRecordAndGenerateXml(i, allNamespaces, namespacesOfElem, + options, null, getChildElementType(referredType, null), + isParentSequence, isParentSequenceArray, parentModelGroupInfo, parentElementInfo), + allNamespaces, options, getAttributesMap(i, options, allNamespaces, parentNamespaces)); } xNode = Concat.concat(xNode, isParentSequenceArray ? childElement.children() : childElement); } @@ -212,13 +258,22 @@ options, null, getChildElementType(referredType, null), return xNode; } - private static ArrayList getModelGroupRelatedFieldNames(Type expType, - HashMap elementNamesMap) { + private static HashMap getModelGroupRelatedFieldNames(Type expType, + HashMap elementNamesMap) { Type referedType = TypeUtils.getReferredType(expType); if (referedType instanceof RecordType recordType) { return DataUtils.getFieldNamesWithModelGroupAnnotations(recordType, elementNamesMap); } - return new ArrayList<>(); + return new HashMap<>(); + } + + private static HashMap getElementInfoRelatedFieldNames(Type expType, + HashMap elementNamesMap) { + Type referedType = TypeUtils.getReferredType(expType); + if (referedType instanceof RecordType recordType) { + return DataUtils.getFieldNamesWithElementGroupAnnotations(recordType, elementNamesMap, null); + } + return new HashMap<>(); } private static ArrayList getSequenceFieldNames(Type expType, @@ -231,7 +286,7 @@ private static ArrayList getSequenceFieldNames(Type expType, } - private static Type getChildElementType(Type type, String recordKey) { + private static Type getChildElementType(Type type, String recordKey) throws BError { try { if (type instanceof ArrayType arrayType) { return TypeUtils.getReferredType(arrayType.getElementType()); @@ -292,15 +347,8 @@ public static boolean isSingleRecordMember(Object node) { } return true; } - public static BXml getElementFromRecordMember(BString name, BXml children, BMap namespaces, BMap options, BMap attributes) { - return getElementFromRecordMember( - name, children, namespaces, options, attributes, PredefinedTypes.TYPE_ANYDATA); - } - - public static BXml getElementFromRecordMember(BString name, BXml children, BMap namespaces, - BMap options, BMap attributes, Type type) { String attributePrefix = options.get(Constants.ATTRIBUTE_PREFIX).toString(); String userAttributePrefix = options.get(Constants.USER_ATTRIBUTE_PREFIX).toString(); BXml element; diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java index e7d48961..23bf6078 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ChoiceInfo.java @@ -212,4 +212,16 @@ private void reOrderElementNamesBasedOnTheNameAnnotation() { } }); } + + public long getMinOccurs() { + return minOccurs; + } + + public long getMaxOccurs() { + return maxOccurs; + } + + public String getFieldName() { + return fieldName; + } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java index 6139c406..be7f6ff4 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ElementInfo.java @@ -21,6 +21,7 @@ import io.ballerina.lib.data.xmldata.utils.Constants; import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode; import io.ballerina.lib.data.xmldata.utils.DiagnosticLog; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; @@ -31,7 +32,7 @@ */ public class ElementInfo { String name; - String fieldName; + public String fieldName; public long minOccurs; public long maxOccurs; public int occurrences; @@ -62,11 +63,11 @@ public void updateOccurrences() { } } - public void validate() { + public void validate() throws BError { validateMinOccurrences(); } - private void validateMinOccurrences() { + private void validateMinOccurrences() throws BError { if (!isInsideChoice && this.occurrences < this.minOccurs) { throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, name); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java index ca6e44d5..bf0f612e 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/ModelGroupInfo.java @@ -30,4 +30,7 @@ public interface ModelGroupInfo { boolean isMiddleOfModelGroup(); boolean predictStartNewModelGroup(String element); void validateMinOccurrences(); + long getMinOccurs(); + long getMaxOccurs(); + String getFieldName(); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java index b711cda3..770cc5f1 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/xsd/SequenceInfo.java @@ -275,4 +275,16 @@ private void reOrderElementNamesBasedOnTheNameAnnotation() { } }); } + + public long getMinOccurs() { + return minOccurs; + } + + public long getMaxOccurs() { + return maxOccurs; + } + + public String getFieldName() { + return fieldName; + } } From 8269d3d997b781b52ae2e6d17d0a9edbef3f95b2 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 20 Nov 2024 11:49:09 +0530 Subject: [PATCH 49/58] Add xsd sequence toXml tests with namespace annotations --- ...xml_tests.bal => xsd_test_toxml_tests.bal} | 0 .../tests/xsd_test_toxml_with_namespaces.bal | 340 ++++++++++++++++++ .../lib/data/xmldata/utils/DataUtils.java | 22 +- .../lib/data/xmldata/utils/ToXmlUtils.java | 5 +- 4 files changed, 363 insertions(+), 4 deletions(-) rename ballerina/tests/{xsd_to_xml_tests.bal => xsd_test_toxml_tests.bal} (100%) create mode 100644 ballerina/tests/xsd_test_toxml_with_namespaces.bal diff --git a/ballerina/tests/xsd_to_xml_tests.bal b/ballerina/tests/xsd_test_toxml_tests.bal similarity index 100% rename from ballerina/tests/xsd_to_xml_tests.bal rename to ballerina/tests/xsd_test_toxml_tests.bal diff --git a/ballerina/tests/xsd_test_toxml_with_namespaces.bal b/ballerina/tests/xsd_test_toxml_with_namespaces.bal new file mode 100644 index 00000000..81f57556 --- /dev/null +++ b/ballerina/tests/xsd_test_toxml_with_namespaces.bal @@ -0,0 +1,340 @@ +// Copyright (c) 2023, 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; + +@Name { + value: "A" +} +type ToXmlWithNamespaces1 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + @Namespace { + uri: "http://example.com", + prefix: "ex" + } + Seq_A_toXmlWithNamespaces seq_a; +}; + +type Seq_A_toXmlWithNamespaces record { + @Order { + value: 3 + } + @Namespace { + uri: "http://example2.com", + prefix: "ex2" + } + string c; + + + @Namespace { + uri: "http://example2.com", + prefix: "ex2" + } + @Order { + value: 1 + } + string a; + + @Order { + value: 2 + } + @Namespace { + uri: "http://example2.com", + prefix: "ex2" + } + string b; +}; + +@Name { + value: "A" +} +type ToXmlWithNamespaces2 record { + Seq_A_toXmlWithNamespaces2 name; +}; + +type Seq_A_toXmlWithNamespaces2 record { + + @Namespace { + uri: "http://example3.com", + prefix: "c" + } + @Order { + value: 3 + } + string c; + + @Order { + value: 1 + } + + @Namespace { + uri: "http://example1.com", + prefix: "a" + } + string a; + + @Namespace { + uri: "http://example3.com", + prefix: "b" + } + @Order { + value: 2 + } + string b; +}; + +@test:Config {groups: ["xsd", "to_xml"]} +function testToXmlWithSimpleRecordWithNamespaces2() { + ToXmlWithNamespaces2 a; + xml|Error xmlResult; + + a = {name: {b: "B", a: "A", c: "C"}}; + xmlResult = toXml(a); + test:assertEquals(xmlResult, xml `CAB`); +} + +@Name { + value: "A" +} +type ToXmlWithNamespaces3 record { + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3_2 name2; +}; + +type Seq_A_toXmlWithNamespaces3 record { + @Order { + value: 3 + } + @Namespace { + uri: "http://example3.com", + prefix: "c" + } + string c; + + @Order { + value: 1 + } + string a; + + @Order { + value: 3 + } + @Namespace { + uri: "http://example2.com", + prefix: "b" + } + string b?; +}; + +type Seq_A_toXmlWithNamespaces3_2 record { + @Order { + value: 3 + } + string c2; + + @Namespace { + uri: "http://example3.com", + prefix: "a2" + } + @Order { + value: 1 + } + string a2; + + @Order { + value: 3 + } + string b2; +}; + +@Name { + value: "A" +} +type ToXmlWithNamespaces4 record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3_2 name2; + + record{record{int n;} name;} name3; +}; + +@Name { + value: "A" +} +type ToXmlWithNamespaces5 record { + record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3_2 name2; + + record{record{int n;} name;} name3; + } a; + + string c; + + record { + int n; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3 name; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_A_toXmlWithNamespaces3_2 name2; + + record{record{int n;} name;} name3; + } b; +}; + +@Name { + value: "Root" +} +type ToXmlWithNamespaces6 record { + @Sequence { + minOccurs: 0, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithNamespace13_1 seq_XSDSequenceRecord13_1?; + + @Sequence { + minOccurs: 1, + maxOccurs: 1 + } + Seq_XSDSequenceRecordWithNamespace13_2 seq_XSDSequenceRecord13_2; +}; + +type Seq_XSDSequenceRecordWithNamespace13_1 record { + @Order {value: 1} + @Namespace { + uri: "http://example1.com", + prefix: "field1" + } + Seq_A_13 field1; + + @Order {value: 2} + @Namespace { + uri: "http://example2.com", + prefix: "field2" + } + Seq_B_13 field2; + + @Namespace { + uri: "http://example3.com", + prefix: "field3" + } + @Order {value: 3} + Seq_C_13 field3; +}; + +type Seq_XSDSequenceRecordWithNamespace13_2 record { + @Order {value: 1} + @Namespace { + uri: "http://example4.com", + prefix: "field4" + } + Seq_D_13 field4; + + @Order {value: 2} + @Namespace { + uri: "http://example5.com" + } + Seq_E_13 field5; + + @Namespace { + uri: "http://example6.com", + prefix: "field6" + } + @Order {value: 3} + Seq_F_13 field6; +}; + +@test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlWithXsdProviderWithNamespaces} +function testToXmlWithXsdWithNamespaces(typedesc recordType, record{} value, xml expected) returns error?{ + xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); + test:assertEquals(xmlResult, expected); +} + +function testToXmlWithXsdProviderWithNamespaces() returns [typedesc, record{}, xml][] { + return [[ + ToXmlWithNamespaces1, + {seq_a: {b: "B", a: "A", c: "C"}}, + xml `ABC` + ], + [ + ToXmlWithNamespaces2, + {name: {b: "B", a: "A", c: "C"}}, + xml `CAB` + ], + [ + ToXmlWithNamespaces3, + {name: {b: "B", a: "A", c: "C"}, name2: {b2: "B", a2: "A", c2: "C"}}, + xml `ABCABC` + ], + [ + ToXmlWithNamespaces4, + {name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}, n: 1}, + xml `1ABCABC1` + ], + [ + ToXmlWithNamespaces5, + {a: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, b: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, c: "A"}, + xml `1ABCABC1A1ABCABC1` + ] + // TODO: Values are exact equals but assertion is failing. Need to check. + // [ + // ToXmlWithNamespaces6, + // {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}, + // xml `123123123123123123` + // ] + ]; +} 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 178bb1b5..e85b5ae2 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 @@ -524,17 +524,35 @@ private static BMap addFields(BMap input, Type static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap input, HashMap xsdSequencePriorityOrder) { + HashMap localPartKeys = getLocalPartKeys(input); if (!xsdSequencePriorityOrder.isEmpty()) { return xsdSequencePriorityOrder.entrySet().stream() - .filter(entry -> input.containsKey(StringUtils.fromString(entry.getKey()))) + .filter(entry -> localPartKeys.containsKey(entry.getKey())) .sorted(Comparator.comparingInt(Map.Entry::getValue)) - .map(entry -> StringUtils.fromString(entry.getKey())) + .map(entry -> StringUtils.fromString(localPartKeys.get(entry.getKey()))) .toArray(BString[]::new); } else { return input.getKeys(); } } + private static HashMap getLocalPartKeys(BMap input) { + HashMap localPartKeys = new HashMap<>(); + for (Map.Entry entry : input.entrySet()) { + String k = entry.getKey().getValue(); + if (k.contains(ATTRIBUTE_PREFIX)) { + continue; + } + int i = k.indexOf(Constants.COLON); + if (i != -1) { + localPartKeys.put(k.substring(i + 1), k); + } else { + localPartKeys.put(k, k); + } + } + return localPartKeys; + } + private static void processRecordField(Type fieldType, BMap annotations, BMap recordValue, Map.Entry entry, String key, Object value) { diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index fcb7bdb9..1efa5e7a 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -20,6 +20,7 @@ import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; +import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; @@ -94,7 +95,6 @@ public static Object fromRecordToXml(Object jsonValue, BMap opt String keyStr = key.getValue(); HashMap elementNamesMap = DataUtils.getElementNameMap(referredType); ArrayList sequenceFieldNames = getSequenceFieldNames(referredType, elementNamesMap); - boolean isSequenceField = sequenceFieldNames.contains(keyStr); HashMap modelGroupRelatedFieldNames = getModelGroupRelatedFieldNames(referredType, elementNamesMap); HashMap elementInfoRelatedFieldNames = @@ -102,6 +102,7 @@ public static Object fromRecordToXml(Object jsonValue, BMap opt String localJsonKeyPart = keyStr.contains(Constants.COLON) ? keyStr.substring(keyStr.indexOf(Constants.COLON) + 1) : keyStr; String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); + boolean isSequenceField = sequenceFieldNames.contains(recordKey); boolean isContainsModelGroup = modelGroupRelatedFieldNames.containsKey(recordKey); ModelGroupInfo parentModelGroupInfo = modelGroupRelatedFieldNames.get(recordKey); ElementInfo elementInfo = elementInfoRelatedFieldNames.get(recordKey); @@ -202,7 +203,7 @@ value, allNamespaces, namespacesOfElem, options, null, getChildElementType( } } else if (jNode instanceof BArray arrayNode) { int size = arrayNode.size(); - if (isParentSequenceArray && parentModelGroupInfo != null) { + if (isParentSequenceArray && parentModelGroupInfo != null && parentModelGroupInfo instanceof SequenceInfo) { if (size < parentModelGroupInfo.getMinOccurs()) { throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, parentModelGroupInfo.getFieldName()); From 414b636206962605b503a50c66078c9592598e10 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 20 Nov 2024 12:17:15 +0530 Subject: [PATCH 50/58] Add documentations for xsd related public types --- ballerina/xml_api.bal | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 496fc07d..0ad626fc 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -22,6 +22,7 @@ const ATTRIBUTE_PREFIX = "attribute_"; const XMLNS = "xmlns"; const EMPTY_STRING = ""; +# Define the configurations for min and max occurrences of members in the XML schema (XSD). public type ParticleOccurrence record {| # Specifies the minimum number of occurrences. int:Unsigned32 minOccurs?; @@ -37,22 +38,28 @@ public type ElementConfig record {| # Annotation to define schema rules for an XML element in Ballerina. public const annotation ElementConfig Element on type, record field; +# Defines the configuration for an XML sequence in the XML schema (XSD). public type SequenceConfig record {| *ParticleOccurrence; |}; +# Annotation to define schema rules for an XML sequence in Ballerina. public const annotation SequenceConfig Sequence on type, record field; +# Defines the configuration for an XML choice in the XML schema (XSD). public type ChoiceConfig record {| *ParticleOccurrence; |}; +# Annotation to define schema rules for an XML choice in Ballerina. public const annotation ChoiceConfig Choice on type, record field; +# Defines the configuration for the sequence order in the XML schema (XSD). public type SequenceOrderConfig record {| int value; |}; +# Annotation to define schema rules for the sequence order in Ballerina. public const annotation SequenceOrderConfig Order on type, record field; # Defines the name of the XML element. @@ -487,5 +494,5 @@ isolated function addNamespaces(map allNamespaces, map namespace public function validate(string|typedesc schema, xml xmlValue) returns Error? = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; -public isolated function fromRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error +isolated function fromRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error = @java:Method {'class: "io.ballerina.lib.data.xmldata.utils.ToXmlUtils"} external; From 50beed2009f67671d6edfe1c6533334aa5ea5e75 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Wed, 20 Nov 2024 22:51:37 +0530 Subject: [PATCH 51/58] Add tests for xsd with namespaces --- ...h_namespace_annotation_with_parse_type.bal | 16 +- ballerina/tests/xsd_test_toxml_tests.bal | 176 ++++++++++++ .../tests/xsd_test_toxml_with_namespaces.bal | 272 +++++++++++++++++- 3 files changed, 460 insertions(+), 4 deletions(-) diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal index 03ee89d4..d256b73f 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal @@ -16,6 +16,9 @@ import ballerina/test; +@Name { + value: "Root" +} type XsdSequenceWithNamespaceAnnotationWithXmlValue record { @Sequence { minOccurs: 0, @@ -70,15 +73,21 @@ type Seq_EA1_NamespaceAnnotationWithXmlValue record { function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { xml xmlStr; XsdSequenceWithNamespaceAnnotationWithXmlValue|Error v; + xml|Error toXmlResult; xmlStr = xml `ABCABC`; v = parseAsType(xmlStr); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element(s) 'EA3' is not found in 'seq_EA1_NamespaceAnnotationWithXmlValue'"); + toXmlResult = toXml({seq_EA1_NamespaceAnnotationWithXmlValue: {EA1: "ABC", EA2: "ABC", EA3: []}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'EA3' occurs less than the min required times"); xmlStr = xml `ABCABCABABAB`; v = parseAsType(xmlStr); test:assertEquals(v, {seq_EA1_NamespaceAnnotationWithXmlValue: {EA1: "ABC", EA2: "ABC", EA3: ["AB", "AB", "AB"]}}); + // // BUG: https://github.com/ballerina-platform/ballerina-library/issues/7389 + // test:assertEquals(toXml(check v), xml `ABCABCABABAB`); xmlStr = xml `ABCABAB`; v = parseAsType(xmlStr); @@ -96,6 +105,9 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { v = parseAsType(xmlStr); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs more than the max allowed times"); + toXmlResult = toXml({seq_EA1_NamespaceAnnotationWithXmlValue: {EA3: ["AB", "AB", "AB", "AB", "AB", "AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), (v).message()); xmlStr = xml `ABCABAB`; v = parseAsType(xmlStr); @@ -110,6 +122,9 @@ function testXsdSequenceWithNamespaceAnnotationWithXmlValue() returns error? { v = parseAsType(xmlStr); test:assertTrue(v is Error); test:assertEquals((v).message(), "'EA3' occurs less than the min required times"); + toXmlResult = toXml({seq_EA1_NamespaceAnnotationWithXmlValue: {EA3: ["AB"]}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), (v).message()); xmlStr = xml `ABCAB`; v = parseAsType(xmlStr); @@ -170,7 +185,6 @@ type Seq_EA2_NamespaceAnnotationWithXmlValue record { maxOccurs: 4, minOccurs: 2 } - @Namespace { uri: "example3.com", prefix: "ea3" diff --git a/ballerina/tests/xsd_test_toxml_tests.bal b/ballerina/tests/xsd_test_toxml_tests.bal index ee0ec5b9..f9d3fcb2 100644 --- a/ballerina/tests/xsd_test_toxml_tests.bal +++ b/ballerina/tests/xsd_test_toxml_tests.bal @@ -210,6 +210,17 @@ type ToXml6 record { Seq_XSDSequenceRecord13_2 seq_XSDSequenceRecord13_2; }; +@Name { + value: "A" +} +type ToXml7 record { + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + Seq_A_toXml[] seq_a; +}; + @test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlWithXsdProvider} function testToXmlWithXsd(typedesc recordType, record{} value, xml expected) returns error?{ xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); @@ -246,6 +257,171 @@ function testToXmlWithXsdProvider() returns [typedesc, record{}, xml][ ToXml6, {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}, xml `123123123123123123` + ], + [ + ToXml7, + {seq_a: [{b: "B", a: "A", c: "C"}, {b: "B", a: "A", c: "C"}]}, + xml `ABCABC` + ] + ]; +} + +@Name { + value: "A" +} +type ToXmlChoice1 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_A_toXml choice_a; +}; + +type Choice_A_toXml record { + string c?; + string a?; + string b?; +}; + +@Name { + value: "A" +} +type ToXmlChoice2 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_A_toXml2 choice_a; + + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_A_toXml3 choice_b; +}; + +type Choice_A_toXml2 record { + @Element { + minOccurs: 2, + maxOccurs: 3 + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 5 + } + string b?; +}; + +type Choice_A_toXml3 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string b?; +}; + +@Name { + value: "A" +} +type ToXmlChoice4 record { + record { + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_A_toXml4 choice_a; + } nestedName; + + record { + @Choice { + minOccurs: 2, + maxOccurs: 2 + } + Choice_A_toXml4 choice_a; + } nestedName2; +}; + +type Choice_A_toXml4 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string b?; +}; + +type Choice_A_toXml5 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string a?; + + @Element { + minOccurs: 2, + maxOccurs: 3 + } + string b?; +}; + +@test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlWithXsdProvider2} +function testToXmlWithXsd2(typedesc recordType, record{} value, xml expected) returns error?{ + xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); + test:assertEquals(xmlResult, expected); +} + +function testToXmlWithXsdProvider2() returns [typedesc, record{}, xml][] { + return [[ + ToXmlChoice1, + {choice_a: {b: "B"}}, + xml `B` + ], + [ + ToXmlChoice2, + {choice_a: {c: "C"}, choice_b: {b: "B", a: "A", c: "C"}}, + xml `CCAB` + ], + [ + ToXmlChoice4, + {nestedName: {choice_a: {b: "B", a: "A"}}, nestedName2: {choice_a: {b: "B", a: "A"}}}, + xml `ABAB` ] ]; } diff --git a/ballerina/tests/xsd_test_toxml_with_namespaces.bal b/ballerina/tests/xsd_test_toxml_with_namespaces.bal index 81f57556..9e3cd6e9 100644 --- a/ballerina/tests/xsd_test_toxml_with_namespaces.bal +++ b/ballerina/tests/xsd_test_toxml_with_namespaces.bal @@ -16,6 +16,8 @@ import ballerina/test; +type EmptyRec record {}; + @Name { value: "A" } @@ -41,7 +43,6 @@ type Seq_A_toXmlWithNamespaces record { } string c; - @Namespace { uri: "http://example2.com", prefix: "ex2" @@ -298,6 +299,46 @@ type Seq_XSDSequenceRecordWithNamespace13_2 record { Seq_F_13 field6; }; +@Name { + value: "A" +} +type ToXmlWithNamespaces7 record { + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + @Namespace { + uri: "http://example.com", + prefix: "ex" + } + Seq_A_toXmlWithNamespaces[] seq_a; +}; + +@Name { + value: "A" +} +type ToXmlWithNamespaces8 record { + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + @Namespace { + uri: "http://example.com", + prefix: "ex" + } + Seq_A_toXmlWithNamespaces[] seq_a; + + @Sequence { + minOccurs: 1, + maxOccurs: 2 + } + @Namespace { + uri: "http://example2.com", + prefix: "ex2" + } + Seq_A_toXmlWithNamespaces[] seq_a2; +}; + @test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlWithXsdProviderWithNamespaces} function testToXmlWithXsdWithNamespaces(typedesc recordType, record{} value, xml expected) returns error?{ xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); @@ -329,12 +370,237 @@ function testToXmlWithXsdProviderWithNamespaces() returns [typedesc, r ToXmlWithNamespaces5, {a: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, b: {n: 1, name: {b: "B", a: "A", c: "C"}, name3: {name: {n: 1}}, name2: {b2: "B", a2: "A", c2: "C"}}, c: "A"}, xml `1ABCABC1A1ABCABC1` - ] + ], // TODO: Values are exact equals but assertion is failing. Need to check. // [ // ToXmlWithNamespaces6, // {seq_XSDSequenceRecord13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecord13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}, // xml `123123123123123123` - // ] + // ], + [ + EmptyRec, + {}, + xml `` + ], + [ + ToXmlWithNamespaces7, + {seq_a: [{b: "B", a: "A", c: "C"}, {b: "B", a: "A", c: "C"}]}, + xml `ABCABC` + ], + [ + ToXmlWithNamespaces8, + {seq_a: [{b: "B", a: "A", c: "C"}, {b: "B", a: "A", c: "C"}], seq_a2: [{b: "B", a: "A", c: "C"}, {b: "B", a: "A", c: "C"}]}, + xml `ABCABCABCABC` + ] + ]; +} + +@Name { + value: "A" +} +type ToXmlChoiceWithNamespace1 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_A_toXmlWithNamespace choice_a; +}; + +type Choice_A_toXmlWithNamespace record { + @Namespace { + uri: "http://examplec.com", + prefix: "c" + } + string c?; + + @Namespace { + uri: "http://examplea.com", + prefix: "a" + } + string a?; + + @Namespace { + uri: "http://exampleb.com", + prefix: "b" + } + string b?; +}; + +@Name { + value: "A" +} +type ToXmlChoiceWithNamespace2 record { + @Choice { + minOccurs: 1, + maxOccurs: 1 + } + Choice_A_toXmlWithNamespace2 choice_a; + + @Choice { + minOccurs: 1, + maxOccurs: 3 + } + Choice_A_toXmlWithNamespace3 choice_b; +}; + +type Choice_A_toXmlWithNamespace2 record { + @Element { + minOccurs: 2, + maxOccurs: 3 + } + @Namespace { + uri: "http://examplea.com", + prefix: "c" + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 5 + } + @Namespace { + uri: "http://examplea.com", + prefix: "b" + } + string b?; +}; + +type Choice_A_toXmlWithNamespace3 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string c?; + + @Namespace { + uri: "http://examplea.com", + prefix: "a" + } + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + string b?; +}; + +@Name { + value: "A" +} +type ToXmlChoiceWithNamespace4 record { + record { + @Choice { + minOccurs: 2, + maxOccurs: 3 + } + Choice_A_toXmlWithNamespace4 choice_a; + } nestedName; + + record { + @Choice { + minOccurs: 2, + maxOccurs: 2 + } + Choice_A_toXmlWithNamespace3 choice_a; + } nestedName2; +}; + +type Choice_A_toXmlWithNamespace4 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + @Namespace { + uri: "http://examplec.com", + prefix: "c" + } + string c?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + @Namespace { + uri: "http://exampleb.com", + prefix: "a" + } + string a?; + + @Element { + minOccurs: 1, + maxOccurs: 1 + } + @Namespace { + uri: "http://exampleb.com", + prefix: "b" + } + string b?; +}; + +type Choice_A_toXmlWithNamespace5 record { + @Element { + minOccurs: 1, + maxOccurs: 1 + } + @Namespace { + uri: "http://examplea.com", + prefix: "c" + } + string c?; + + @Namespace { + uri: "http://examplea.com", + prefix: "a" + } + @Element { + minOccurs: 1, + maxOccurs: 2 + } + string a?; + + @Element { + minOccurs: 2, + maxOccurs: 3 + } + @Namespace { + uri: "http://examplea.com", + prefix: "b" + } + string b?; +}; + +@test:Config {groups: ["xsd", "to_xml"], dataProvider: testToXmlDataProviderWithNamespaces2} +function testToXmlWithXsdWithNamespace(typedesc recordType, record{} value, xml expected) returns error?{ + xml|Error xmlResult = toXml(check value.ensureType(recordType), {}); + test:assertEquals(xmlResult, expected); +} + +function testToXmlDataProviderWithNamespaces2() returns [typedesc, record{}, xml][] { + return [[ + ToXmlChoiceWithNamespace1, + {choice_a: {b: "B"}}, + xml `B` + ], + [ + ToXmlChoiceWithNamespace2, + {choice_a: {c: "C"}, choice_b: {b: "B", a: "A", c: "C"}}, + xml `CCAB` + ], + [ + ToXmlChoiceWithNamespace4, + {nestedName: {choice_a: {b: "B", a: "A"}}, nestedName2: {choice_a: {b: "B", a: "A"}}}, + xml `ABAB` + ] ]; } From 4d374741239a4d58892a314dd3db52c66f382942 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 21 Nov 2024 10:58:30 +0530 Subject: [PATCH 52/58] Add validation for choice tags in toXml function --- .../xsd_choice_array_test_with_parse_type.bal | 9 ++++ .../tests/xsd_choice_test_with_parse_type.bal | 15 +++++++ .../lib/data/xmldata/utils/ToXmlUtils.java | 42 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal index f8abc0fb..9ee44904 100644 --- a/ballerina/tests/xsd_choice_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_array_test_with_parse_type.bal @@ -56,6 +56,9 @@ function testXsdChoiceArrayWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue' occurs more than the max allowed times"); + xml|Error toXmlResult = toXml({choice_XsdChoiceArrayWithXmlValue: {age: [13, 14, 15], salary: [11.1, 14.1, 15.1]}}); + test:assertTrue(toXmlResult is error); + test:assertEquals((toXmlResult).message(), "'choice_XsdChoiceArrayWithXmlValue' occurs more than the max allowed times"); } @Name { @@ -109,11 +112,17 @@ function testXsdChoiceArrayWithXmlValue2() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue2_2' occurs more than the max allowed times"); + xml|Error toXmlResult = toXml({choice_XsdChoiceArrayWithXmlValue2: {age: [13], salary: []}, choice_XsdChoiceArrayWithXmlValue2_2: {age2: [13, 14, 15], salary2: [11.1]}}); + test:assertTrue(toXmlResult is error); + test:assertEquals((toXmlResult).message(), "'choice_XsdChoiceArrayWithXmlValue2_2' occurs more than the max allowed times"); xmlValue = xml `13131313`; v = parseAsType(xmlValue); test:assertTrue(v is error); test:assertEquals((v).message(), "'choice_XsdChoiceArrayWithXmlValue2' occurs more than the max allowed times"); + toXmlResult = toXml({choice_XsdChoiceArrayWithXmlValue2: {age: [13, 14, 15], salary: [11.1, 14.1, 15.1]}}); + test:assertTrue(toXmlResult is error); + test:assertEquals((toXmlResult).message(), "'choice_XsdChoiceArrayWithXmlValue2' occurs more than the max allowed times"); xmlValue = xml `1313`; v = parseAsType(xmlValue); diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index 1d66f686..84039286 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -57,6 +57,9 @@ function testXsdChoiceWithXmlValue() returns error? { test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); e = validate(XSDChoiceWithXmlValueRecord, xmlValue); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times'"); + xml|Error toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord: {age: 10, salary: 11.1}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); xmlValue = xml `11.111.1`; v = parseAsType(xmlValue); @@ -210,11 +213,17 @@ function testXsdChoiceWithXmlValue2() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); + xml|Error toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord2: {age: 10, salary: 11.1}, num: 3}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); xmlValue = xml `10311.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); + toXmlResult = toXml({num: 3, choice_XSDChoiceWithXmlValueRecord2: {age: 10, salary: 11.1}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'choice_XSDChoiceWithXmlValueRecord2' occurs more than the max allowed times"); } @Name { @@ -604,6 +613,9 @@ function testXsdChoiceWithXmlValue10() returns error? { test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times"); e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times'"); + xml|Error toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}, field2: {value2: {d: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {d: "2"}}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times"); xmlValue = xml `122`; v2 = parseAsType(xmlValue); @@ -618,4 +630,7 @@ function testXsdChoiceWithXmlValue10() returns error? { test:assertEquals((v2).message(), "'value2' occurs more than the max allowed times"); e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); test:assertEquals((e).message(), "Invalid XML found: ''value2' occurs more than the max allowed times'"); + toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord10_1: {field2: {value2: {d: "1", e: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {d: "2"}}}}); + test:assertTrue(toXmlResult is Error); + test:assertEquals((toXmlResult).message(), "'value2' occurs more than the max allowed times"); } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java index 1efa5e7a..541a23d2 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/ToXmlUtils.java @@ -18,6 +18,7 @@ package io.ballerina.lib.data.xmldata.utils; +import io.ballerina.lib.data.xmldata.xml.xsd.ChoiceInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ElementInfo; import io.ballerina.lib.data.xmldata.xml.xsd.ModelGroupInfo; import io.ballerina.lib.data.xmldata.xml.xsd.SequenceInfo; @@ -165,6 +166,10 @@ public static BXml traverseRecordAndGenerateXml(Object jNode, BMap elementInfoRelatedFieldNames, + HashMap elementNamesMap) { + // TODO: Update this later for validate choices with multiple element occurences. + boolean isMeasurable = true; + int occurences = 0; + + for (Object key : jMap.getKeys()) { + String jsonKey = key.toString(); + Object value = jMap.get(key); + String localJsonKeyPart = jsonKey.contains(Constants.COLON) ? + jsonKey.substring(jsonKey.indexOf(Constants.COLON) + 1) : jsonKey; + String recordKey = elementNamesMap.getOrDefault(localJsonKeyPart, localJsonKeyPart); + ElementInfo elementInfo = elementInfoRelatedFieldNames.get(recordKey); + if (elementInfo != null && elementInfo.maxOccurs != 1) { + isMeasurable = false; + break; + } + + if (value instanceof BArray array) { + occurences += array.size(); + } else { + occurences++; + } + } + + if (isMeasurable && occurences > parentModelGroupInfo.getMaxOccurs()) { + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_MORE_THAN_MAX_ALLOWED_TIMES, + parentModelGroupInfo.getFieldName()); + } + + if (isMeasurable && occurences < parentModelGroupInfo.getMinOccurs()) { + throw DiagnosticLog.error(DiagnosticErrorCode.ELEMENT_OCCURS_LESS_THAN_MIN_REQUIRED_TIMES, + parentModelGroupInfo.getFieldName()); + } + } + private static HashMap getModelGroupRelatedFieldNames(Type expType, HashMap elementNamesMap) { Type referedType = TypeUtils.getReferredType(expType); From 0f3e4f4db47f7ec8a9aca71e50c3fb6f73f1df38 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Thu, 21 Nov 2024 16:34:43 +0530 Subject: [PATCH 53/58] Refactor sequence order compile time validation --- ...sd_choice_test_with_element_annotation.bal | 6 +- .../tests/xsd_choice_test_with_parse_type.bal | 18 +-- ballerina/tests/xsd_sequence_array_test.bal | 58 ++++----- ...sd_sequence_array_test_with_parse_type.bal | 36 +++--- ..._sequence_test_with_element_annotation.bal | 44 +++---- ...ith_element_annotation_with_parse_type.bal | 26 ++-- ...xsd_sequence_test_with_name_annotation.bal | 14 +-- ...t_with_name_annotation_with_parse_type.bal | 14 +-- ...equence_test_with_namespace_annotation.bal | 14 +-- ...h_namespace_annotation_with_parse_type.bal | 14 +-- ballerina/tests/xsd_sequence_tests.bal | 116 +++++++++--------- .../xsd_sequence_tests_with_parse_type.bal | 116 +++++++++--------- ballerina/tests/xsd_test_toxml_tests.bal | 24 ++-- .../tests/xsd_test_toxml_with_namespaces.bal | 36 +++--- .../xsd_validation_with_file_path_tests.bal | 20 +-- ballerina/xml_api.bal | 49 ++++---- .../xmldata/compiler/CompilerPluginTest.java | 8 +- .../compiler/XmldataRecordFieldValidator.java | 11 +- .../lib/data/xmldata/xml/Native.java | 2 +- 19 files changed, 315 insertions(+), 311 deletions(-) diff --git a/ballerina/tests/xsd_choice_test_with_element_annotation.bal b/ballerina/tests/xsd_choice_test_with_element_annotation.bal index eef7ed88..f70664aa 100644 --- a/ballerina/tests/xsd_choice_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_choice_test_with_element_annotation.bal @@ -128,7 +128,7 @@ type Choice_EA2 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -137,7 +137,7 @@ type Choice_EA2 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -146,7 +146,7 @@ type Choice_EA2 record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; diff --git a/ballerina/tests/xsd_choice_test_with_parse_type.bal b/ballerina/tests/xsd_choice_test_with_parse_type.bal index 84039286..25677648 100644 --- a/ballerina/tests/xsd_choice_test_with_parse_type.bal +++ b/ballerina/tests/xsd_choice_test_with_parse_type.bal @@ -41,21 +41,21 @@ function testXsdChoiceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {age: 10}}); test:assertEquals(toXml(check v), xmlValue); - Error? e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + Error? e = validate(xmlValue, XSDChoiceWithXmlValueRecord); test:assertEquals(e, ()); xmlValue = xml `10.5`; v = parseAsType(xmlValue); test:assertEquals(v, {choice_XSDChoiceWithXmlValueRecord: {salary: 10.5}}); test:assertEquals(toXml(check v), xmlValue); - e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord); test:assertEquals(e, ()); xmlValue = xml `1011.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); - e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times'"); xml|Error toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord: {age: 10, salary: 11.1}}); test:assertTrue(toXmlResult is Error); @@ -65,14 +65,14 @@ function testXsdChoiceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times"); - e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs more than the max allowed times'"); xmlValue = xml ``; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "'choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times"); - e = validate(XSDChoiceWithXmlValueRecord, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord' occurs less than the min required times'"); } @@ -604,14 +604,14 @@ function testXsdChoiceWithXmlValue10() returns error? { XSDChoiceWithXmlValueRecord10|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {"d": "2"}}}}); test:assertEquals(toXml(check v2), xmlValue); - Error? e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + Error? e = validate(xmlValue, XSDChoiceWithXmlValueRecord10); test:assertEquals(e, ()); xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times"); - e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord10); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord10_1' occurs more than the max allowed times'"); xml|Error toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord10_1: {field1: {value1: {a: "1"}}, field2: {value2: {d: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {d: "2"}}}}); test:assertTrue(toXmlResult is Error); @@ -621,14 +621,14 @@ function testXsdChoiceWithXmlValue10() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'choice_XSDChoiceWithXmlValueRecord10_2' occurs more than the max allowed times"); - e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord10); test:assertEquals((e).message(), "Invalid XML found: ''choice_XSDChoiceWithXmlValueRecord10_2' occurs more than the max allowed times'"); xmlValue = xml `112`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "'value2' occurs more than the max allowed times"); - e = validate(XSDChoiceWithXmlValueRecord10, xmlValue); + e = validate(xmlValue, XSDChoiceWithXmlValueRecord10); test:assertEquals((e).message(), "Invalid XML found: ''value2' occurs more than the max allowed times'"); toXmlResult = toXml({choice_XSDChoiceWithXmlValueRecord10_1: {field2: {value2: {d: "1", e: "1"}}}, choice_XSDChoiceWithXmlValueRecord10_2: {field5: {value2: {d: "2"}}}}); test:assertTrue(toXmlResult is Error); diff --git a/ballerina/tests/xsd_sequence_array_test.bal b/ballerina/tests/xsd_sequence_array_test.bal index 0d2bd8b3..eb5d2c9c 100644 --- a/ballerina/tests/xsd_sequence_array_test.bal +++ b/ballerina/tests/xsd_sequence_array_test.bal @@ -25,12 +25,12 @@ type XsdSequenceArray record {| |}; type Seq_XsdSequenceArray record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -66,24 +66,24 @@ type XsdSequenceArray2 record {| |}; type Seq_XsdSequenceArray2 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XsdSequenceArray2_2 record {| - @Order { + @SequenceOrder { value: 1 } int age2; - @Order { + @SequenceOrder { value: 2 } float salary2; @@ -124,24 +124,24 @@ type XSDSequenceArrayRecord13 record { }; type Seq_XSDSequenceArrayRecord13_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_A_3 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_B_3 field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_Array_C_3 field3; }; type Seq_XSDSequenceArrayRecord13_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_D_3 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_E_3 field5; - @Order {value: 3} + @SequenceOrder {value: 3} Seq__Array_F_3 field6; }; @@ -194,35 +194,35 @@ type Seq__Array_F_3 record { }; type Seq_Array_3 record { - @Order {value: 1} + @SequenceOrder {value: 1} string a; - @Order {value: 2} + @SequenceOrder {value: 2} string b; - @Order {value: 3} + @SequenceOrder {value: 3} string c; }; type Seq2_Array_3 record { - @Order {value: 1} + @SequenceOrder {value: 1} string d; - @Order {value: 2} + @SequenceOrder {value: 2} string e; - @Order {value: 3} + @SequenceOrder {value: 3} string f; }; type Seq3_Array_3 record { - @Order {value: 1} + @SequenceOrder {value: 1} string g; - @Order {value: 2} + @SequenceOrder {value: 2} string h; - @Order {value: 3} + @SequenceOrder {value: 3} string i; }; @@ -285,12 +285,12 @@ type XsdSequenceArray5 record {| |}; type Seq_XsdSequenceArray5 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -335,18 +335,18 @@ type XSDSequenceArrayRecord6 record { }; type Seq_XSDSequenceArrayRecord6_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_A_6 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_B_6 field2; }; type Seq_XSDSequenceArrayRecord6_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_D_6 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_E_6 field5; }; @@ -383,12 +383,12 @@ type Seq_Array_E_6 record { }; type Seq_Array_6 record { - @Order {value: 1} + @SequenceOrder {value: 1} string a; }; type Seq2_Array_6 record { - @Order {value: 1} + @SequenceOrder {value: 1} string d; }; diff --git a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal index 3f9f112e..91f099e6 100644 --- a/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_array_test_with_parse_type.bal @@ -28,12 +28,12 @@ type XsdSequenceArrayWithXmlValue record {| |}; type Seq_XsdSequenceArrayWithXmlValue record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -87,24 +87,24 @@ type XsdSequenceArrayWithXmlValue2 record {| |}; type Seq_XsdSequenceArrayWithXmlValue2 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XsdSequenceArrayWithXmlValue2_2 record {| - @Order { + @SequenceOrder { value: 1 } int age2; - @Order { + @SequenceOrder { value: 2 } float salary2; @@ -156,24 +156,24 @@ type XSDSequenceArrayWithXmlValueRecord13 record { }; type Seq_XSDSequenceArrayWithXmlValueRecord13_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_A_3 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_B_3 field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_Array_C_3 field3; }; type Seq_XSDSequenceArrayWithXmlValueRecord13_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_D_3 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_E_3 field5; - @Order {value: 3} + @SequenceOrder {value: 3} Seq__Array_F_3 field6; }; @@ -247,12 +247,12 @@ type XsdSequenceArrayWithXmlValue5 record {| |}; type Seq_XsdSequenceArrayWithXmlValue5 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -305,18 +305,18 @@ type XSDSequenceArrayWithXmlValueRecord6 record { }; type Seq_XSDSequenceArrayWithXmlValueRecord6_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_A_6 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_B_6 field2; }; type Seq_XSDSequenceArrayWithXmlValueRecord6_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_Array_D_6 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_Array_E_6 field5; }; diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal index 6ef734d0..5f8b8e16 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation.bal @@ -31,7 +31,7 @@ type Seq_EA1 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -40,7 +40,7 @@ type Seq_EA1 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -49,7 +49,7 @@ type Seq_EA1 record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -125,7 +125,7 @@ type XsdSequenceWithElementAnnotation2 record { }; type Seq_EA2 record { - @Order { + @SequenceOrder { value: 1 } record { @@ -133,7 +133,7 @@ type Seq_EA2 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -142,7 +142,7 @@ type Seq_EA2 record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -151,7 +151,7 @@ type Seq_EA2 record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -243,26 +243,26 @@ type Seq_XsdSequenceWithElementAnnotation3_1 record { minOccurs: 1, maxOccurs: 3 } - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A_3[] field1; @Element { minOccurs: 0, maxOccurs: 3 } - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B_3[] field2?; @Element { minOccurs: 1, maxOccurs: 3 } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C_3 field3; }; type Seq_XsdSequenceWithElementAnnotation3_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} @Element { minOccurs: 0, maxOccurs: 3 @@ -273,14 +273,14 @@ type Seq_XsdSequenceWithElementAnnotation3_2 record { minOccurs: 0, maxOccurs: 3 } - @Order {value: 2} + @SequenceOrder {value: 2} Seq_E_3[] field5?; @Element { minOccurs: 0, maxOccurs: 3 } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_F_3[] field6?; }; @@ -337,39 +337,39 @@ type Seq_3 record { minOccurs: 0, maxOccurs: 3 } - @Order {value: 1} + @SequenceOrder {value: 1} string[] a?; @Element { minOccurs: 0, maxOccurs: 3 } - @Order {value: 2} + @SequenceOrder {value: 2} string[] b?; - @Order {value: 3} + @SequenceOrder {value: 3} string c; }; type Seq2_3 record { - @Order {value: 1} + @SequenceOrder {value: 1} string d; - @Order {value: 2} + @SequenceOrder {value: 2} string e; - @Order {value: 3} + @SequenceOrder {value: 3} string f; }; type Seq3_3 record { - @Order {value: 1} + @SequenceOrder {value: 1} string g; - @Order {value: 2} + @SequenceOrder {value: 2} string h; - @Order {value: 3} + @SequenceOrder {value: 3} string i; }; diff --git a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal index 00b62a8f..41460b03 100644 --- a/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_element_annotation_with_parse_type.bal @@ -33,7 +33,7 @@ type Seq_EA1_Xml_Value record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -42,7 +42,7 @@ type Seq_EA1_Xml_Value record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -51,7 +51,7 @@ type Seq_EA1_Xml_Value record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -154,7 +154,7 @@ type XsdSequenceWithElementAnnotationWithXmlValue2 record { }; type Seq_EA2_With_Xml_Value record { - @Order { + @SequenceOrder { value: 1 } record { @@ -162,7 +162,7 @@ type Seq_EA2_With_Xml_Value record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -171,7 +171,7 @@ type Seq_EA2_With_Xml_Value record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -180,7 +180,7 @@ type Seq_EA2_With_Xml_Value record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3; @@ -292,26 +292,26 @@ type Seq_XsdSequenceWithElementAnnotationWithXmlValue3_1 record { minOccurs: 1, maxOccurs: 3 } - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A_3[] field1; @Element { minOccurs: 0, maxOccurs: 3 } - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B_3[] field2?; @Element { minOccurs: 1, maxOccurs: 3 } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C_3 field3; }; type Seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} @Element { minOccurs: 0, maxOccurs: 3 @@ -322,14 +322,14 @@ type Seq_XsdSequenceWithElementAnnotationWithXmlValue3_2 record { minOccurs: 0, maxOccurs: 3 } - @Order {value: 2} + @SequenceOrder {value: 2} Seq_E_3[] field5?; @Element { minOccurs: 0, maxOccurs: 3 } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_F_3[] field6?; }; diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal index ef40c5cb..78bb9450 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation.bal @@ -31,7 +31,7 @@ type Seq_EA1_NameAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -41,7 +41,7 @@ type Seq_EA1_NameAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -51,7 +51,7 @@ type Seq_EA1_NameAnnotation record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -127,7 +127,7 @@ type XsdSequenceWithNameAnnotation2 record { }; type Seq_EA2_NameAnnotation record { - @Order { + @SequenceOrder { value: 1 } record { @@ -135,7 +135,7 @@ type Seq_EA2_NameAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Name {value: "A1"} @@ -145,7 +145,7 @@ type Seq_EA2_NameAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Name {value: "A2"} @@ -157,7 +157,7 @@ type Seq_EA2_NameAnnotation record { } @Name {value: "A3"} - @Order { + @SequenceOrder { value: 3 } string[] EA3?; diff --git a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal index 9984e628..880d2fc5 100644 --- a/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_name_annotation_with_parse_type.bal @@ -31,7 +31,7 @@ type Seq_EA1_NameAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } string EA1?; @@ -41,7 +41,7 @@ type Seq_EA1_NameAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } string EA2?; @@ -51,7 +51,7 @@ type Seq_EA1_NameAnnotationWithXmlValue record { maxOccurs: 4, minOccurs: 2 } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -127,7 +127,7 @@ type XsdSequenceWithNameAnnotationWithXmlValue2 record { }; type Seq_EA2_NameAnnotationWithXmlValue record { - @Order { + @SequenceOrder { value: 1 } record { @@ -135,7 +135,7 @@ type Seq_EA2_NameAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Name {value: "A1"} @@ -145,7 +145,7 @@ type Seq_EA2_NameAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Name {value: "A2"} @@ -157,7 +157,7 @@ type Seq_EA2_NameAnnotationWithXmlValue record { } @Name {value: "A3"} - @Order { + @SequenceOrder { value: 3 } string[] EA3?; diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal index a6eacf6c..5d66cbc4 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation.bal @@ -30,7 +30,7 @@ type Seq_EA1_NamespaceAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Namespace { @@ -43,7 +43,7 @@ type Seq_EA1_NamespaceAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Namespace { @@ -60,7 +60,7 @@ type Seq_EA1_NamespaceAnnotation record { uri: "example3.com", prefix: "ea3" } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -136,7 +136,7 @@ type XsdSequenceWithNamespaceAnnotation2 record { }; type Seq_EA2_NamespaceAnnotation record { - @Order { + @SequenceOrder { value: 1 } record { @@ -144,7 +144,7 @@ type Seq_EA2_NamespaceAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Namespace { @@ -157,7 +157,7 @@ type Seq_EA2_NamespaceAnnotation record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Namespace { @@ -175,7 +175,7 @@ type Seq_EA2_NamespaceAnnotation record { uri: "example3.com", prefix: "ea3" } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; diff --git a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal index d256b73f..382cbcf4 100644 --- a/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_test_with_namespace_annotation_with_parse_type.bal @@ -33,7 +33,7 @@ type Seq_EA1_NamespaceAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Namespace { @@ -46,7 +46,7 @@ type Seq_EA1_NamespaceAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Namespace { @@ -63,7 +63,7 @@ type Seq_EA1_NamespaceAnnotationWithXmlValue record { uri: "example3.com", prefix: "ea3" } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; @@ -151,7 +151,7 @@ type XsdSequenceWithNamespaceAnnotationWithXmlValue2 record { }; type Seq_EA2_NamespaceAnnotationWithXmlValue record { - @Order { + @SequenceOrder { value: 1 } record { @@ -159,7 +159,7 @@ type Seq_EA2_NamespaceAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 1 } @Namespace { @@ -172,7 +172,7 @@ type Seq_EA2_NamespaceAnnotationWithXmlValue record { maxOccurs: 1, minOccurs: 0 } - @Order { + @SequenceOrder { value: 2 } @Namespace { @@ -189,7 +189,7 @@ type Seq_EA2_NamespaceAnnotationWithXmlValue record { uri: "example3.com", prefix: "ea3" } - @Order { + @SequenceOrder { value: 3 } string[] EA3?; diff --git a/ballerina/tests/xsd_sequence_tests.bal b/ballerina/tests/xsd_sequence_tests.bal index d4a6b1ae..382216e4 100644 --- a/ballerina/tests/xsd_sequence_tests.bal +++ b/ballerina/tests/xsd_sequence_tests.bal @@ -25,12 +25,12 @@ type XSDSequenceRecord record {| |}; type Seq_XSDSequenceRecord record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -84,17 +84,17 @@ type Seq_XSDSequenceRecordP2 record {| minOccurs: 1, maxOccurs: 3 } - @Order { + @SequenceOrder { value: 1 } int[] age; - @Order { + @SequenceOrder { value: 2 } float salary; - @Order { + @SequenceOrder { value: 3 } @Element { @@ -169,12 +169,12 @@ type XSDSequenceRecord2 record {| |}; type Seq_XSDSequenceRecord2 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -214,12 +214,12 @@ type XSDSequenceRecord3 record {| |}; type Seq_XSDSequenceRecord3 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -257,12 +257,12 @@ type XSDSequenceRecord4 record {| |}; type Seq_XSDSequenceRecord4 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -301,12 +301,12 @@ type XSDSequenceRecord5 record {| |}; type Seq_XSDSequenceRecord5 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -360,24 +360,24 @@ type XSDSequenceRecord6 record {| |}; type Seq_XSDSequenceRecord6_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord6_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -470,24 +470,24 @@ type XSDSequenceRecord7 record {| |}; type Seq_XSDSequenceRecord7_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord7_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -523,24 +523,24 @@ type XSDSequenceRecord8P2 record {| |}; type Seq_XSDSequenceRecord8_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord8_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -578,24 +578,24 @@ type XSDSequenceRecord9P record {| |}; type Seq_XSDSequenceRecord9_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord9_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -675,24 +675,24 @@ type XSDSequenceRecord10P record {| |}; type Seq_XSDSequenceRecord10_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord10_2 record {| - @Order { + @SequenceOrder { value: 1 } Rec10 name; - @Order { + @SequenceOrder { value: 2 } Rec10 status; @@ -793,24 +793,24 @@ type XSDSequenceRecord11P2 record {| |}; type Seq_XSDSequenceRecord11_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecord11_2 record {| - @Order { + @SequenceOrder { value: 1 } Rec11 name; - @Order { + @SequenceOrder { value: 2 } Rec11 status; @@ -868,13 +868,13 @@ type XSDSequenceRecord12 record { }; type Seq_XSDSequenceRecord12_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C field3; }; @@ -903,13 +903,13 @@ type Seq_C record { }; type Seq record { - @Order {value: 1} + @SequenceOrder {value: 1} string a; - @Order {value: 2} + @SequenceOrder {value: 2} string b; - @Order {value: 3} + @SequenceOrder {value: 3} string c; }; @@ -935,24 +935,24 @@ type XSDSequenceRecord13 record { }; type Seq_XSDSequenceRecord13_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A_13 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B_13 field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C_13 field3; }; type Seq_XSDSequenceRecord13_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_D_13 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_E_13 field5; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_F_13 field6; }; @@ -1005,35 +1005,35 @@ type Seq_F_13 record { }; type Seq_13 record { - @Order {value: 1} + @SequenceOrder {value: 1} string a; - @Order {value: 2} + @SequenceOrder {value: 2} string b; - @Order {value: 3} + @SequenceOrder {value: 3} string c; }; type Seq2_13 record { - @Order {value: 1} + @SequenceOrder {value: 1} string d; - @Order {value: 2} + @SequenceOrder {value: 2} string e; - @Order {value: 3} + @SequenceOrder {value: 3} string f; }; type Seq3_13 record { - @Order {value: 1} + @SequenceOrder {value: 1} string g; - @Order {value: 2} + @SequenceOrder {value: 2} string h; - @Order {value: 3} + @SequenceOrder {value: 3} string i; }; diff --git a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal index e84728ab..dcb28ee7 100644 --- a/ballerina/tests/xsd_sequence_tests_with_parse_type.bal +++ b/ballerina/tests/xsd_sequence_tests_with_parse_type.bal @@ -28,12 +28,12 @@ type XSDSequenceRecordWithXmlValue record {| |}; type Seq_XSDSequenceRecordWithXmlValue record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -47,14 +47,14 @@ function testXsdSequenceWithXmlValue() returns error? { test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.age, 13); test:assertEquals((check v).seq_XSDSequenceRecordWithXmlValue.salary, 11.1); test:assertEquals(toXml(check v), xmlValue); - Error? e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + Error? e = validate(xmlValue, XSDSequenceRecordWithXmlValue); test:assertTrue(e is ()); xmlValue = xml `11.1`; v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); - e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue); test:assertTrue(e is Error); test:assertEquals(( e).message(), "Invalid XML found: 'Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue''"); @@ -62,7 +62,7 @@ function testXsdSequenceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue'"); - e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue); test:assertTrue(e is Error); test:assertEquals(( e).message(), "Invalid XML found: 'Element(s) 'salary' is not found in 'seq_XSDSequenceRecordWithXmlValue''"); @@ -70,7 +70,7 @@ function testXsdSequenceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), ("required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML"), msg = (v).message()); - e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue); test:assertTrue(e is Error); test:assertEquals(( e).message(), "Invalid XML found: 'required field 'seq_XSDSequenceRecordWithXmlValue' not present in XML'"); @@ -78,7 +78,7 @@ function testXsdSequenceWithXmlValue() returns error? { v = parseAsType(xmlValue); test:assertTrue(v is Error); test:assertEquals((v).message(), "Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue'"); - e = validate(XSDSequenceRecordWithXmlValue, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue); test:assertTrue(e is Error); test:assertEquals(( e).message(), "Invalid XML found: 'Element 'salary' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue''"); } @@ -99,17 +99,17 @@ type Seq_XSDSequenceRecordWithXmlValueP2 record {| minOccurs: 1, maxOccurs: 3 } - @Order { + @SequenceOrder { value: 1 } int[] age; - @Order { + @SequenceOrder { value: 2 } float salary; - @Order { + @SequenceOrder { value: 3 } @Element { @@ -187,12 +187,12 @@ type XSDSequenceRecordWithXmlValue2 record {| |}; type Seq_XSDSequenceRecordWithXmlValue2 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -236,12 +236,12 @@ type XSDSequenceRecordWithXmlValue3 record {| |}; type Seq_XSDSequenceRecordWithXmlValue3 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -284,12 +284,12 @@ type XSDSequenceRecordWithXmlValue4 record {| |}; type Seq_XSDSequenceRecordWithXmlValue4 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -333,12 +333,12 @@ type XSDSequenceRecordWithXmlValue5 record {| |}; type Seq_XSDSequenceRecordWithXmlValue5 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; @@ -398,24 +398,24 @@ type XSDSequenceRecordWithXmlValue6 record {| |}; type Seq_XSDSequenceRecordWithXmlValue6_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue6_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -515,24 +515,24 @@ type XSDSequenceRecordWithXmlValue7 record {| |}; type Seq_XSDSequenceRecordWithXmlValue7_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue7_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -571,24 +571,24 @@ type XSDSequenceRecordWithXmlValue8P2 record {| |}; type Seq_XSDSequenceRecordWithXmlValue8_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue8_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -630,24 +630,24 @@ type XSDSequenceRecordWithXmlValue9P record {| |}; type Seq_XSDSequenceRecordWithXmlValue9_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue9_2 record {| - @Order { + @SequenceOrder { value: 1 } string name; - @Order { + @SequenceOrder { value: 2 } string status; @@ -734,24 +734,24 @@ type XSDSequenceRecordWithXmlValue10P record {| |}; type Seq_XSDSequenceRecordWithXmlValue10_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue10_2 record {| - @Order { + @SequenceOrder { value: 1 } Rec10 name; - @Order { + @SequenceOrder { value: 2 } Rec10 status; @@ -854,24 +854,24 @@ type XSDSequenceRecordWithXmlValue11P2 record {| |}; type Seq_XSDSequenceRecordWithXmlValue11_1 record {| - @Order { + @SequenceOrder { value: 1 } int age; - @Order { + @SequenceOrder { value: 2 } float salary; |}; type Seq_XSDSequenceRecordWithXmlValue11_2 record {| - @Order { + @SequenceOrder { value: 1 } Rec11 name; - @Order { + @SequenceOrder { value: 2 } Rec11 status; @@ -930,13 +930,13 @@ type XSDSequenceRecordWithXmlValue12 record { }; type Seq_XSDSequenceRecordWithXmlValue12_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C field3; }; @@ -966,24 +966,24 @@ type XSDSequenceRecordWithXmlValue13 record { }; type Seq_XSDSequenceRecordWithXmlValue13_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_A_13 field1; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_B_13 field2; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C_13 field3; }; type Seq_XSDSequenceRecordWithXmlValue13_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} Seq_D_13 field4; - @Order {value: 2} + @SequenceOrder {value: 2} Seq_E_13 field5; - @Order {value: 3} + @SequenceOrder {value: 3} Seq_F_13 field6; }; @@ -993,21 +993,21 @@ function testXsdSequenceWithXmlValue13() returns error? { XSDSequenceRecordWithXmlValue13|Error v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_1: {field1: {value1: {a: "1", b: "2", c: "3"}}, field2: {value2: {d: "1", e: "2", f: "3"}}, field3: {value3: {g: "1", h: "2", i: "3"}}}, seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); test:assertEquals(toXml(check v2), xml `123123123123123123`); - Error? e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + Error? e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is ()); xmlValue = xml `123123123`; v2 = parseAsType(xmlValue); test:assertEquals(v2, {seq_XSDSequenceRecordWithXmlValue13_2: {field4: {value1: {a: "1", b: "2", c: "3"}}, field5: {value2: {d: "1", e: "2", f: "3"}}, field6: {value3: {g: "1", h: "2", i: "3"}}}}); test:assertEquals(toXml(check v2), xml `123123123`); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is ()); xmlValue = xml `123123123123123123123`; v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element(s) 'field6' is not found in 'seq_XSDSequenceRecordWithXmlValue13_2'"); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is Error); test:assertEquals((e).message(), "Invalid XML found: 'Element(s) 'field6' is not found in 'seq_XSDSequenceRecordWithXmlValue13_2''"); @@ -1015,7 +1015,7 @@ function testXsdSequenceWithXmlValue13() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element(s) 'f' is not found in 'value2'"); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is Error); test:assertEquals((e).message(), "Invalid XML found: 'Element(s) 'f' is not found in 'value2''"); @@ -1023,7 +1023,7 @@ function testXsdSequenceWithXmlValue13() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element 'i' is not in the correct order in 'value3'"); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is Error); test:assertEquals((e).message(), "Invalid XML found: 'Element 'i' is not in the correct order in 'value3''"); @@ -1031,7 +1031,7 @@ function testXsdSequenceWithXmlValue13() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), "Element 'c' is not in the correct order in 'value1'"); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is Error); test:assertEquals((e).message(), "Invalid XML found: 'Element 'c' is not in the correct order in 'value1''"); @@ -1039,7 +1039,7 @@ function testXsdSequenceWithXmlValue13() returns error? { v2 = parseAsType(xmlValue); test:assertTrue(v2 is Error); test:assertEquals((v2).message(), ("Element 'field5' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue13_2'"), msg = (v2).message()); - e = validate(XSDSequenceRecordWithXmlValue13, xmlValue); + e = validate(xmlValue, XSDSequenceRecordWithXmlValue13); test:assertTrue(e is Error); test:assertEquals((e).message(), "Invalid XML found: 'Element 'field5' is not in the correct order in 'seq_XSDSequenceRecordWithXmlValue13_2''"); } diff --git a/ballerina/tests/xsd_test_toxml_tests.bal b/ballerina/tests/xsd_test_toxml_tests.bal index f9d3fcb2..eb4f5b1f 100644 --- a/ballerina/tests/xsd_test_toxml_tests.bal +++ b/ballerina/tests/xsd_test_toxml_tests.bal @@ -28,17 +28,17 @@ type ToXml1 record { }; type Seq_A_toXml record { - @Order { + @SequenceOrder { value: 3 } string c; - @Order { + @SequenceOrder { value: 1 } string a; - @Order { + @SequenceOrder { value: 2 } string b; @@ -52,17 +52,17 @@ type ToXml2 record { }; type Seq_A_toXml2 record { - @Order { + @SequenceOrder { value: 3 } string c; - @Order { + @SequenceOrder { value: 1 } string a; - @Order { + @SequenceOrder { value: 2 } string b; @@ -96,34 +96,34 @@ type ToXml3 record { }; type Seq_A_toXml3 record { - @Order { + @SequenceOrder { value: 3 } string c; - @Order { + @SequenceOrder { value: 1 } string a; - @Order { + @SequenceOrder { value: 3 } string b?; }; type Seq_A_toXml3_2 record { - @Order { + @SequenceOrder { value: 3 } string c2; - @Order { + @SequenceOrder { value: 1 } string a2; - @Order { + @SequenceOrder { value: 3 } string b2; diff --git a/ballerina/tests/xsd_test_toxml_with_namespaces.bal b/ballerina/tests/xsd_test_toxml_with_namespaces.bal index 9e3cd6e9..e0fbdcd9 100644 --- a/ballerina/tests/xsd_test_toxml_with_namespaces.bal +++ b/ballerina/tests/xsd_test_toxml_with_namespaces.bal @@ -34,7 +34,7 @@ type ToXmlWithNamespaces1 record { }; type Seq_A_toXmlWithNamespaces record { - @Order { + @SequenceOrder { value: 3 } @Namespace { @@ -47,12 +47,12 @@ type Seq_A_toXmlWithNamespaces record { uri: "http://example2.com", prefix: "ex2" } - @Order { + @SequenceOrder { value: 1 } string a; - @Order { + @SequenceOrder { value: 2 } @Namespace { @@ -75,12 +75,12 @@ type Seq_A_toXmlWithNamespaces2 record { uri: "http://example3.com", prefix: "c" } - @Order { + @SequenceOrder { value: 3 } string c; - @Order { + @SequenceOrder { value: 1 } @@ -94,7 +94,7 @@ type Seq_A_toXmlWithNamespaces2 record { uri: "http://example3.com", prefix: "b" } - @Order { + @SequenceOrder { value: 2 } string b; @@ -128,7 +128,7 @@ type ToXmlWithNamespaces3 record { }; type Seq_A_toXmlWithNamespaces3 record { - @Order { + @SequenceOrder { value: 3 } @Namespace { @@ -137,12 +137,12 @@ type Seq_A_toXmlWithNamespaces3 record { } string c; - @Order { + @SequenceOrder { value: 1 } string a; - @Order { + @SequenceOrder { value: 3 } @Namespace { @@ -153,7 +153,7 @@ type Seq_A_toXmlWithNamespaces3 record { }; type Seq_A_toXmlWithNamespaces3_2 record { - @Order { + @SequenceOrder { value: 3 } string c2; @@ -162,12 +162,12 @@ type Seq_A_toXmlWithNamespaces3_2 record { uri: "http://example3.com", prefix: "a2" } - @Order { + @SequenceOrder { value: 1 } string a2; - @Order { + @SequenceOrder { value: 3 } string b2; @@ -255,14 +255,14 @@ type ToXmlWithNamespaces6 record { }; type Seq_XSDSequenceRecordWithNamespace13_1 record { - @Order {value: 1} + @SequenceOrder {value: 1} @Namespace { uri: "http://example1.com", prefix: "field1" } Seq_A_13 field1; - @Order {value: 2} + @SequenceOrder {value: 2} @Namespace { uri: "http://example2.com", prefix: "field2" @@ -273,19 +273,19 @@ type Seq_XSDSequenceRecordWithNamespace13_1 record { uri: "http://example3.com", prefix: "field3" } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_C_13 field3; }; type Seq_XSDSequenceRecordWithNamespace13_2 record { - @Order {value: 1} + @SequenceOrder {value: 1} @Namespace { uri: "http://example4.com", prefix: "field4" } Seq_D_13 field4; - @Order {value: 2} + @SequenceOrder {value: 2} @Namespace { uri: "http://example5.com" } @@ -295,7 +295,7 @@ type Seq_XSDSequenceRecordWithNamespace13_2 record { uri: "http://example6.com", prefix: "field6" } - @Order {value: 3} + @SequenceOrder {value: 3} Seq_F_13 field6; }; diff --git a/ballerina/tests/xsd_validation_with_file_path_tests.bal b/ballerina/tests/xsd_validation_with_file_path_tests.bal index e6e666cc..fcee985b 100644 --- a/ballerina/tests/xsd_validation_with_file_path_tests.bal +++ b/ballerina/tests/xsd_validation_with_file_path_tests.bal @@ -30,10 +30,10 @@ function testValidateSchema1() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - Error? e = validate(xsdPath, validXml); + Error? e = validate(validXml, xsdPath); test:assertTrue(e is (), msg = "Valid XML should pass validation"); - e = validate(xsdPath, invalidXml); + e = validate(invalidXml, xsdPath); test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @@ -47,10 +47,10 @@ function testValidateSchema2() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - Error? e = validate(xsdPath, validXml); + Error? e = validate(validXml, xsdPath); test:assertTrue(e is (), msg = "Valid XML should pass validation"); - e = validate(xsdPath, invalidXml); + e = validate(invalidXml, xsdPath); test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @@ -64,10 +64,10 @@ function testValidateSchema3() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - Error? e = validate(xsdPath, validXml); + Error? e = validate(validXml, xsdPath); test:assertTrue(e is (), msg = "Valid XML should pass validation"); - e = validate(xsdPath, invalidXml); + e = validate(invalidXml, xsdPath); test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @@ -81,10 +81,10 @@ function testValidateSchema4() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - Error? e = validate(xsdPath, validXml); + Error? e = validate(validXml, xsdPath); test:assertTrue(e is (), msg = "Valid XML should pass validation"); - e = validate(xsdPath, invalidXml); + e = validate(invalidXml, xsdPath); test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } @@ -98,10 +98,10 @@ function testValidateSchema5() returns error? { xml validXml = check readXmlFile(validXmlPath); xml invalidXml = check readXmlFile(invalidXmlPath); - Error? e = validate(xsdPath, validXml); + Error? e = validate(validXml, xsdPath); test:assertTrue(e is (), msg = "Valid XML should pass validation"); - e = validate(xsdPath, invalidXml); + e = validate(invalidXml, xsdPath); test:assertTrue(e is Error, msg = "Invalid XML should fail validation"); test:assertTrue((e).message().includes("Invalid XML found"), msg = "Invalid XML should fail validation"); } diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index 0ad626fc..d191b5bc 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -24,15 +24,15 @@ const EMPTY_STRING = ""; # Define the configurations for min and max occurrences of members in the XML schema (XSD). public type ParticleOccurrence record {| - # Specifies the minimum number of occurrences. - int:Unsigned32 minOccurs?; - # Specifies the maximum number of occurrences. - int:Unsigned32 maxOccurs?; + # Specifies the minimum number of occurrences. + int:Unsigned32 minOccurs?; + # Specifies the maximum number of occurrences. + int:Unsigned32 maxOccurs?; |}; # Defines the configuration for an XML element in the XML schema (XSD). public type ElementConfig record {| - *ParticleOccurrence; + *ParticleOccurrence; |}; # Annotation to define schema rules for an XML element in Ballerina. @@ -56,11 +56,12 @@ public const annotation ChoiceConfig Choice on type, record field; # Defines the configuration for the sequence order in the XML schema (XSD). public type SequenceOrderConfig record {| + # The element order in the sequence int value; |}; # Annotation to define schema rules for the sequence order in Ballerina. -public const annotation SequenceOrderConfig Order on type, record field; +public const annotation SequenceOrderConfig SequenceOrder on type, record field; # Defines the name of the XML element. public type NameConfig record {| @@ -224,8 +225,8 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu if !isSingleNode(jsonValue) { addNamespaces(allNamespaces, check getNamespacesMap(jsonValue, options, {})); return getElement(rootTag ?: "root", - check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, - check getAttributesMap(jsonValue, options, allNamespaces)); + check traverseNode(jsonValue, allNamespaces, {}, options), allNamespaces, options, + check getAttributesMap(jsonValue, options, allNamespaces)); } map|error jMap = jsonValue.ensureType(); @@ -238,8 +239,8 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu addNamespaces(allNamespaces, check getNamespacesMap(value, options, {})); if value is json[] { return getElement(rootTag ?: "root", - check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), - allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); + check traverseNode(value, allNamespaces, {}, options, jMap.keys()[0]), + allNamespaces, options, check getAttributesMap(value, options, allNamespaces)); } string key = jMap.keys()[0]; @@ -247,8 +248,8 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu return xml:createText(value.toString()); } xml output = check getElement(jMap.keys()[0], check traverseNode(value, allNamespaces, {}, options), - allNamespaces, options, - check getAttributesMap(value, options, allNamespaces)); + allNamespaces, options, + check getAttributesMap(value, options, allNamespaces)); if rootTag is string { return xml:createElement(rootTag, {}, output); } @@ -279,8 +280,8 @@ isolated function traverseNode(json jNode, map allNamespaces, map allNamespaces, map allNamespaces, map namespace # xml bookXml = xml `Sample`; # Error? isValid = validate(xsdContent, bookXml); # -# // Using Ballerina record to represent XSD -# type xsdRecord record {string name;}; -# Error? isValid = validate(xsdRecord, bookXml); +# // Using a record generated from an XSD document. +# type XsdRecord record {string name;}; +# Error? isValid = validate(XsdRecord, bookXml); # ``` -public function validate(string|typedesc schema, xml xmlValue) +public function validate(xml xmlValue, string|typedesc schema) returns Error? = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; isolated function fromRecordToXml(json jsonValue, JsonOptions options, typedesc inputType) returns xml|Error diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java index 7ba659da..a174bfc0 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java @@ -213,7 +213,7 @@ public void testCompilerPluginWithXsdAnnotation3() { List errorDiagnosticsList = diagnosticResult.diagnostics().stream() .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); - Assert.assertEquals(errorDiagnosticsList.size(), 10); + Assert.assertEquals(errorDiagnosticsList.size(), 9); Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), "Invalid sequence member: Order should be defined in in all fields"); Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), @@ -227,12 +227,10 @@ public void testCompilerPluginWithXsdAnnotation3() { Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), "Invalid sequence member: Order should be defined in in all fields"); Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), - "Invalid sequence member: Order should be defined in in all fields"); - Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), + Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(9).diagnosticInfo().messageFormat(), + Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), "Invalid choice member: Choice members should be defined in a closed record"); } } diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java index e56dee18..8cec2d4b 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/XmldataRecordFieldValidator.java @@ -375,6 +375,7 @@ private void validateSequenceAnnotation(TypeSymbol typeSymbol, reportDiagnosticInfo(ctx, fieldSymbol.getLocation(), XmldataDiagnosticCodes.INVALID_SEQUENCE_TYPE); } + boolean isOrderAnnotationFound = false; for (AnnotationAttachmentSymbol annotSymbol : fieldSymbol.annotAttachments()) { AnnotationSymbol annotationSymbol = annotSymbol.typeDescriptor(); if (!isAnnotFromXmldata(annotationSymbol)) { @@ -385,11 +386,15 @@ private void validateSequenceAnnotation(TypeSymbol typeSymbol, continue; } String name = annotName.get(); - if (!name.equals(Constants.ORDER)) { - reportDiagnosticInfo(ctx, fieldSymbol - .getLocation(), XmldataDiagnosticCodes.INVALID_SEQUENCE_TYPE); + if (name.equals(Constants.ORDER)) { + isOrderAnnotationFound = true; } } + + if (!isOrderAnnotationFound) { + reportDiagnosticInfo(ctx, fieldSymbol + .getLocation(), XmldataDiagnosticCodes.INVALID_SEQUENCE_TYPE); + } } } } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java index aab06d83..bbcf00a0 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java @@ -82,7 +82,7 @@ public static Object parseStream(Environment env, BStream xml, BMap Date: Fri, 22 Nov 2024 11:00:03 +0530 Subject: [PATCH 54/58] Refactor the API docs in the validate API --- ballerina/xml_api.bal | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index d191b5bc..dc72925a 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -472,26 +472,12 @@ isolated function addNamespaces(map allNamespaces, map namespace # Validates an XML document against a provided XML schema. # -# The schema can either be a content of a XSD (as a `string`) or a Ballerina record type that represents +# The schema can either be a file path to the `.xsd` file or a Ballerina record type that represents # the XSD structure. The function checks if the `xmlValue` conforms to the provided schema. # # + schema - A `string` representing the XSD content or a Ballerina record type representing the XSD. # + xmlValue - The XML document that needs to be validated against the schema. # + return - Returns `()` if the XML is valid according to the schema, otherwise returns `Error`. -# -# # Examples -# -# ```ballerina -# string xsdContent = string ` -# -# `; -# xml bookXml = xml `Sample`; -# Error? isValid = validate(xsdContent, bookXml); -# -# // Using a record generated from an XSD document. -# type XsdRecord record {string name;}; -# Error? isValid = validate(XsdRecord, bookXml); -# ``` public function validate(xml xmlValue, string|typedesc schema) returns Error? = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; From 4272d145a2750ce56b5548ed7c662d88772a35d1 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 22 Nov 2024 11:03:45 +0530 Subject: [PATCH 55/58] Update the validate API doc parameters --- ballerina/xml_api.bal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index dc72925a..e2e1d17a 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -475,7 +475,7 @@ isolated function addNamespaces(map allNamespaces, map namespace # The schema can either be a file path to the `.xsd` file or a Ballerina record type that represents # the XSD structure. The function checks if the `xmlValue` conforms to the provided schema. # -# + schema - A `string` representing the XSD content or a Ballerina record type representing the XSD. +# + schema - A `string` representing the file path to the `.xsd` file or a Ballerina record type representing the XSD. # + xmlValue - The XML document that needs to be validated against the schema. # + return - Returns `()` if the XML is valid according to the schema, otherwise returns `Error`. public function validate(xml xmlValue, string|typedesc schema) From 85891cf878098dfffa8ef53c043405f5a61ff525 Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 22 Nov 2024 13:37:51 +0530 Subject: [PATCH 56/58] Refactor the sequence order annotation in compiler plugin --- .../ballerina_sources/sample_package_11/main.bal | 4 ++-- .../ballerina_sources/sample_package_13/main.bal | 16 ++++++++-------- .../lib/data/xmldata/compiler/Constants.java | 2 +- .../lib/data/xmldata/utils/Constants.java | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal index 17dca1bd..f449909c 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_11/main.bal @@ -190,12 +190,12 @@ type A4 record {| |}; type Seq_XsdSequenceArray record {| - @xmldata:Order { + @xmldata:SequenceOrder { value: 1 } int age; - @xmldata:Order { + @xmldata:SequenceOrder { value: 2 } float salary; diff --git a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal index 34b59c03..72fe8d8e 100644 --- a/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal +++ b/compiler-plugin-test/src/test/resources/ballerina_sources/sample_package_13/main.bal @@ -25,12 +25,12 @@ type XSDSequenceRecord record {| |}; type Seq_XSDSequenceRecord record {| - @xmldata:Order { + @xmldata:SequenceOrder { value: 1 } int age; - @xmldata:Order { + @xmldata:SequenceOrder { value: 2 } float salary; @@ -57,12 +57,12 @@ type XSDSequenceRecord2 record {| |}; type Seq_XSDSequenceRecord2 record {| - @xmldata:Order { + @xmldata:SequenceOrder { value: 1 } int age; - @xmldata:Order { + @xmldata:SequenceOrder { value: 2 } float salary; @@ -74,13 +74,13 @@ type Seq_XSDSequenceRecord2 record {| |}; type Seq_XSDSequenceRecord3 record { - @xmldata:Order { + @xmldata:SequenceOrder { value: 1 } int age; string name; - @xmldata:Order { + @xmldata:SequenceOrder { value: 2 } float salary; @@ -98,7 +98,7 @@ type XSDSequenceRecord4 record {| type Seq_XSDSequenceRecord4 record { int age; - @xmldata:Order { + @xmldata:SequenceOrder { value: 1 } int age2; @@ -106,7 +106,7 @@ type Seq_XSDSequenceRecord4 record { @xmldata:Element { minOccurs: 2 } - @xmldata:Order { + @xmldata:SequenceOrder { value: 2 } float salary; diff --git a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java index 4cd968f5..8f06b083 100644 --- a/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java +++ b/compiler-plugin/src/main/java/io/ballerina/lib/data/xmldata/compiler/Constants.java @@ -20,5 +20,5 @@ public class Constants { static final String DATA_XMLDATA = "data.xmldata"; static final String SEQUENCE = "Sequence"; static final String CHOICE = "Choice"; - static final String ORDER = "Order"; + static final String ORDER = "SequenceOrder"; } diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java index 1b6144d7..299ba33a 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java @@ -77,5 +77,5 @@ private Constants() {} public static final String CHOICE = "Choice"; public static final BString MIN_OCCURS = StringUtils.fromString("minOccurs"); public static final BString MAX_OCCURS = StringUtils.fromString("maxOccurs"); - public static final String ORDER = "Order"; + public static final String ORDER = "SequenceOrder"; } From 0044bf01ca0359f67c67b6cc74a1a874d36e66cd Mon Sep 17 00:00:00 2001 From: Sasindu Alahakoon Date: Fri, 22 Nov 2024 15:28:27 +0530 Subject: [PATCH 57/58] Refactor data utils to make function early returns --- ballerina/xml_api.bal | 17 +- .../xmldata/compiler/CompilerPluginTest.java | 108 ++++----- .../compiler/CompilerPluginTestUtils.java | 6 + .../lib/data/xmldata/utils/DataUtils.java | 205 ++++++++---------- .../lib/data/xmldata/xml/Native.java | 2 +- .../lib/data/xmldata/xml/XSDValidator.java | 10 +- 6 files changed, 176 insertions(+), 172 deletions(-) diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal index e2e1d17a..4491e9e8 100644 --- a/ballerina/xml_api.bal +++ b/ballerina/xml_api.bal @@ -36,7 +36,7 @@ public type ElementConfig record {| |}; # Annotation to define schema rules for an XML element in Ballerina. -public const annotation ElementConfig Element on type, record field; +public const annotation ElementConfig Element on record field; # Defines the configuration for an XML sequence in the XML schema (XSD). public type SequenceConfig record {| @@ -44,7 +44,7 @@ public type SequenceConfig record {| |}; # Annotation to define schema rules for an XML sequence in Ballerina. -public const annotation SequenceConfig Sequence on type, record field; +public const annotation SequenceConfig Sequence on record field; # Defines the configuration for an XML choice in the XML schema (XSD). public type ChoiceConfig record {| @@ -52,7 +52,7 @@ public type ChoiceConfig record {| |}; # Annotation to define schema rules for an XML choice in Ballerina. -public const annotation ChoiceConfig Choice on type, record field; +public const annotation ChoiceConfig Choice on record field; # Defines the configuration for the sequence order in the XML schema (XSD). public type SequenceOrderConfig record {| @@ -61,7 +61,7 @@ public type SequenceOrderConfig record {| |}; # Annotation to define schema rules for the sequence order in Ballerina. -public const annotation SequenceOrderConfig SequenceOrder on type, record field; +public const annotation SequenceOrderConfig SequenceOrder on record field; # Defines the name of the XML element. public type NameConfig record {| @@ -470,14 +470,15 @@ isolated function addNamespaces(map allNamespaces, map namespace } } -# Validates an XML document against a provided XML schema. +# Validates an XML document against an XML schema. # # The schema can either be a file path to the `.xsd` file or a Ballerina record type that represents -# the XSD structure. The function checks if the `xmlValue` conforms to the provided schema. +# the schema defined by the XSD. The function checks if the XML value conforms to the specified schema. # -# + schema - A `string` representing the file path to the `.xsd` file or a Ballerina record type representing the XSD. +# + schema - A string representing the file path to the `.xsd` file or a +# Ballerina record type representing the schema defined by the XSD. # + xmlValue - The XML document that needs to be validated against the schema. -# + return - Returns `()` if the XML is valid according to the schema, otherwise returns `Error`. +# + return - Returns `()` if the XML value is valid according to the schema, otherwise returns `Error`. public function validate(xml xmlValue, string|typedesc schema) returns Error? = @java:Method {'class: "io.ballerina.lib.data.xmldata.xml.Native"} external; diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java index a174bfc0..7e122408 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTest.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.stream.Collectors; +import static io.ballerina.lib.data.xmldata.compiler.CompilerPluginTestUtils.getErrorMessage; + /** * This class includes tests for Ballerina Xmldata compiler plugin. */ @@ -39,7 +41,7 @@ public void testDuplicateFieldNegative1() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 1); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid field: duplicate field found"); } @@ -51,7 +53,7 @@ public void testDuplicateFieldNegative2() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 1); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid field: duplicate field found"); } @@ -63,7 +65,7 @@ public void testChildRecordWithNameAnnotNegative() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.WARNING)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 1); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid annotation attachment: child record does not allow name annotation"); } @@ -75,13 +77,13 @@ public void testDuplicateFieldInInlineRecordsNegative() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 4); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 1), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 2), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 3), "invalid field: duplicate field found"); } @@ -93,17 +95,17 @@ public void testUnionTypeNegative() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 6); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid type: expected a record type"); - Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 1), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 2), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 3), "invalid type: expected a record type"); - Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 4), "invalid field: duplicate field found"); - Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 5), "invalid field: duplicate field found"); } @@ -115,7 +117,7 @@ public void testCompilerPluginWithAProjectWithSubModule() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 1); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid field: duplicate field found"); List warningDiagnosticsList = diagnosticResult.diagnostics().stream() @@ -134,53 +136,53 @@ public void testCompilerPluginWithXsdAnnotation() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 24); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 1), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 2), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 3), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 4), "Invalid sequence member: Sequence members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 5), "Invalid sequence member: Sequence members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 6), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 7), "Invalid sequence member: Sequence members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 8), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(9).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 9), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(10).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 10), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(11).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 11), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(12).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 12), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(13).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 13), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(14).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 14), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(15).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 15), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(16).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 16), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(17).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 17), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(18).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 18), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); - Assert.assertEquals(errorDiagnosticsList.get(19).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 19), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); - Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 20), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); - Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 21), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); - Assert.assertEquals(errorDiagnosticsList.get(13).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 22), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(20).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 23), "A record field cannot contains sequence/choice/element/attribute annotations simultaneously"); } @@ -192,17 +194,17 @@ public void testCompilerPluginWithXsdAnnotation2() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 6); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 1), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 2), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 3), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 4), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 5), "invalid xsd annotation: record type or record array type expected"); } @@ -214,23 +216,23 @@ public void testCompilerPluginWithXsdAnnotation3() { .filter(r -> r.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) .collect(Collectors.toList()); Assert.assertEquals(errorDiagnosticsList.size(), 9); - Assert.assertEquals(errorDiagnosticsList.get(0).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 0), "Invalid sequence member: Order should be defined in in all fields"); - Assert.assertEquals(errorDiagnosticsList.get(1).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 1), "Invalid sequence member: Sequence members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(2).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 2), "Invalid sequence member: Order should be defined in in all fields"); - Assert.assertEquals(errorDiagnosticsList.get(3).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 3), "Invalid sequence member: Sequence members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(4).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 4), "Invalid sequence member: Order should be defined in in all fields"); - Assert.assertEquals(errorDiagnosticsList.get(5).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 5), "Invalid sequence member: Order should be defined in in all fields"); - Assert.assertEquals(errorDiagnosticsList.get(6).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 6), "Invalid choice member: Choice members should be defined in a closed record"); - Assert.assertEquals(errorDiagnosticsList.get(7).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 7), "invalid xsd annotation: record type or record array type expected"); - Assert.assertEquals(errorDiagnosticsList.get(8).diagnosticInfo().messageFormat(), + Assert.assertEquals(getErrorMessage(errorDiagnosticsList, 8), "Invalid choice member: Choice members should be defined in a closed record"); } } diff --git a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTestUtils.java b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTestUtils.java index b54f3a37..280959b2 100644 --- a/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTestUtils.java +++ b/compiler-plugin-test/src/test/java/io/ballerina/lib/data/xmldata/compiler/CompilerPluginTestUtils.java @@ -23,9 +23,11 @@ import io.ballerina.projects.directory.BuildProject; import io.ballerina.projects.environment.Environment; import io.ballerina.projects.environment.EnvironmentBuilder; +import io.ballerina.tools.diagnostics.Diagnostic; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; /** * Utility functions related to compiler plugins tests. @@ -43,4 +45,8 @@ static Package loadPackage(String path) { BuildProject project = BuildProject.load(projectEnvironmentBuilder, projectDirPath); return project.currentPackage(); } + + static String getErrorMessage(List errorDiagnosticsList, int index) { + return errorDiagnosticsList.get(index).diagnosticInfo().messageFormat(); + } } 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 e85b5ae2..5615006e 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 @@ -525,14 +525,14 @@ private static BMap addFields(BMap input, Type static BString[] getOrderedRecordKeysIfXsdSequencePresent(BMap input, HashMap xsdSequencePriorityOrder) { HashMap localPartKeys = getLocalPartKeys(input); - if (!xsdSequencePriorityOrder.isEmpty()) { + if (xsdSequencePriorityOrder.isEmpty()) { + return input.getKeys(); + } else { return xsdSequencePriorityOrder.entrySet().stream() .filter(entry -> localPartKeys.containsKey(entry.getKey())) .sorted(Comparator.comparingInt(Map.Entry::getValue)) .map(entry -> StringUtils.fromString(localPartKeys.get(entry.getKey()))) .toArray(BString[]::new); - } else { - return input.getKeys(); } } @@ -1144,19 +1144,22 @@ public static HashMap getXsdSequencePriorityOrder(Type type, bo BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { - BMap fieldAnnotationValue = - (BMap) fieldAnnotation.get(fieldAnnotationKey); - elementPriorityOrder.put(fieldName, - fieldAnnotationValue.getIntValue(Constants.VALUE).intValue()); - } - } + if (!key.contains(Constants.FIELD)) { + continue; + } + + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (!fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + continue; + } + if (fieldAnnotationKeyStr.endsWith(Constants.ORDER)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + elementPriorityOrder.put(fieldName, + fieldAnnotationValue.getIntValue(Constants.VALUE).intValue()); } } } @@ -1169,30 +1172,33 @@ public static HashMap getFieldNamesWithModelGroupAnnotat BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - if (elementNamesMap.containsKey(fieldName)) { - fieldName = elementNamesMap.get(fieldName); + if (!key.contains(Constants.FIELD)) { + continue; + } + + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + if (elementNamesMap.containsKey(fieldName)) { + fieldName = elementNamesMap.get(fieldName); + } + + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (!fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + continue; + } + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + SequenceInfo sequenceInfo = new SequenceInfo(fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey), + fieldType, null); + fieldNamesWithModelGroupAnnotations.put(fieldName, sequenceInfo); } - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { - SequenceInfo sequenceInfo = new SequenceInfo(fieldName, - (BMap) fieldAnnotation.get(fieldAnnotationKey), - fieldType, null); - fieldNamesWithModelGroupAnnotations.put(fieldName, sequenceInfo); - } - - if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { - ChoiceInfo choiceInfo = new ChoiceInfo(fieldName, - (BMap) fieldAnnotation.get(fieldAnnotationKey), - fieldType, null); - fieldNamesWithModelGroupAnnotations.put(fieldName, choiceInfo); - } - } + if (fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { + ChoiceInfo choiceInfo = new ChoiceInfo(fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey), + fieldType, null); + fieldNamesWithModelGroupAnnotations.put(fieldName, choiceInfo); } } } @@ -1205,22 +1211,26 @@ public static HashMap getFieldNamesWithElementGroupAnnotati BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - if (elementNamesMap.containsKey(fieldName)) { - fieldName = elementNamesMap.get(fieldName); + if (!key.contains(Constants.FIELD)) { + continue; + } + + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + if (elementNamesMap.containsKey(fieldName)) { + fieldName = elementNamesMap.get(fieldName); + } + + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (!fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + continue; } - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { - ElementInfo elementInfo = new ElementInfo(xmlElementName, fieldName, - (BMap) fieldAnnotation.get(fieldAnnotationKey)); - fieldNamesWithElementInfoAnnotations.put(fieldName, elementInfo); - } - } + if (fieldAnnotationKeyStr.endsWith(Constants.ELEMENT)) { + ElementInfo elementInfo = new ElementInfo(xmlElementName, fieldName, + (BMap) fieldAnnotation.get(fieldAnnotationKey)); + fieldNamesWithElementInfoAnnotations.put(fieldName, elementInfo); } } } @@ -1234,19 +1244,23 @@ public static ArrayList getFieldNamesWithSequenceAnnotations(RecordType BMap annotations = fieldType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { - if (elementNamesMap.containsKey(fieldName)) { - fieldNamesWithModelGroupAnnotations.add(elementNamesMap.get(fieldName)); - } else { - fieldNamesWithModelGroupAnnotations.add(fieldName); - } - } + if (!key.contains(Constants.FIELD)) { + continue; + } + + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (!fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + continue; + } + + if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE)) { + if (elementNamesMap.containsKey(fieldName)) { + fieldNamesWithModelGroupAnnotations.add(elementNamesMap.get(fieldName)); + } else { + fieldNamesWithModelGroupAnnotations.add(fieldName); } } } @@ -1260,20 +1274,23 @@ public static HashMap getElementNameMap(Type type) { BMap annotations = recordType.getAnnotations(); for (BString annotationKey : annotations.getKeys()) { String key = annotationKey.getValue(); - if (key.contains(Constants.FIELD)) { - String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); - Map fieldAnnotation = (Map) annotations.get(annotationKey); - for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { - String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); - if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { - if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { - BMap fieldAnnotationValue = - (BMap) fieldAnnotation.get(fieldAnnotationKey); - String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue - .getStringValue(Constants.VALUE)); - names.put(xmlElementName, fieldName); - } - } + if (!key.contains(Constants.FIELD)) { + continue; + } + String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); + Map fieldAnnotation = (Map) annotations.get(annotationKey); + for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { + String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); + if (!fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { + continue; + } + + if (fieldAnnotationKeyStr.endsWith(Constants.NAME)) { + BMap fieldAnnotationValue = + (BMap) fieldAnnotation.get(fieldAnnotationKey); + String xmlElementName = StringUtils.getStringValue(fieldAnnotationValue + .getStringValue(Constants.VALUE)); + names.put(xmlElementName, fieldName); } } } @@ -1282,36 +1299,6 @@ public static HashMap getElementNameMap(Type type) { return names; } -// public static BMap getXmlElementModelGroupMap(BTypedesc typed) { -// Type type = TypeUtils.getReferredType(typed.getDescribingType()); -// if (type.getTag() != TypeTags.RECORD_TYPE_TAG) { -// return null; -// } -// -// BMap xmlModelGroupMap = ValueCreator -// .createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_BOOLEAN)); -// RecordType recordType = (RecordType) type; -// BMap annotations = recordType.getAnnotations(); -// for (BString annotationKey : annotations.getKeys()) { -// String key = annotationKey.getValue(); -// if (key.contains(Constants.FIELD)) { -// String fieldName = key.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", ""); -// Map fieldAnnotation = (Map) annotations.get(annotationKey); -// for (BString fieldAnnotationKey : fieldAnnotation.keySet()) { -// String fieldAnnotationKeyStr = fieldAnnotationKey.getValue(); -// if (fieldAnnotationKeyStr.startsWith(Constants.MODULE_NAME)) { -// if (fieldAnnotationKeyStr.endsWith(Constants.SEQUENCE) -// || fieldAnnotationKeyStr.endsWith(Constants.CHOICE)) { -// xmlModelGroupMap.put(StringUtils.fromString(fieldName), true); -// } else { -// xmlModelGroupMap.put(StringUtils.fromString(fieldName), false); -// } -// } -// } -// } -// } -// return xmlModelGroupMap; -// } /** * Holds data required for the processing. * diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java index bbcf00a0..b363a8cc 100644 --- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java +++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java @@ -84,7 +84,7 @@ public static Object parseStream(Environment env, BStream xml, BMap Date: Fri, 22 Nov 2024 17:48:29 +0530 Subject: [PATCH 58/58] Update ballerina/tests/xsd_choice_array_test.bal --- ballerina/tests/xsd_choice_array_test.bal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/tests/xsd_choice_array_test.bal b/ballerina/tests/xsd_choice_array_test.bal index 7899db5b..d5e98c41 100644 --- a/ballerina/tests/xsd_choice_array_test.bal +++ b/ballerina/tests/xsd_choice_array_test.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). +// 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