From b0459b8e93cbbfcad0b7e7371e69bbd320207848 Mon Sep 17 00:00:00 2001 From: mindula Date: Thu, 18 Apr 2024 14:14:27 +0530 Subject: [PATCH 1/9] Fix record generation for xml with multiple namespaces --- .../XMLToRecordConverter.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 1e37315a68ff..e1a80b261601 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -61,6 +61,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -237,6 +238,8 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole List diagnosticMessages, String textFieldName, boolean withNameSpace) { List recordFields = new ArrayList<>(); + Map prefixMap = new HashMap<>(); + int suffix = 1; String xmlNodeName = xmlElement.getNodeName(); org.w3c.dom.NodeList xmlNodeList = xmlElement.getChildNodes(); @@ -259,14 +262,27 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole if (indexOfRecordFieldNode == -1) { recordFields.add(recordField); } else { - RecordFieldNode existingRecordField = - (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); - RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); - recordFields.add(indexOfRecordFieldNode, updatedRecordField); + boolean isSameField = prefixMap.entrySet().stream().anyMatch(entry -> + entry.getKey().equals(xmlElementNode.getPrefix()) && + entry.getValue().equals(xmlElementNode.getLocalName())); + if (withNameSpace && !isSameField && prefixMap.containsValue(xmlElementNode.getLocalName())) { + recordFields.add(recordField.modify().withFieldName( + AbstractNodeFactory.createIdentifierToken(xmlElementNode.getLocalName() + + suffix)).apply()); + suffix++; + } else { + RecordFieldNode existingRecordField = + (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); + RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); + recordFields.add(indexOfRecordFieldNode, updatedRecordField); + } } } else { recordFields.add(recordField); } + if (xmlElementNode.getPrefix() != null) { + prefixMap.put(xmlElementNode.getPrefix(), xmlElementNode.getLocalName()); + } } } NamedNodeMap xmlAttributesMap = xmlElement.getAttributes(); From 14bd5718abf6745e5cc4e526cb22b8c11a8af00a Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 6 May 2024 16:04:33 +0530 Subject: [PATCH 2/9] Fix suggesting incorrect records when there are multiple namespaces --- .../XMLToRecordConverter.java | 101 +++++++++++++----- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index e1a80b261601..53612d2f8ca6 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -51,6 +51,7 @@ import org.ballerinalang.formatter.core.FormatterException; import org.ballerinalang.formatter.core.options.ForceFormattingOptions; import org.ballerinalang.formatter.core.options.FormattingOptions; +import org.javatuples.Pair; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -238,8 +239,6 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole List diagnosticMessages, String textFieldName, boolean withNameSpace) { List recordFields = new ArrayList<>(); - Map prefixMap = new HashMap<>(); - int suffix = 1; String xmlNodeName = xmlElement.getNodeName(); org.w3c.dom.NodeList xmlNodeList = xmlElement.getChildNodes(); @@ -253,35 +252,44 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole generateRecords(xmlElementNode, isClosed, recordToTypeDescNodes, recordToAnnotationNodes, recordToElementNodes, diagnosticMessages, textFieldName, withNameSpace); } - RecordFieldNode recordField = getRecordField(xmlElementNode, false, withNameSpace); - if (recordFields.stream().anyMatch(recField -> ((RecordFieldNode) recField).fieldName().text() - .equals(recordField.fieldName().text()))) { + Map prefixMap = hasMultipleFieldsWithSameName(xmlNodeList, + xmlElementNode.getLocalName()); + RecordFieldNode recordField = getRecordField(xmlElementNode, false, withNameSpace, + prefixMap.size() > 1); + + if (withNameSpace && xmlElementNode.getPrefix() != null) { int indexOfRecordFieldNode = IntStream.range(0, recordFields.size()) .filter(j -> ((RecordFieldNode) recordFields.get(j)).fieldName().text() - .equals(recordField.fieldName().text())).findFirst().orElse(-1); + .equals(recordField.fieldName().text()) + && prefixMap.get(xmlElementNode.getPrefix())).findFirst().orElse(-1); if (indexOfRecordFieldNode == -1) { - recordFields.add(recordField); + if (prefixMap.size() > 1) { + addRecordField(recordFields, xmlElementNode, recordField); + } else { + recordFields.add(recordField); + } } else { - boolean isSameField = prefixMap.entrySet().stream().anyMatch(entry -> - entry.getKey().equals(xmlElementNode.getPrefix()) && - entry.getValue().equals(xmlElementNode.getLocalName())); - if (withNameSpace && !isSameField && prefixMap.containsValue(xmlElementNode.getLocalName())) { - recordFields.add(recordField.modify().withFieldName( - AbstractNodeFactory.createIdentifierToken(xmlElementNode.getLocalName() - + suffix)).apply()); - suffix++; + RecordFieldNode existingRecordField = + (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); + RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); + if (prefixMap.size() > 1) { + addRecordField(recordFields, xmlElementNode, updatedRecordField); } else { - RecordFieldNode existingRecordField = - (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); - RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); recordFields.add(indexOfRecordFieldNode, updatedRecordField); } } } else { - recordFields.add(recordField); - } - if (xmlElementNode.getPrefix() != null) { - prefixMap.put(xmlElementNode.getPrefix(), xmlElementNode.getLocalName()); + int indexOfRecordFieldNode = IntStream.range(0, recordFields.size()) + .filter(j -> ((RecordFieldNode) recordFields.get(j)).fieldName().text() + .equals(recordField.fieldName().text())).findFirst().orElse(-1); + if (indexOfRecordFieldNode == -1) { + recordFields.add(recordField); + } else { + RecordFieldNode existingRecordField = + (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); + RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); + recordFields.add(indexOfRecordFieldNode, updatedRecordField); + } } } } @@ -331,6 +339,43 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole return recordFields; } + private static void addRecordField(List recordFields, Element xmlElementNode, RecordFieldNode recordField) { + recordFields.add(recordField.modify().withFieldName( + AbstractNodeFactory.createIdentifierToken( + xmlElementNode.getPrefix() + + xmlElementNode.getLocalName().substring(0, 1).toUpperCase() + + xmlElementNode.getLocalName().substring(1))).apply()); + } + + /** + * This method checks whether there are multiple fields with the same name in the provided NodeList. + * + * @param xmlNodeList NodeList of XML nodes + * @param fieldName Field name to be checked + * @return {@link Pair} of List of prefixes and a boolean value indicating whether multiple fields with the same + * name exist + */ + private static Map hasMultipleFieldsWithSameName(org.w3c.dom.NodeList xmlNodeList, + String fieldName) { + String defaultNamespace = ""; + Map prefixMap = new HashMap<>(); + for (int i = 0; i < xmlNodeList.getLength(); i++) { + org.w3c.dom.Node xmlNode = xmlNodeList.item(i); + if (xmlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { + Element xmlElementNode = (Element) xmlNode; + if (xmlElementNode.getLocalName().equals(fieldName)) { + String prefix = xmlElementNode.getPrefix() == null ? defaultNamespace : xmlElementNode.getPrefix(); + if (prefixMap.keySet().contains(prefix)) { + prefixMap.put(prefix, true); + } else { + prefixMap.put(prefix, false); + } + } + } + } + return prefixMap; + } + /** * This method returns a List of RecordFieldNodes that are updated if already a Record with same name exists. * @@ -409,7 +454,7 @@ private static List updateRecordFields(Map previo } private static RecordFieldNode getRecordField(Element xmlElementNode, boolean isOptionalField, - boolean withNameSpace) { + boolean withNameSpace, boolean sameFieldExists) { Token typeName; Token questionMarkToken = AbstractNodeFactory.createToken(SyntaxKind.QUESTION_MARK_TOKEN); IdentifierToken fieldName = @@ -425,8 +470,14 @@ private static RecordFieldNode getRecordField(Element xmlElementNode, boolean is String type = getRecordName(elementKey); typeName = AbstractNodeFactory.createIdentifierToken(type); } - List xmlNameNode = List.of(getXMLNamespaceNode(xmlElementNode.getPrefix(), - xmlElementNode.getNamespaceURI())); + List xmlNameNode = new ArrayList<>(); + if (sameFieldExists) { + xmlNameNode.add(getXMLNameNode(xmlElementNode.getLocalName())); + xmlNameNode.add(getXMLNamespaceNode(xmlElementNode.getPrefix(), xmlElementNode.getNamespaceURI())); + } else { + xmlNameNode.add(getXMLNamespaceNode(xmlElementNode.getPrefix(), xmlElementNode.getNamespaceURI())); + } + NodeList annotationNodes = NodeFactory.createNodeList(xmlNameNode); MetadataNode metadataNode = NodeFactory.createMetadataNode(null, annotationNodes); TypeDescriptorNode fieldTypeName = NodeFactory.createBuiltinSimpleNameReferenceNode(typeName.kind(), typeName); From 93cf184637b9925de221fc7d6a8da5319a574780 Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 6 May 2024 16:05:42 +0530 Subject: [PATCH 3/9] Add tests --- .../XMLToRecordConverterTests.java | 56 +++++++++++++++++++ .../test/resources/ballerina/sample_28.bal | 17 ++++++ .../test/resources/ballerina/sample_29.bal | 23 ++++++++ .../test/resources/ballerina/sample_30.bal | 24 ++++++++ .../test/resources/ballerina/sample_31.bal | 24 ++++++++ .../src/test/resources/xml/sample_26.xml | 12 ---- .../src/test/resources/xml/sample_28.xml | 7 +++ .../src/test/resources/xml/sample_29.xml | 9 +++ .../src/test/resources/xml/sample_30.xml | 14 +++++ .../src/test/resources/xml/sample_31.xml | 22 ++++++++ 10 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal create mode 100644 misc/xml-to-record-converter/src/test/resources/ballerina/sample_29.bal create mode 100644 misc/xml-to-record-converter/src/test/resources/ballerina/sample_30.bal create mode 100644 misc/xml-to-record-converter/src/test/resources/ballerina/sample_31.bal delete mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_28.xml create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_29.xml create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_30.xml create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_31.xml diff --git a/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java b/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java index 7a3d4b0d8346..d6c78f22e176 100644 --- a/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java +++ b/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java @@ -179,6 +179,26 @@ public class XMLToRecordConverterTests { private final Path sample27Bal = RES_DIR.resolve(BAL_DIR) .resolve("sample_27.bal"); + private final Path sample28XML = RES_DIR.resolve(XML_DIR) + .resolve("sample_28.xml"); + private final Path sample28Bal = RES_DIR.resolve(BAL_DIR) + .resolve("sample_28.bal"); + + private final Path sample29XML = RES_DIR.resolve(XML_DIR) + .resolve("sample_29.xml"); + private final Path sample29Bal = RES_DIR.resolve(BAL_DIR) + .resolve("sample_29.bal"); + + private final Path sample30XML = RES_DIR.resolve(XML_DIR) + .resolve("sample_30.xml"); + private final Path sample30Bal = RES_DIR.resolve(BAL_DIR) + .resolve("sample_30.bal"); + + private final Path sample31XML = RES_DIR.resolve(XML_DIR) + .resolve("sample_31.xml"); + private final Path sample31Bal = RES_DIR.resolve(BAL_DIR) + .resolve("sample_31.bal"); + private static final String XMLToRecordServiceEP = "xmlToRecord/convert"; @@ -407,6 +427,42 @@ public void testXMLWithoutNamespaces() throws IOException { Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); } + @Test(description = "testXMLWithMultipleNamespacesAndSameElement") + public void testXMLWithMultipleNamespacesAndSameElement() throws IOException { + String xmlFileContent = Files.readString(sample28XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample28Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithMultipleNamespaces2") + public void testXMLWithMultipleNamespaces2() throws IOException { + String xmlFileContent = Files.readString(sample29XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample29Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithMultipleNamespaces3") + public void testXMLWithMultipleNamespaces3() throws IOException { + String xmlFileContent = Files.readString(sample30XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample30Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithoutNamespaceAnnotation") + public void testXMLWithoutNamespaceAnnotation() throws IOException { + String xmlFileContent = Files.readString(sample31XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, false).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample31Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + @Test(description = "testXMLToRecordService") public void testXMLToRecordService() throws IOException, ExecutionException, InterruptedException { Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal new file mode 100644 index 000000000000..936559f6d9c0 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal @@ -0,0 +1,17 @@ +@xmldata:Name {value: "book"} +@xmldata:Namespace {prefix: "bk", uri: "http://example.com/book"} +type Bk_Book record { + @xmldata:Name {value: "title"} + @xmldata:Namespace {prefix: "bk", uri: "http://example.com/book"} + string bkTitle; + @xmldata:Name {value: "title"} + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + string auTitle; + @xmldata:Name {value: "title"} + @xmldata:Namespace {prefix: "da", uri: "http://example.com/date"} + string daTitle; + @xmldata:Namespace {prefix: "bk", uri: "http://example.com/book"} + int chapter; + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + string country; +}; \ No newline at end of file diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_29.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_29.bal new file mode 100644 index 000000000000..ab6fb4a8f745 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_29.bal @@ -0,0 +1,23 @@ +type Lib_Book record { + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + int id; + @xmldata:Name {value: "title"} + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + string libTitle; + @xmldata:Name {value: "title"} + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + string auTitle; + @xmldata:Name {value: "author"} + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + string libAuthor; + @xmldata:Name {value: "author"} + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + string auAuthor; +}; + +@xmldata:Name {value: "library"} +@xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} +type Lib_Library record { + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + Lib_Book book; +}; diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_30.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_30.bal new file mode 100644 index 000000000000..322dc2942b64 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_30.bal @@ -0,0 +1,24 @@ +type Lib_Book record { + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + string[] title; +}; + +type Au_Author record { + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + string[] name; +}; + +type Pr_Price record { + @xmldata:Namespace {prefix: "pr", uri: "http://example.com/price"} + (decimal|int)[] price; +}; + +@xmldata:Name {value: "library"} +type Library record { + @xmldata:Namespace {prefix: "lib", uri: "http://example.com/library"} + Lib_Book book; + @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} + Au_Author author; + @xmldata:Namespace {prefix: "pr", uri: "http://example.com/price"} + Pr_Price price; +}; diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_31.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_31.bal new file mode 100644 index 000000000000..3f1908559ce6 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_31.bal @@ -0,0 +1,24 @@ +type St_Product record { + string[] name; +}; + +type Mk_Brand record { + string name; + string counry; + int year; +}; + +type Mk_Maker record { + Mk_Brand[] brand; +}; + +type Pr_Cost record { + int[] amount; +}; + +@xmldata:Name {value: "store"} +type Store record { + St_Product product; + Mk_Maker maker; + Pr_Cost cost; +}; diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml deleted file mode 100644 index 6d381a7007a9..000000000000 --- a/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml +++ /dev/null @@ -1,12 +0,0 @@ - - 123456 - The Great Gatsby - F. Scott Fitzgerald - Fiction - 10.99 - 1925-04-10 - - A classic novel depicting the roaring twenties in America, - exploring themes of love, wealth, and the American Dream. - - diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_28.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_28.xml new file mode 100644 index 000000000000..c3f43f8dc2f3 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_28.xml @@ -0,0 +1,7 @@ + + string + string + string + 1 + string + diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_29.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_29.xml new file mode 100644 index 000000000000..bacd00eabd55 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_29.xml @@ -0,0 +1,9 @@ + + + 601971 + string + Title + Name + Name + + diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_30.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_30.xml new file mode 100644 index 000000000000..5ccd52b6c41f --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_30.xml @@ -0,0 +1,14 @@ + + + string + string + + + string + string + + + 1000 + 1000.50 + + diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_31.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_31.xml new file mode 100644 index 000000000000..9ff9fd2ca2cc --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_31.xml @@ -0,0 +1,22 @@ + + + Laptop + Desktop + + + + Dell + USA + 2024 + + + HP + USA + 2023 + + + + 1000 + 800 + + From 72376e9832bf48d537fad378d0d0455f79990259 Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 6 May 2024 16:14:32 +0530 Subject: [PATCH 4/9] Fix checkstyle failure --- .../ballerina/xmltorecordconverter/XMLToRecordConverter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 53612d2f8ca6..86fc3c16a984 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -66,6 +66,7 @@ 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.function.Function; @@ -343,7 +344,7 @@ private static void addRecordField(List recordFields, Element xmlElementNo recordFields.add(recordField.modify().withFieldName( AbstractNodeFactory.createIdentifierToken( xmlElementNode.getPrefix() + - xmlElementNode.getLocalName().substring(0, 1).toUpperCase() + + xmlElementNode.getLocalName().substring(0, 1).toUpperCase(Locale.ENGLISH)+ xmlElementNode.getLocalName().substring(1))).apply()); } From 3ea28f2e763dd1681748b0949a574c54fb421141 Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 6 May 2024 16:18:53 +0530 Subject: [PATCH 5/9] Update the doc --- .../ballerina/xmltorecordconverter/XMLToRecordConverter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 86fc3c16a984..545d365cf92d 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -353,8 +353,7 @@ private static void addRecordField(List recordFields, Element xmlElementNo * * @param xmlNodeList NodeList of XML nodes * @param fieldName Field name to be checked - * @return {@link Pair} of List of prefixes and a boolean value indicating whether multiple fields with the same - * name exist + * @return {@link Map} Map of prefixes and whether there are multiple fields with the same name */ private static Map hasMultipleFieldsWithSameName(org.w3c.dom.NodeList xmlNodeList, String fieldName) { From 43d3db25744ec0853a94e931bfefef1e04ba05e2 Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 6 May 2024 22:59:46 +0530 Subject: [PATCH 6/9] Fix checkstyle issues --- .../ballerina/xmltorecordconverter/XMLToRecordConverter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 545d365cf92d..f572ca3b1ccb 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -51,7 +51,6 @@ import org.ballerinalang.formatter.core.FormatterException; import org.ballerinalang.formatter.core.options.ForceFormattingOptions; import org.ballerinalang.formatter.core.options.FormattingOptions; -import org.javatuples.Pair; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -344,7 +343,7 @@ private static void addRecordField(List recordFields, Element xmlElementNo recordFields.add(recordField.modify().withFieldName( AbstractNodeFactory.createIdentifierToken( xmlElementNode.getPrefix() + - xmlElementNode.getLocalName().substring(0, 1).toUpperCase(Locale.ENGLISH)+ + xmlElementNode.getLocalName().substring(0, 1).toUpperCase(Locale.ENGLISH) + xmlElementNode.getLocalName().substring(1))).apply()); } From e3b8306ccd23ede0dfdee3ecf3d4eef8159d36ab Mon Sep 17 00:00:00 2001 From: mindula Date: Mon, 3 Jun 2024 11:18:56 +0530 Subject: [PATCH 7/9] Address review suggestions --- .../XMLToRecordConverter.java | 18 ++++++++---------- .../src/test/resources/ballerina/sample_28.bal | 2 +- .../src/test/resources/xml/sample_26.xml | 12 ++++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index f572ca3b1ccb..697671fb54ca 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -360,17 +360,15 @@ private static Map hasMultipleFieldsWithSameName(org.w3c.dom.No Map prefixMap = new HashMap<>(); for (int i = 0; i < xmlNodeList.getLength(); i++) { org.w3c.dom.Node xmlNode = xmlNodeList.item(i); - if (xmlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { - Element xmlElementNode = (Element) xmlNode; - if (xmlElementNode.getLocalName().equals(fieldName)) { - String prefix = xmlElementNode.getPrefix() == null ? defaultNamespace : xmlElementNode.getPrefix(); - if (prefixMap.keySet().contains(prefix)) { - prefixMap.put(prefix, true); - } else { - prefixMap.put(prefix, false); - } - } + if (xmlNode.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { + continue; + } + Element xmlElementNode = (Element) xmlNode; + if (!xmlNode.getLocalName().equals(fieldName)) { + continue; } + String prefix = xmlElementNode.getPrefix() == null ? defaultNamespace : xmlElementNode.getPrefix(); + prefixMap.put(prefix, prefixMap.containsKey(prefix)); } return prefixMap; } diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal index 936559f6d9c0..47ea3600db47 100644 --- a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_28.bal @@ -14,4 +14,4 @@ type Bk_Book record { int chapter; @xmldata:Namespace {prefix: "au", uri: "http://example.com/author"} string country; -}; \ No newline at end of file +}; diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml new file mode 100644 index 000000000000..6d381a7007a9 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_26.xml @@ -0,0 +1,12 @@ + + 123456 + The Great Gatsby + F. Scott Fitzgerald + Fiction + 10.99 + 1925-04-10 + + A classic novel depicting the roaring twenties in America, + exploring themes of love, wealth, and the American Dream. + + From 5b16973d16e45e58802f227e7afe39c5a1009efb Mon Sep 17 00:00:00 2001 From: mindula Date: Tue, 4 Jun 2024 10:08:00 +0530 Subject: [PATCH 8/9] Address more review suggestions --- .../XMLToRecordConverter.java | 7 +- .../XMLToRecordConverterTests.java | 86 +++++++++++-------- .../test/resources/ballerina/sample_32.bal | 11 +++ .../src/test/resources/xml/sample_32.xml | 17 ++++ 4 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 misc/xml-to-record-converter/src/test/resources/ballerina/sample_32.bal create mode 100644 misc/xml-to-record-converter/src/test/resources/xml/sample_32.xml diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 697671fb54ca..132943cd1b7d 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -264,7 +264,7 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole && prefixMap.get(xmlElementNode.getPrefix())).findFirst().orElse(-1); if (indexOfRecordFieldNode == -1) { if (prefixMap.size() > 1) { - addRecordField(recordFields, xmlElementNode, recordField); + generateRecordFieldForSameLocalNameElements(recordFields, xmlElementNode, recordField); } else { recordFields.add(recordField); } @@ -273,7 +273,7 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); if (prefixMap.size() > 1) { - addRecordField(recordFields, xmlElementNode, updatedRecordField); + generateRecordFieldForSameLocalNameElements(recordFields, xmlElementNode, updatedRecordField); } else { recordFields.add(indexOfRecordFieldNode, updatedRecordField); } @@ -339,7 +339,8 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole return recordFields; } - private static void addRecordField(List recordFields, Element xmlElementNode, RecordFieldNode recordField) { + private static void generateRecordFieldForSameLocalNameElements(List recordFields, Element xmlElementNode, + RecordFieldNode recordField) { recordFields.add(recordField.modify().withFieldName( AbstractNodeFactory.createIdentifierToken( xmlElementNode.getPrefix() + diff --git a/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java b/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java index d6c78f22e176..6f370d5547d4 100644 --- a/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java +++ b/misc/xml-to-record-converter/src/test/java/io/ballerina/xmltorecordconverter/XMLToRecordConverterTests.java @@ -199,6 +199,11 @@ public class XMLToRecordConverterTests { private final Path sample31Bal = RES_DIR.resolve(BAL_DIR) .resolve("sample_31.bal"); + private final Path sample32XML = RES_DIR.resolve(XML_DIR) + .resolve("sample_32.xml"); + private final Path sample32Bal = RES_DIR.resolve(BAL_DIR) + .resolve("sample_32.bal"); + private static final String XMLToRecordServiceEP = "xmlToRecord/convert"; @@ -427,42 +432,6 @@ public void testXMLWithoutNamespaces() throws IOException { Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); } - @Test(description = "testXMLWithMultipleNamespacesAndSameElement") - public void testXMLWithMultipleNamespacesAndSameElement() throws IOException { - String xmlFileContent = Files.readString(sample28XML); - String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, - null, true).getCodeBlock().replaceAll("\\s+", ""); - String expectedCodeBlock = Files.readString(sample28Bal).replaceAll("\\s+", ""); - Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); - } - - @Test(description = "testXMLWithMultipleNamespaces2") - public void testXMLWithMultipleNamespaces2() throws IOException { - String xmlFileContent = Files.readString(sample29XML); - String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, - null, true).getCodeBlock().replaceAll("\\s+", ""); - String expectedCodeBlock = Files.readString(sample29Bal).replaceAll("\\s+", ""); - Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); - } - - @Test(description = "testXMLWithMultipleNamespaces3") - public void testXMLWithMultipleNamespaces3() throws IOException { - String xmlFileContent = Files.readString(sample30XML); - String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, - null, true).getCodeBlock().replaceAll("\\s+", ""); - String expectedCodeBlock = Files.readString(sample30Bal).replaceAll("\\s+", ""); - Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); - } - - @Test(description = "testXMLWithoutNamespaceAnnotation") - public void testXMLWithoutNamespaceAnnotation() throws IOException { - String xmlFileContent = Files.readString(sample31XML); - String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, - null, false).getCodeBlock().replaceAll("\\s+", ""); - String expectedCodeBlock = Files.readString(sample31Bal).replaceAll("\\s+", ""); - Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); - } - @Test(description = "testXMLToRecordService") public void testXMLToRecordService() throws IOException, ExecutionException, InterruptedException { Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); @@ -507,4 +476,49 @@ public void testXMLWithoutNamespaceAnnotations() throws IOException { String expectedCodeBlock = Files.readString(sample27Bal).replaceAll("\\s+", ""); Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); } + + @Test(description = "testXMLWithMultipleNamespacesAndSameElement") + public void testXMLWithMultipleNamespacesAndSameElement() throws IOException { + String xmlFileContent = Files.readString(sample28XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample28Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithMultipleNamespaces2") + public void testXMLWithMultipleNamespaces2() throws IOException { + String xmlFileContent = Files.readString(sample29XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample29Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithMultipleNamespaces3") + public void testXMLWithMultipleNamespaces3() throws IOException { + String xmlFileContent = Files.readString(sample30XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, true).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample30Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithoutNamespaceAnnotation") + public void testXMLWithoutNamespaceAnnotation() throws IOException { + String xmlFileContent = Files.readString(sample31XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, false).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample31Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } + + @Test(description = "testXMLWithSameElementAndWithoutMultipleNamespaces") + public void testXMLWithSameElementAndWithoutMultipleNamespaces() throws IOException { + String xmlFileContent = Files.readString(sample32XML); + String generatedCodeBlock = XMLToRecordConverter.convert(xmlFileContent, false, false, false, + null, false).getCodeBlock().replaceAll("\\s+", ""); + String expectedCodeBlock = Files.readString(sample32Bal).replaceAll("\\s+", ""); + Assert.assertEquals(generatedCodeBlock, expectedCodeBlock); + } } diff --git a/misc/xml-to-record-converter/src/test/resources/ballerina/sample_32.bal b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_32.bal new file mode 100644 index 000000000000..f95547fda29a --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/ballerina/sample_32.bal @@ -0,0 +1,11 @@ +type Bk_Chapter record { + int[] number; +}; + +@xmldata:Name {value: "book"} +type Bk_Book record { + string[] title; + string subtitle; + string language; + Bk_Chapter[] chapter; +}; diff --git a/misc/xml-to-record-converter/src/test/resources/xml/sample_32.xml b/misc/xml-to-record-converter/src/test/resources/xml/sample_32.xml new file mode 100644 index 000000000000..02d05b97eb40 --- /dev/null +++ b/misc/xml-to-record-converter/src/test/resources/xml/sample_32.xml @@ -0,0 +1,17 @@ + + string + string + string + string + string + + 1 + 1 + 1 + + + 1 + 1 + 1 + + From 8bbaeaa944ff09b81a00155d56c10d679b6c011c Mon Sep 17 00:00:00 2001 From: mindula Date: Tue, 4 Jun 2024 11:25:40 +0530 Subject: [PATCH 9/9] Fix checkstyle failure --- .../ballerina/xmltorecordconverter/XMLToRecordConverter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java index 132943cd1b7d..c64ea0f2905c 100644 --- a/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java +++ b/misc/xml-to-record-converter/src/main/java/io/ballerina/xmltorecordconverter/XMLToRecordConverter.java @@ -273,7 +273,8 @@ private static List getRecordFieldsForXMLElement(Element xmlElement, boole (RecordFieldNode) recordFields.remove(indexOfRecordFieldNode); RecordFieldNode updatedRecordField = mergeRecordFields(existingRecordField, recordField); if (prefixMap.size() > 1) { - generateRecordFieldForSameLocalNameElements(recordFields, xmlElementNode, updatedRecordField); + generateRecordFieldForSameLocalNameElements(recordFields, xmlElementNode, + updatedRecordField); } else { recordFields.add(indexOfRecordFieldNode, updatedRecordField); }