From e0c18141dc6aa81075816f47a2824a0678e79941 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:02:11 +0530 Subject: [PATCH 01/23] Improve the functionalities of handling XSD elements --- .../java/io/ballerina/xsd/core/Utils.java | 17 ++-- .../io/ballerina/xsd/core/XSDFactory.java | 2 +- .../io/ballerina/xsd/core/XSDToRecord.java | 41 ++++---- .../xsd/core/visitor/VisitorUtils.java | 15 ++- .../xsd/core/visitor/XSDVisitorImpl.java | 94 +++++++++++++++---- 5 files changed, 115 insertions(+), 54 deletions(-) diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/Utils.java b/xsd-core/src/main/java/io/ballerina/xsd/core/Utils.java index 318504b..b27ae48 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/Utils.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/Utils.java @@ -38,7 +38,6 @@ import java.util.Map; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.CLOSE_BRACES; -import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.CONTENT_FIELD; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.RECORD_WITH_OPEN_BRACE; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.SEMICOLON; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.STRING; @@ -65,29 +64,29 @@ static ModulePartNode generateModulePartNode(Map nodes, - String element, String type) { + String element, String type, String contentField) { String fields = extractSubstring(nodes.get(type).toString(), RECORD_WITH_OPEN_BRACE, - VERTICAL_BAR + CLOSE_BRACES + SEMICOLON); + VERTICAL_BAR + CLOSE_BRACES + SEMICOLON, contentField); String extendedValue = nodes.get(element) - .toString().replace(type + WHITESPACE + CONTENT_FIELD + SEMICOLON, fields); + .toString().replace(type + WHITESPACE + contentField + SEMICOLON, fields); ModuleMemberDeclarationNode moduleNode = NodeParser.parseModuleMemberDeclaration(extendedValue); nodes.put(element, moduleNode); } static void processSingleTypeElements(Map nodes, - String element, String type, String[] tokens) { + String element, String type, String[] tokens, String contentField) { String token = (!nodes.containsKey(type)) || nodes.get(type).toString().contains(XSDVisitorImpl.ENUM) ? STRING : tokens[tokens.length - 2]; - String rootElement = nodes.get(element).toString().replace(type + WHITESPACE + CONTENT_FIELD, - token + WHITESPACE + CONTENT_FIELD); + String rootElement = nodes.get(element).toString().replace(type + WHITESPACE + contentField, + token + WHITESPACE + contentField); ModuleMemberDeclarationNode moduleNode = NodeParser.parseModuleMemberDeclaration(rootElement); nodes.put(element, moduleNode); } - static String extractSubstring(String baseString, String startToken, String endToken) { + static String extractSubstring(String baseString, String startToken, String endToken, String contentField) { if (!baseString.contains(startToken)) { return baseString.split(WHITESPACE)[baseString.split(WHITESPACE).length - 2] + - WHITESPACE + CONTENT_FIELD + SEMICOLON; + WHITESPACE + contentField + SEMICOLON; } int startIndex = baseString.indexOf(startToken) + startToken.length(); int endIndex = baseString.indexOf(endToken, startIndex); diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/XSDFactory.java b/xsd-core/src/main/java/io/ballerina/xsd/core/XSDFactory.java index 569c6d6..3b8d418 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/XSDFactory.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/XSDFactory.java @@ -20,8 +20,8 @@ import io.ballerina.xsd.core.component.ComplexType; import io.ballerina.xsd.core.component.Element; -import io.ballerina.xsd.core.component.XSDComponent; import io.ballerina.xsd.core.component.SimpleType; +import io.ballerina.xsd.core.component.XSDComponent; import org.w3c.dom.Node; import java.util.Optional; diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/XSDToRecord.java b/xsd-core/src/main/java/io/ballerina/xsd/core/XSDToRecord.java index 18c0703..fc5411d 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/XSDToRecord.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/XSDToRecord.java @@ -21,11 +21,11 @@ import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.NodeParser; +import io.ballerina.xsd.core.component.XSDComponent; import io.ballerina.xsd.core.diagnostic.XsdDiagnostic; +import io.ballerina.xsd.core.visitor.VisitorUtils; import io.ballerina.xsd.core.visitor.XSDVisitor; import io.ballerina.xsd.core.visitor.XSDVisitorImpl; -import io.ballerina.xsd.core.component.XSDComponent; -import io.ballerina.xsd.core.visitor.VisitorUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -43,7 +43,6 @@ import static io.ballerina.xsd.core.visitor.VisitorUtils.OPEN_BRACES; import static io.ballerina.xsd.core.visitor.VisitorUtils.QUOTATION_MARK; import static io.ballerina.xsd.core.visitor.VisitorUtils.WHITESPACE; -import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.CONTENT_FIELD; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.ENUM; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.NAME; import static io.ballerina.xsd.core.visitor.XSDVisitorImpl.RECORD_WITH_OPEN_BRACE; @@ -65,6 +64,8 @@ public final class XSDToRecord { public static final String EQUAL = "="; public static final String TARGET_NAMESPACE = "targetNamespace"; + private static final String CONTENT_FIELD = "\\#content"; + public static Response convert(Document document) throws Exception { Element rootElement = document.getDocumentElement(); if (!Objects.equals(rootElement.getLocalName(), SCHEMA)) { @@ -113,21 +114,23 @@ private static void generateNodes(Element rootElement, Map nodes, Map rootElements) { - for (String element: rootElements.keySet()) { - String type = rootElements.get(element); + for (Map.Entry entry : rootElements.entrySet()) { + String element = entry.getKey(); + String type = entry.getValue(); String[] tokens = nodes.get(type).toString().split(WHITESPACE); if (!nodes.get(type).toString().contains(RECORD_WITH_OPEN_BRACE)) { - Utils.processSingleTypeElements(nodes, element, type, tokens); + Utils.processSingleTypeElements(nodes, element, type, tokens, CONTENT_FIELD); } else { - Utils.processRecordTypeElements(nodes, element, type); + Utils.processRecordTypeElements(nodes, element, type, CONTENT_FIELD); } } } private static void processNestedElements(Map nodes, Map nestedElements) { - for (String element: nestedElements.keySet()) { - String nestedElement = nestedElements.get(element); + for (Map.Entry entry : nestedElements.entrySet()) { + String element = entry.getKey(); + String nestedElement = entry.getValue(); ModuleMemberDeclarationNode moduleNode = NodeParser.parseModuleMemberDeclaration(nestedElement); nodes.put(element, moduleNode); } @@ -135,9 +138,11 @@ private static void processNestedElements(Map nodes, Map nameResolvers) { - for (String element: nameResolvers.keySet()) { + for (Map.Entry entry : nameResolvers.entrySet()) { + String element = entry.getKey(); + String annotation = entry.getValue(); String node = nodes.get(element).toString(); - String newNode = String.format(XMLDATA_NAME_ANNOTATION, nameResolvers.get(element)) + node; + String newNode = String.format(XMLDATA_NAME_ANNOTATION, annotation) + node; ModuleMemberDeclarationNode moduleNode = NodeParser.parseModuleMemberDeclaration(newNode); nodes.put(element, moduleNode); } @@ -145,11 +150,12 @@ private static void processNameResolvers(Map nodes, XSDVisitor xsdVisitor) { Map extensions = xsdVisitor.getExtensions(); - for (String key: extensions.keySet()) { + for (Map.Entry entry : extensions.entrySet()) { + String key = entry.getKey(); + String baseValue = entry.getValue(); if (!nodes.containsKey(key)) { continue; } - String baseValue = extensions.get(key); if (VisitorUtils.isSimpleType(baseValue)) { String fields = RECORD_WITH_OPEN_BRACE + baseValue + WHITESPACE + CONTENT_FIELD + SEMICOLON; ModuleMemberDeclarationNode parentNode = nodes.get(key); @@ -160,7 +166,7 @@ private static void processExtensions(Map n ModuleMemberDeclarationNode baseNode = nodes.get(baseValue); ModuleMemberDeclarationNode parentNode = nodes.get(key); String fields = Utils.extractSubstring(baseNode.toString(), RECORD_WITH_OPEN_BRACE, - VERTICAL_BAR + CLOSE_BRACES + SEMICOLON); + VERTICAL_BAR + CLOSE_BRACES + SEMICOLON, CONTENT_FIELD); fields = RECORD_WITH_OPEN_BRACE + fields; String extendedValue = parentNode.toString().replace(RECORD_WITH_OPEN_BRACE, fields); ModuleMemberDeclarationNode moduleNode = NodeParser.parseModuleMemberDeclaration(extendedValue); @@ -171,10 +177,11 @@ private static void processExtensions(Map n public static void processEnumerations(Map nodes, Map> enumerations) { - for (String key: enumerations.keySet()) { - ArrayList enums = enumerations.get(key); + for (Map.Entry> entry : enumerations.entrySet()) { + String key = entry.getKey(); + ArrayList enums = entry.getValue(); StringBuilder enumBuilder = new StringBuilder(); - for (String enumValue: enums) { + for (String enumValue : enums) { if (nodes.containsKey(enumValue)) { enumValue = enumValue.toLowerCase(Locale.ROOT) + WHITESPACE + EQUAL + QUOTATION_MARK + enumValue + QUOTATION_MARK; diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/VisitorUtils.java b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/VisitorUtils.java index a620259..9ee54f7 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/VisitorUtils.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/VisitorUtils.java @@ -117,7 +117,7 @@ public static String generateDefaultValue(String type, String value) { StringBuilder builder = new StringBuilder().append(WHITESPACE).append(EQUAL).append(WHITESPACE); switch (type) { case INT -> builder.append(value); - case STRING -> builder.append(QUOTATION_MARK).append(value).append(QUOTATION_MARK); + default -> builder.append(QUOTATION_MARK).append(value).append(QUOTATION_MARK); } return builder.toString(); } @@ -147,15 +147,12 @@ public static void handleMaxOccurrences(Node node, StringBuilder builder) { } } - public static void handleFixedValues(StringBuilder builder, Node typeNode, Node fixedNode) { - builder.append(fixedNode != null - ? generateFixedValue(deriveType(typeNode), fixedNode.getNodeValue()) - : deriveType(typeNode)).append(WHITESPACE); - } - public static String deriveType(Node node) { - String derivedType = node.getNodeValue().contains(COLON) ? - node.getNodeValue().substring(node.getNodeValue().indexOf(COLON) + 1) : node.getNodeValue(); + String derivedType = (node != null) + ? node.getNodeValue().contains(COLON) + ? node.getNodeValue().substring(node.getNodeValue().indexOf(COLON) + 1) + : node.getNodeValue() + : STRING; return typeGenerator(derivedType); } diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java index 655e354..e73504e 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java @@ -23,8 +23,8 @@ import io.ballerina.xsd.core.XSDFactory; import io.ballerina.xsd.core.component.ComplexType; import io.ballerina.xsd.core.component.Element; -import io.ballerina.xsd.core.component.XSDComponent; import io.ballerina.xsd.core.component.SimpleType; +import io.ballerina.xsd.core.component.XSDComponent; import io.ballerina.xsd.core.diagnostic.DiagnosticMessage; import io.ballerina.xsd.core.diagnostic.XsdDiagnostic; import org.w3c.dom.Node; @@ -59,7 +59,6 @@ public class XSDVisitorImpl implements XSDVisitor { public static final String PUBLIC = "public"; public static final String WHITESPACE = " "; - public static final String CONTENT_FIELD = "\\#content"; public static final String SEMICOLON = ";"; public static final String RECORD = "record"; public static final String TYPE = "type"; @@ -77,6 +76,7 @@ public class XSDVisitorImpl implements XSDVisitor { public static final String SEQUENCE = "sequence"; public static final String CHOICE = "choice"; public static final String ATTRIBUTE = "attribute"; + public static final String ALL = "all"; public static final String COMPLEX_CONTENT = "complexContent"; public static final String SIMPLE_CONTENT = "simpleContent"; public static final String EXTENSION = "extension"; @@ -106,6 +106,10 @@ public class XSDVisitorImpl implements XSDVisitor { public static final String XMLDATA_SEQUENCE = "@xmldata:Sequence"; public static final String SEQUENCE_NAME = "SequenceGroup"; public static final String XMLDATA_ORDER = "@xmldata:SequenceOrder"; + public static final String RESTRICTION = "restriction"; + public static final String CONTENT_FIELD = "\\#content"; + public static final String LIST = "list"; + public static final String ITEM_TYPE = "itemType"; private final ArrayList imports = new ArrayList<>(); private final Map extensions = new LinkedHashMap<>(); @@ -113,9 +117,11 @@ public class XSDVisitorImpl implements XSDVisitor { private final Map nameResolvers = new LinkedHashMap<>(); private final Map nestedElements = new LinkedHashMap<>(); private final Map> enumerationElements = new LinkedHashMap<>(); - private List diagnostics = new ArrayList<>(); + private final List diagnostics = new ArrayList<>(); public String targetNamespace; + public XSDVisitorImpl() {} + @Override public String visit(Element element) throws Exception { if (element.isSubType()) { @@ -142,18 +148,14 @@ public String visit(Element element, boolean subElement) { } catch (Exception e) { diagnostics.add(DiagnosticMessage.xsdToBallerinaError101(e, null)); } - if (nameNode == null && typeNode == null) { + if (nameNode == null || typeNode == null) { builder.append(STRING).append(WHITESPACE).append(CONTENT_FIELD); } else { handleFixedValues(node, builder, typeNode); handleMaxOccurrences(node, builder); - if (nameNode == null) { - diagnostics.add(DiagnosticMessage.xsdToBallerinaError102(NAME, null)); - } else { - builder.append(nameNode.getNodeValue()); - handleMinOccurrences(element, builder); - handleDefaultValues(node, builder, typeNode); - } + builder.append(nameNode.getNodeValue()); + handleMinOccurrences(element, builder); + handleDefaultValues(node, builder, typeNode); } builder.append(SEMICOLON); return builder.toString(); @@ -188,7 +190,8 @@ public String visit(ComplexType element, boolean isSubType) throws Exception { case SEQUENCE -> builder.append(visitSequence(childNode, false)); case CHOICE -> builder.append(visitChoice(childNode)); case ATTRIBUTE -> builder.append(visitAttribute(childNode)); - case COMPLEX_CONTENT, SIMPLE_CONTENT -> builder.append(visitComplexContent(childNode)); + case ALL -> builder.append(visitAllContent(childNode, false)); + default -> builder.append(visitComplexContent(childNode)); } } builder.append(VERTICAL_BAR).append(CLOSE_BRACES).append(SEMICOLON); @@ -260,7 +263,13 @@ public String visitAttribute(Node attribute) { Node typeNode = attribute.getAttributes().getNamedItem(TYPE); builder.append(ATTRIBUTE_ANNOTATION).append(WHITESPACE); Node fixedNode = attribute.getAttributes().getNamedItem(FIXED); - handleFixedValues(builder, typeNode, fixedNode); + if (fixedNode != null) { + builder.append(generateFixedValue(deriveType(typeNode), fixedNode.getNodeValue())).append(WHITESPACE); + } else if (attribute.hasChildNodes()) { + builder.append(visitAttributeChildNodes(attribute.getChildNodes(), builder)).append(WHITESPACE); + } else { + builder.append(deriveType(typeNode)).append(WHITESPACE); + } builder.append(nameNode.getNodeValue()); Node attributeType = attribute.getAttributes().getNamedItem(USE); if (attributeType != null && !attributeType.getNodeValue().equals(REQUIRED)) { @@ -274,6 +283,29 @@ public String visitAttribute(Node attribute) { return builder.toString(); } + public String visitAttributeChildNodes(NodeList childNodes, StringBuilder builder) { + for (int i = 0; i < childNodes.getLength(); i++) { + if (childNodes.item(i).getNodeType() != Node.ELEMENT_NODE) { + continue; + } + Node childNode = childNodes.item(i); + if (childNode.getLocalName().equals(SIMPLE_TYPE)) { + for (Node simpleTypeNode : asIterable(childNode.getChildNodes())) { + if (simpleTypeNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + if (simpleTypeNode.getLocalName().equals(RESTRICTION)) { + return deriveType(simpleTypeNode.getAttributes().getNamedItem(BASE)); + } else if (simpleTypeNode.getLocalName().equals(LIST)) { + return deriveType(simpleTypeNode.getAttributes().getNamedItem(ITEM_TYPE)); + } + return STRING; + } + } + } + return STRING; + } + public String visitComplexContent(Node node) throws Exception { StringBuilder builder = new StringBuilder(); NodeList childNodes = node.getChildNodes(); @@ -281,8 +313,11 @@ public String visitComplexContent(Node node) throws Exception { if (childNode.getNodeType() == Node.ELEMENT_NODE && EXTENSION.equals(childNode.getLocalName())) { String base = deriveType(childNode.getAttributes().getNamedItem(BASE)); builder.append(visitExtension(childNode)); - String parentNodeName = deriveType(node.getParentNode().getAttributes().getNamedItem(NAME)); - extensions.put(parentNodeName, base); + Node nameNode = node.getParentNode().getAttributes().getNamedItem(NAME); + if (nameNode != null) { + String parentNodeName = deriveType(node.getParentNode().getAttributes().getNamedItem(NAME)); + extensions.put(parentNodeName, base); + } } } return builder.toString(); @@ -299,7 +334,8 @@ public String visitExtension(Node node) throws Exception { case SEQUENCE -> builder.append(visitSequence(childNode, false)); case CHOICE -> builder.append(visitChoice(childNode)); case ATTRIBUTE -> builder.append(visitAttribute(childNode)); - case COMPLEX_CONTENT, SIMPLE_CONTENT -> builder.append(visitComplexContent(childNode)); + case ALL -> builder.append(visitAllContent(childNode, false)); + default -> builder.append(visitComplexContent(childNode)); } } } @@ -343,6 +379,13 @@ public String visitSequence(Node node, boolean isOptional) throws Exception { return builder.toString(); } + public String visitAllContent(Node node, boolean isOptional) throws Exception { + NodeList childNodes = node.getChildNodes(); + StringBuilder childNodeBuilder = new StringBuilder(); + processAllChildNodes(isOptional, childNodes, childNodeBuilder); + return childNodeBuilder.toString(); + } + private String handleElementsWithChildNodes(Node node, StringBuilder builder) throws Exception { Node nameNode = node.getAttributes().getNamedItem(NAME); Node typeNode = node.getAttributes().getNamedItem(TYPE); @@ -379,7 +422,7 @@ public static String generateFixedValue(String type, String value) { StringBuilder builder = new StringBuilder(); switch (type) { case INT -> builder.append(value).append(WHITESPACE); - case STRING -> builder.append(QUOTATION_MARK).append(value).append(QUOTATION_MARK).append(WHITESPACE); + default -> builder.append(QUOTATION_MARK).append(value).append(QUOTATION_MARK).append(WHITESPACE); } return builder.toString(); } @@ -418,7 +461,8 @@ private void processChildNodeByType(Node childNode, StringBuilder builder) throw case SEQUENCE -> builder.append(visitSequence(childNode, false)); case CHOICE -> builder.append(visitChoice(childNode)); case ATTRIBUTE -> builder.append(visitAttribute(childNode)); - case COMPLEX_CONTENT, SIMPLE_CONTENT -> builder.append(visitComplexContent(childNode)); + case ALL -> builder.append(visitAllContent(childNode, false)); + default -> builder.append(visitComplexContent(childNode)); } } @@ -487,6 +531,20 @@ private void processChildNodes(boolean isOptional, NodeList childNodes, } } + private void processAllChildNodes(boolean isOptional, NodeList childNodes, + StringBuilder stringBuilder) throws Exception { + for (Node childNode : asIterable(childNodes)) { + Optional component = XSDFactory.generateComponents(childNode); + if (component.isEmpty()) { + continue; + } + component.get().setSubType(true); + component.get().setOptional(isOptional); + stringBuilder.append(addNamespace(this, getTargetNamespace())); + stringBuilder.append(component.get().accept(this)); + } + } + private void processChildNode(boolean isOptional, Node childNode, StringBuilder stringBuilder) throws Exception { Optional component = XSDFactory.generateComponents(childNode); From e3ed03e0cda89cf6ac28705b74567c63b3f1b927 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:02:47 +0530 Subject: [PATCH 02/23] Update Gradle build files in the project --- build-config/checkstyle/build.gradle | 30 ++++++++++++++++- build.gradle | 2 +- xsd-core/build.gradle | 50 ++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/build-config/checkstyle/build.gradle b/build-config/checkstyle/build.gradle index da05a18..5eac081 100644 --- a/build-config/checkstyle/build.gradle +++ b/build-config/checkstyle/build.gradle @@ -14,6 +14,8 @@ * limitations under the License. * */ +import groovy.xml.XmlParser +import groovy.xml.XmlUtil plugins { id "de.undercouch.download" @@ -21,6 +23,7 @@ plugins { apply plugin: 'java' +// Task to download the Checkstyle rule files task downloadCheckstyleRuleFiles(type: Download) { src([ 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/checkstyle.xml', @@ -31,16 +34,41 @@ task downloadCheckstyleRuleFiles(type: Download) { dest buildDir } +// Task to remove +task modifyCheckstyleConfig { + dependsOn downloadCheckstyleRuleFiles +// doLast { +// File checkstyleFile = file("$project.buildDir/checkstyle.xml") +// if (checkstyleFile.exists()) { +// // Parse the checkstyle.xml file +// def xml = new XmlParser().parse(checkstyleFile) +// +// // Find and remove +// xml.module.findAll { it.'@name' == 'StaticVariableName' }.each { staticVariableModule -> +// xml.remove(staticVariableModule) +// } +// +// // Write the updated XML back to the file +// checkstyleFile.text = XmlUtil.serialize(xml) +// } else { +// throw new GradleException("Checkstyle file not found at $checkstyleFile") +// } +// } +} + +// Disable jar task jar { enabled = false } +// Disable clean task clean { enabled = false } +// Add modified checkstyle.xml and suppressions.xml as artifacts artifacts.add('default', file("$project.buildDir/checkstyle.xml")) { - builtBy('downloadCheckstyleRuleFiles') + builtBy('modifyCheckstyleConfig') } artifacts.add('default', file("$project.buildDir/suppressions.xml")) { diff --git a/build.gradle b/build.gradle index 62b9cde..5c9c360 100644 --- a/build.gradle +++ b/build.gradle @@ -87,7 +87,7 @@ tasks.register('clean') { } tasks.register('build') { - dependsOn(":module-ballerina-xsd:build") + dependsOn(":module-ballerina-xsd:build") } tasks.register('test') { diff --git a/xsd-core/build.gradle b/xsd-core/build.gradle index 920adaa..c1c9ffd 100644 --- a/xsd-core/build.gradle +++ b/xsd-core/build.gradle @@ -1,5 +1,7 @@ plugins { id 'java' + id 'checkstyle' + id 'com.github.spotbugs' } group = 'io.ballerina' @@ -10,6 +12,9 @@ repositories { } dependencies { + checkstyle project(':checkstyle') + checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'formatter-core', version: "${ballerinaLangVersion}" @@ -25,3 +30,48 @@ dependencies { test { useJUnitPlatform() } + + +checkstyle { + toolVersion "${project.checkstylePluginVersion}" + configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") + configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] +} + +def excludePattern = '**/module-info.java' +tasks.withType(Checkstyle) { + exclude excludePattern +} + +checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") +checkstyleTest.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") + +compileJava { + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + ] + classpath = files() + } +} + +spotbugsMain { + def classLoader = plugins["com.github.spotbugs"].class.classLoader + def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") + def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") + effort = SpotBugsEffort.MAX + reportLevel = SpotBugsConfidence.LOW + reportsDir = file("$project.buildDir/reports/spotbugs") + reports { + html.enabled true + text.enabled = true + } + def excludeFile = file("${rootDir}/build-config/spotbugs-exclude.xml") + if(excludeFile.exists()) { + excludeFilter = excludeFile + } +} + +spotbugsTest { + enabled = false +} From d498344453aa8c0f28a95ddc801c2d00b4ef520c Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:03:43 +0530 Subject: [PATCH 03/23] Improve XSD cli commands --- .../java/io/ballerina/xsd/cmd/XsdCmd.java | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java b/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java index a0becdf..90421f5 100644 --- a/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java +++ b/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java @@ -40,6 +40,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Scanner; import javax.xml.parsers.DocumentBuilder; @@ -60,6 +61,10 @@ public class XsdCmd implements BLauncherCmd { private static final String FILE_OVERWRITE_PROMPT = "File already exists at %s. Overwrite? (y/N): "; public static final String INVALID_BALLERINA_DIRECTORY_ERROR = "Invalid Ballerina package directory: %s, cannot find 'Ballerina.toml' file"; + public static final String INVALID_DIRECTORY_PATH = "Error: Invalid directory path has been provided. " + + "Output path '%s' is a file"; + public static final String TYPES_FILE_NAME = "types.bal"; + public static final String SLASH = "/"; private final PrintStream outStream; private final boolean exitWhenFinish; @CommandLine.Option(names = {"-h", "--help"}, hidden = true) @@ -68,9 +73,9 @@ public class XsdCmd implements BLauncherCmd { @CommandLine.Parameters(description = "Input file path of the XSD schema") private final List argList = new ArrayList<>(); - @CommandLine.Option(names = {"-o", "--output"}, description = "Destination file path of the generated types from " + - "the XSD file") - private String outputPath = "types.bal"; + @CommandLine.Option(names = {"-m", "--module"}, description = "The name of the module in which the Ballerina " + + "client and record types are generated.") + private String outputPath = ""; public XsdCmd() { this.outStream = System.err; @@ -92,6 +97,21 @@ public void execute() { exitOnError(); return; } + Path outputDirPath = Paths.get(outputPath); + if (!Objects.equals(outputPath, EMPTY_STRING)) { + Path basePath = Paths.get("modules").toAbsolutePath(); + outputDirPath = basePath.resolve(outputPath).normalize(); + if (!outputDirPath.startsWith(basePath)) { + System.out.printf("Invalid output path: Path traversal detected in '%s'%n", outputPath); + exitOnError(); + return; + } + } + if (Files.exists(outputDirPath) && !Files.isDirectory(outputDirPath)) { + outStream.printf((INVALID_DIRECTORY_PATH) + "%n", outputPath); + exitOnError(); + return; + } if (argList.isEmpty()) { outStream.println("An XSD file path is required to generate the types"); outStream.println("e.g: bal xsd "); @@ -111,8 +131,13 @@ public void execute() { exitOnError(); return; } - Path destinationFile = Files.exists(Paths.get(outputPath)) - ? handleFileOverwrite(Paths.get(outputPath), outStream) : Paths.get(outputPath); + Path path; + if (outputPath.equals(EMPTY_STRING)) { + path = Path.of(TYPES_FILE_NAME); + } else { + path = outputDirPath.resolve(SLASH).resolve(TYPES_FILE_NAME); + } + Path destinationFile = Files.exists(path) ? handleFileOverwrite(path, outStream) : path; Path parentDirectory = destinationFile.getParent(); if (parentDirectory != null && !Files.exists(parentDirectory)) { Files.createDirectories(parentDirectory); From bd4fd2d8b6f4f588b69a9045795e09a54c4a51c0 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:04:50 +0530 Subject: [PATCH 04/23] Add necessary files to the package --- build-config/spotbugs-exclude.xml | 92 + .../xml/22_attributes_with_fixed_values.xml | 2 +- .../src/test/resources/xml/phone_verify.xsd | 75 + .../src/test/resources/xml/reservation.xsd | 3366 +++++++++++++++++ 4 files changed, 3534 insertions(+), 1 deletion(-) create mode 100644 build-config/spotbugs-exclude.xml create mode 100644 xsd-core/src/test/resources/xml/phone_verify.xsd create mode 100644 xsd-core/src/test/resources/xml/reservation.xsd diff --git a/build-config/spotbugs-exclude.xml b/build-config/spotbugs-exclude.xml new file mode 100644 index 0000000..4a21761 --- /dev/null +++ b/build-config/spotbugs-exclude.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xsd-core/src/test/resources/xml/22_attributes_with_fixed_values.xml b/xsd-core/src/test/resources/xml/22_attributes_with_fixed_values.xml index f249c27..dd5dda5 100644 --- a/xsd-core/src/test/resources/xml/22_attributes_with_fixed_values.xml +++ b/xsd-core/src/test/resources/xml/22_attributes_with_fixed_values.xml @@ -1,7 +1,7 @@ - + diff --git a/xsd-core/src/test/resources/xml/phone_verify.xsd b/xsd-core/src/test/resources/xml/phone_verify.xsd new file mode 100644 index 0000000..5899570 --- /dev/null +++ b/xsd-core/src/test/resources/xml/phone_verify.xsd @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xsd-core/src/test/resources/xml/reservation.xsd b/xsd-core/src/test/resources/xml/reservation.xsd new file mode 100644 index 0000000..a0130ca --- /dev/null +++ b/xsd-core/src/test/resources/xml/reservation.xsd @@ -0,0 +1,3366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 01a0e60c0d378eb956645d989b71057fa1c15b2e Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:05:46 +0530 Subject: [PATCH 05/23] Update docs in the package --- README.md | 106 +++++++++++++++++++++++++++++++- module-ballerina-xsd/Module.md | 39 +++++++++++- module-ballerina-xsd/Package.md | 37 +++++++++++ 3 files changed, 179 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9516c3..3e8a719 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,104 @@ -# xsd-tools -This is the source code of the XSD tool which converts XSD files into Ballerina records +# Ballerina XSD Tool + +[![Build](https://github.com/ballerina-platform/xsd-tools/actions/workflows/build-timestamped-master.yml/badge.svg)](https://github.com/ballerina-platform/xsd-tools/actions/workflows/build-timestamped-master.yml) +[![codecov](https://codecov.io/gh/ballerina-platform/xsd-tools/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/xsd-tools) +[![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/xsd-tools.svg)](https://github.com/ballerina-platform/xsd-tools/commits/master) +[![GitHub issues](https://img.shields.io/github/issues/ballerina-platform/ballerina-standard-library/module/xsd-tools.svg?label=Open%20Issues)](https://github.com/ballerina-platform/ballerina-library/labels/module%2Fxsd-tools) + +`XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. + +The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. + +### Command Syntax + +The `xsd` command in Ballerina is used for XSD to Ballerina code generation. + +```bash +bal xsd [--module ] +``` + +#### Command Options + +| Option | Description | +|--------|-------------| +| `` | (Required) The path to the XSD file | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | + +### Examples + +#### 1. Generate types for the given XSD file + +This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. + +```bash +bal xsd +``` + +#### 2. Generate types in a specific module + +To generate the Ballerina record types in a specific module, use the `--module` option. + +```bash +bal xsd --module custom +``` + +## Building from the Source + +### Setting Up the Prerequisites + +1. OpenJDK 21 ([Adopt OpenJDK](https://adoptopenjdk.net/) or any other OpenJDK distribution) + + >**Info:** You can also use [Oracle JDK](https://www.oracle.com/java/technologies/javase-downloads.html). Set the JAVA_HOME environment variable to the pathname of the directory into which you installed JDK. + +2. Export GitHub Personal access token with read package permissions as follows, + ``` + export packageUser= + export packagePAT= + ``` + +### Building the Source + +Execute the commands below to build from the source. + +1. To build the library: + + ./gradlew clean build + +2. To run the integration tests: + + ./gradlew clean test + +3. To build the module without the tests: + + ./gradlew clean build -x test + +4. To publish to maven local: + + ./gradlew clean build publishToMavenLocal + +5. Publish the generated artifacts to the local Ballerina central repository: + + ./gradlew clean build -PpublishToLocalCentral=true + +6. Publish the generated artifacts to the Ballerina central repository: + + ./gradlew clean build -PpublishToCentral=true + +## Contributing to Ballerina + +As an open-source project, Ballerina welcomes contributions from the community. + +You can also check for [open issues](https://github.com/ballerina-platform/xsd-tools/issues) that +interest you. We look forward to receiving your contributions. + +For more information, go to the [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md). + +## Code of Conduct + +All contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct). + +## Useful Links + +* Chat live with us via our [Discord server](https://discord.gg/ballerinalang). +* Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag. +* View the [Ballerina performance test results](https://github.com/ballerina-platform/ballerina-lang/blob/master/performance/benchmarks/summary.md). diff --git a/module-ballerina-xsd/Module.md b/module-ballerina-xsd/Module.md index 5c46f48..d9d69a2 100644 --- a/module-ballerina-xsd/Module.md +++ b/module-ballerina-xsd/Module.md @@ -1 +1,38 @@ -## Module Overview +## Overview + +`XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. + +The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. + +### Command Syntax + +The `xsd` command in Ballerina is used for XSD to Ballerina code generation. + +```bash +bal xsd [--module ] +``` + +#### Command Options + +| Option | Description | +|--------|-------------| +| `` | (Required) The path to the XSD file | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | + +### Examples + +#### 1. Generate types for the given XSD file + +This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. + +```bash +bal xsd +``` + +#### 2. Generate types in a specific module + +To generate the Ballerina record types in a specific module, use the `--module` option. + +```bash +bal xsd --module custom +``` diff --git a/module-ballerina-xsd/Package.md b/module-ballerina-xsd/Package.md index de78db2..cdc4d5e 100644 --- a/module-ballerina-xsd/Package.md +++ b/module-ballerina-xsd/Package.md @@ -1 +1,38 @@ ## Package Overview + +`XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. + +The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. + +#### Command Syntax + +The `xsd` command in Ballerina is used for XSD to Ballerina code generation. + +```bash +bal xsd [--module ] +``` + +### Command Options + +| Option | Description | +|--------|-------------| +| `` | (Required) The path to the XSD file | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | + +### Examples + +#### 1. Generate types for the given XSD file + +This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. + +```bash +bal xsd +``` + +#### 2. Generate types in a specific module + +To generate the Ballerina record types in a specific module, use the `--module` option. + +```bash +bal xsd --module custom +``` From 370f7e655c2594f1b348ebbb58ccaf6a2664448a Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:06:16 +0530 Subject: [PATCH 06/23] Add module flag description in the XSD help text --- xsd-cli/src/main/resources/cli-docs/xsd-help.help | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/xsd-cli/src/main/resources/cli-docs/xsd-help.help b/xsd-cli/src/main/resources/cli-docs/xsd-help.help index e542419..7835e22 100644 --- a/xsd-cli/src/main/resources/cli-docs/xsd-help.help +++ b/xsd-cli/src/main/resources/cli-docs/xsd-help.help @@ -1,8 +1,8 @@ NAME - bal xsd - Generate Ballerina types from a XSD file. + bal xsd - Generate Ballerina types from an XSD file. SYNOPSIS - bal xsd [-o | --output] + bal xsd [-m | --module] DESCRIPTION The 'bal xsd' command is used to generate records for a given XSD file. @@ -11,15 +11,14 @@ OPTIONS Path to the XSD file. This is a mandatory input. - -o, --output - Location of the generated Ballerina source code. If this path is not - specified, the output will be written to the same directory from - which the command is run. + -m, --module + The name of the module in which the Ballerina client and record types are generated. EXAMPLE Generate types from an XSD schema. The output will be saved to the types.bal file in the same directory where the command is executed. $ bal xsd schema.xsd - Generate types from an XSD schema with a specified relative path of the output file. - $ bal xsd schema.xsd -o modules/xsd/records.bal + Generate types from an XSD schema with a specified relative path of the output file. The output will be saved + to the 'temp' module in the Ballerina project. + $ bal xsd schema.xsd --module custom From 92ed36faada598e8193f1954ba1aeefc575fbd71 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:32:52 +0530 Subject: [PATCH 07/23] Update spotbugs version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8cef826..54215b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ version=0.1.0-SNAPSHOT # Dependencies ballerinaLangVersion=2201.10.0 checkstylePluginVersion=10.12.0 -spotbugsPluginVersion=5.0.14 +spotbugsPluginVersion=6.0.18 shadowJarPluginVersion=8.1.1 downloadPluginVersion=5.4.0 releasePluginVersion=2.8.0 From 4f01e770a45edf6ea9f794f897dce00e0097adf9 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:33:07 +0530 Subject: [PATCH 08/23] Encapsulate namespace field --- .../main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java index e73504e..212e1a3 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/visitor/XSDVisitorImpl.java @@ -118,7 +118,7 @@ public class XSDVisitorImpl implements XSDVisitor { private final Map nestedElements = new LinkedHashMap<>(); private final Map> enumerationElements = new LinkedHashMap<>(); private final List diagnostics = new ArrayList<>(); - public String targetNamespace; + private String targetNamespace; public XSDVisitorImpl() {} From 8512090e3e84b25d2eb055f6691aae571970cb7f Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:37:10 +0530 Subject: [PATCH 09/23] Add doc comments to the Response class --- .../src/main/java/io/ballerina/xsd/core/Response.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java index b71da91..65a9fee 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java @@ -22,6 +22,11 @@ import java.util.List; -public record Response(String types, List diagnostics) { - -} +/** + * Represents a response that contains the generated types and associated diagnostics. + * + * @param types The generated types as a string representation. + * @param diagnostics A list of {@link XsdDiagnostic} objects containing any diagnostic + * messages related to the XSD processing. + */ +public record Response(String types, List diagnostics) {} From ace48d4ac285a0365a7ab5b0c0cd03873c8fb5fa Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 14:42:04 +0530 Subject: [PATCH 10/23] Fix whitspace formatting issue --- xsd-core/src/main/java/io/ballerina/xsd/core/Response.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java index 65a9fee..61bde08 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java @@ -29,4 +29,4 @@ * @param diagnostics A list of {@link XsdDiagnostic} objects containing any diagnostic * messages related to the XSD processing. */ -public record Response(String types, List diagnostics) {} +public record Response(String types, List diagnostics) { } From 96ab08b843e74751d721a77de58b2fa4f74186da Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Sat, 1 Feb 2025 15:04:06 +0530 Subject: [PATCH 11/23] Update comments in native files --- .../java/io/ballerina/xsd/core/Response.java | 2 ++ .../xsd/core/diagnostic/DiagnosticMessage.java | 5 +++++ .../xsd/core/diagnostic/XsdDiagnostic.java | 5 +++++ xsd-core/src/main/java/module-info.java | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java index 61bde08..b98805d 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/Response.java @@ -28,5 +28,7 @@ * @param types The generated types as a string representation. * @param diagnostics A list of {@link XsdDiagnostic} objects containing any diagnostic * messages related to the XSD processing. + * + * @since 0.1.0 */ public record Response(String types, List diagnostics) { } diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/DiagnosticMessage.java b/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/DiagnosticMessage.java index 8a51032..23bb7ea 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/DiagnosticMessage.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/DiagnosticMessage.java @@ -22,6 +22,11 @@ import java.util.Objects; +/** + * Represents an error diagnostic message with a unique code, description, severity, and arguments. + * + * @since 1.0.0 + */ public class DiagnosticMessage { private final String code; private final String description; diff --git a/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/XsdDiagnostic.java b/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/XsdDiagnostic.java index 57d58e4..439c2f9 100644 --- a/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/XsdDiagnostic.java +++ b/xsd-core/src/main/java/io/ballerina/xsd/core/diagnostic/XsdDiagnostic.java @@ -28,6 +28,11 @@ import java.util.Collections; import java.util.List; +/** + * Represents an error diagnostic message specific to XSD processing. + * + * @since 1.0.0 + */ public class XsdDiagnostic extends Diagnostic { private final DiagnosticInfo diagnosticInfo; private final Location location; diff --git a/xsd-core/src/main/java/module-info.java b/xsd-core/src/main/java/module-info.java index 8522964..8f71eb6 100644 --- a/xsd-core/src/main/java/module-info.java +++ b/xsd-core/src/main/java/module-info.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://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. + */ + module io.ballerina.xsd.core { requires java.xml; requires io.ballerina.parser; From c91c65b0a67f029298c822a6e63e6248c4d30a20 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Tue, 4 Feb 2025 02:26:41 +0530 Subject: [PATCH 12/23] Improve XSD cli commands --- xsd-cli/build.gradle | 2 + .../io/ballerina/xsd/{cmd => cli}/XsdCmd.java | 109 ++++++++++-------- xsd-cli/src/main/java/module-info.java | 23 +++- .../services/io.ballerina.cli.BLauncherCmd | 2 +- 4 files changed, 88 insertions(+), 48 deletions(-) rename xsd-cli/src/main/java/io/ballerina/xsd/{cmd => cli}/XsdCmd.java (70%) diff --git a/xsd-cli/build.gradle b/xsd-cli/build.gradle index 5ee4d31..9d4ae7b 100644 --- a/xsd-cli/build.gradle +++ b/xsd-cli/build.gradle @@ -11,6 +11,8 @@ configurations.all { dependencies { implementation group: 'org.ballerinalang', name: 'ballerina-cli', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" implementation group: 'info.picocli', name: 'picocli', version: "${picocliVersion}" testImplementation group: 'junit', name: 'junit', version: "${junitVersion}" testImplementation group: 'org.testng', name: 'testng', version: "${testngVersion}" diff --git a/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java b/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java similarity index 70% rename from xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java rename to xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java index 90421f5..896a501 100644 --- a/xsd-cli/src/main/java/io/ballerina/xsd/cmd/XsdCmd.java +++ b/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java @@ -16,8 +16,9 @@ * under the License. */ -package io.ballerina.xsd.cmd; +package io.ballerina.xsd.cli; +import io.ballerina.projects.util.ProjectUtils; import io.ballerina.xsd.core.Response; import io.ballerina.xsd.core.XSDToRecord; import org.w3c.dom.Document; @@ -29,7 +30,6 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -58,11 +58,13 @@ ) public class XsdCmd implements BLauncherCmd { private static final String CMD_NAME = "xsd"; - private static final String FILE_OVERWRITE_PROMPT = "File already exists at %s. Overwrite? (y/N): "; + private static final String FILE_OVERWRITE_PROMPT = "The file '%s' already exists at %s. Overwrite? (y/N): "; public static final String INVALID_BALLERINA_DIRECTORY_ERROR = "Invalid Ballerina package directory: %s, cannot find 'Ballerina.toml' file"; public static final String INVALID_DIRECTORY_PATH = "Error: Invalid directory path has been provided. " + "Output path '%s' is a file"; + private static final String AUTO_GENERATED_MESSAGE = "// AUTO-GENERATED FILE. DO NOT MODIFY.\n" + + "// This file is auto-generated by the Ballerina XSD tool."; public static final String TYPES_FILE_NAME = "types.bal"; public static final String SLASH = "/"; private final PrintStream outStream; @@ -91,24 +93,29 @@ public void execute() { return; } Path currentDir = Paths.get("").toAbsolutePath(); - Path commandPath = currentDir.resolve("Ballerina.toml"); - if (!Files.exists(commandPath)) { - outStream.printf((INVALID_BALLERINA_DIRECTORY_ERROR) + "%n", commandPath); + if (!ProjectUtils.isBallerinaProject(currentDir)) { + outStream.printf(INVALID_BALLERINA_DIRECTORY_ERROR + "%n", currentDir); exitOnError(); return; } - Path outputDirPath = Paths.get(outputPath); + if (!ProjectUtils.validateModuleName(outputPath)) { + outStream.println("ERROR: invalid module name : '" + outputPath + "' :\n" + + "module name can only contain alphanumerics, underscores and periods"); + exitOnError(); + return; + } else if (!ProjectUtils.validateNameLength(outputPath)) { + outStream.println("ERROR: invalid module name : '" + outputPath + "' :\n" + + "maximum length of module name is 256 characters"); + exitOnError(); + return; + } + Path outputDirPath = Paths.get(outputPath).toAbsolutePath(); if (!Objects.equals(outputPath, EMPTY_STRING)) { Path basePath = Paths.get("modules").toAbsolutePath(); outputDirPath = basePath.resolve(outputPath).normalize(); - if (!outputDirPath.startsWith(basePath)) { - System.out.printf("Invalid output path: Path traversal detected in '%s'%n", outputPath); - exitOnError(); - return; - } } if (Files.exists(outputDirPath) && !Files.isDirectory(outputDirPath)) { - outStream.printf((INVALID_DIRECTORY_PATH) + "%n", outputPath); + outStream.printf(INVALID_DIRECTORY_PATH + "%n", outputPath); exitOnError(); return; } @@ -119,6 +126,9 @@ public void execute() { return; } try { + if (Files.notExists(outputDirPath)) { + Files.createDirectories(outputDirPath); + } if (!Files.exists(Path.of(argList.get(0)))) { outStream.println(argList.get(0) + " file does not exist."); return; @@ -131,19 +141,7 @@ public void execute() { exitOnError(); return; } - Path path; - if (outputPath.equals(EMPTY_STRING)) { - path = Path.of(TYPES_FILE_NAME); - } else { - path = outputDirPath.resolve(SLASH).resolve(TYPES_FILE_NAME); - } - Path destinationFile = Files.exists(path) ? handleFileOverwrite(path, outStream) : path; - Path parentDirectory = destinationFile.getParent(); - if (parentDirectory != null && !Files.exists(parentDirectory)) { - Files.createDirectories(parentDirectory); - } - Files.writeString(destinationFile, result.types()); - outStream.println("Output is successfully written to " + destinationFile); + writeSourceToFiles(outputDirPath, result); } catch (ParserConfigurationException | SAXException e) { outStream.println("XSD file contains errors. " + e.getLocalizedMessage()); } catch (IOException e) { @@ -154,28 +152,47 @@ public void execute() { } } - public static Path handleFileOverwrite(Path destinationFile, PrintStream outStream) { - if (!Files.exists(destinationFile)) { - return destinationFile; - } - String filePath = destinationFile.toString(); - outStream.printf(FILE_OVERWRITE_PROMPT, filePath); - String response = new Scanner(System.in).nextLine().trim().toLowerCase(); - if (response.equals("y")) { - return destinationFile; + private void writeSourceToFiles(Path outputPath, Response response) throws IOException { + Path clientPath = outputPath.resolve(TYPES_FILE_NAME); + String fileName = clientPath.getFileName().toString(); + if (Files.exists(clientPath)) { + outStream.printf(FILE_OVERWRITE_PROMPT, fileName, getModuleName(clientPath)); + String overwriteAccess = new Scanner(System.in).nextLine().trim().toLowerCase(); + if (overwriteAccess.equals("y")) { + generateFile(response.types(), clientPath, fileName); + } else { + outStream.printf("The operation is cancelled %n"); + } + } else { + generateFile(response.types(), clientPath, fileName); } - int counter = 1; - String fileName = new File(filePath).getName(); - int dotIndex = fileName.lastIndexOf('.'); - String baseName = dotIndex == -1 ? fileName : fileName.substring(0, dotIndex); - String extension = dotIndex == -1 ? EMPTY_STRING : fileName.substring(dotIndex); - String parentPath = new File(filePath).getParent() != null ? new File(filePath).getParent() : EMPTY_STRING; - while (Files.exists(destinationFile)) { - String newFileName = baseName + "." + counter + extension; - destinationFile = Path.of(parentPath, newFileName); - counter++; + } + + private void generateFile(String content, Path clientPath, String fileName) throws IOException { + Files.writeString(clientPath, addAutoGeneratedMessage(content)); + String outputModule = getModuleName(clientPath); + outStream.printf("The '%s' file is written to %s %n", fileName, outputModule); + } + + private static String getModuleName(Path clientPath) { + String outputModule; + String destinationPath = clientPath.toString(); + if (destinationPath.contains("modules")) { + int startIndex = destinationPath.indexOf("modules"); + int endIndex = destinationPath.lastIndexOf("/"); + if (endIndex > startIndex) { + outputModule = destinationPath.substring(startIndex, endIndex); + } else { + outputModule = destinationPath.substring(startIndex); + } + } else { + outputModule = "the default module"; } - return destinationFile; + return outputModule; + } + + private String addAutoGeneratedMessage(String content) { + return AUTO_GENERATED_MESSAGE + "\n\n" + content; } private static Document parseXSD(String xsdData) throws Exception { diff --git a/xsd-cli/src/main/java/module-info.java b/xsd-cli/src/main/java/module-info.java index d10399b..151bef9 100644 --- a/xsd-cli/src/main/java/module-info.java +++ b/xsd-cli/src/main/java/module-info.java @@ -1,7 +1,28 @@ -module io.ballerina.xsd.cmd { +/* + * Copyright (c) 2025, WSO2 LLC. (http://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. + */ + +module io.ballerina.xsd.cli { requires io.ballerina.xsd.core; requires info.picocli; requires io.ballerina.cli; requires io.ballerina.tools.api; requires java.xml; + requires io.ballerina.lang; + + exports io.ballerina.xsd.cli; } diff --git a/xsd-cli/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd b/xsd-cli/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd index cb23102..b348298 100644 --- a/xsd-cli/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd +++ b/xsd-cli/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd @@ -1 +1 @@ -io.ballerina.xsd.cmd.XsdCmd +io.ballerina.xsd.cli.XsdCmd From d45494fe1bc812240a1015b0c1659eb21a22532e Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Tue, 4 Feb 2025 02:27:05 +0530 Subject: [PATCH 13/23] Update xsd help text --- xsd-cli/src/main/resources/cli-docs/xsd-help.help | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xsd-cli/src/main/resources/cli-docs/xsd-help.help b/xsd-cli/src/main/resources/cli-docs/xsd-help.help index 7835e22..0bee3de 100644 --- a/xsd-cli/src/main/resources/cli-docs/xsd-help.help +++ b/xsd-cli/src/main/resources/cli-docs/xsd-help.help @@ -20,5 +20,5 @@ EXAMPLE $ bal xsd schema.xsd Generate types from an XSD schema with a specified relative path of the output file. The output will be saved - to the 'temp' module in the Ballerina project. + to the 'custom' submodule in the Ballerina project. $ bal xsd schema.xsd --module custom From 66de24fbac399e368c669f048ff9e334518615a7 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Tue, 4 Feb 2025 02:27:33 +0530 Subject: [PATCH 14/23] Update docs in the project --- README.md | 73 ++++++++++++++++++++++++++------- module-ballerina-xsd/Module.md | 73 ++++++++++++++++++++++++++------- module-ballerina-xsd/Package.md | 73 ++++++++++++++++++++++++++------- 3 files changed, 174 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 3e8a719..ad067fc 100644 --- a/README.md +++ b/README.md @@ -7,39 +7,82 @@ `XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. -The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. +The Ballerina XSD Tool simplifies the generation of Ballerina record types from an XSD specification, improving the user experience when integrating with XML-based operations in Ballerina. -### Command Syntax +### Installation -The `xsd` command in Ballerina is used for XSD to Ballerina code generation. +Execute the command below to pull the XSD tool from Ballerina Central. ```bash -bal xsd [--module ] +$ bal tool pull xsd ``` -#### Command Options +### Usage -| Option | Description | -|--------|-------------| -| `` | (Required) The path to the XSD file | -| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | +The XSD tool allows you to generate Ballerina record types from an XSD specification. -### Examples +To generate Ballerina types, use the following command. It is mandatory to run the command inside a Ballerina project. -#### 1. Generate types for the given XSD file +```bash +$ bal xsd + [--module ] +``` + +#### Command options + +| Option | Description | Mandatory/Optional | +|--------|-------------|--------------------| +| `` | (Required) The path to the XSD file | Mandatory | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | Optional | + +### Generate types for the given XSD file + +Use the following command to generate Ballerina record types for all elements defined in the specified XSD file. By default, the generated `types.bal` file will be placed in the default module of the current Ballerina project. + +```bash +$ bal xsd +``` + +For example, + +```bash +$ bal xsd sample.xsd +``` -This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. +If successful, you will see the following output. ```bash -bal xsd +The 'types.bal' file is written to the default module ``` -#### 2. Generate types in a specific module +#### Generate types in a specific module To generate the Ballerina record types in a specific module, use the `--module` option. ```bash -bal xsd --module custom +$ bal xsd --module +``` + +For example, + +```bash +$ bal xsd sample.xsd --module custom +``` + +This will generate a `types.bal` file inside the `custom` submodule within the Ballerina project. + +The following output will be displayed. + +```bash +The 'types.bal' file is written to 'modules/custom' +``` + +Upon successful execution, the generated files will include, + +```bash +modules/ +└── custom/ + └── types.bal ``` ## Building from the Source diff --git a/module-ballerina-xsd/Module.md b/module-ballerina-xsd/Module.md index d9d69a2..0d07f48 100644 --- a/module-ballerina-xsd/Module.md +++ b/module-ballerina-xsd/Module.md @@ -2,37 +2,80 @@ `XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. -The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. +The Ballerina XSD Tool simplifies the generation of Ballerina record types from an XSD specification, improving the user experience when integrating with XML-based operations in Ballerina. -### Command Syntax +### Installation -The `xsd` command in Ballerina is used for XSD to Ballerina code generation. +Execute the command below to pull the XSD tool from Ballerina Central. ```bash -bal xsd [--module ] +$ bal tool pull xsd ``` -#### Command Options +### Usage -| Option | Description | -|--------|-------------| -| `` | (Required) The path to the XSD file | -| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | +The XSD tool allows you to generate Ballerina record types from an XSD specification. -### Examples +To generate Ballerina types, use the following command. It is mandatory to run the command inside a Ballerina project. -#### 1. Generate types for the given XSD file +```bash +$ bal xsd + [--module ] +``` + +#### Command options + +| Option | Description | Mandatory/Optional | +|--------|-------------|--------------------| +| `` | The path to the XSD file | Mandatory | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | Optional | + +### Generate types for the given XSD file + +Use the following command to generate Ballerina record types for all elements defined in the specified XSD file. By default, the generated `types.bal` file will be placed in the default module of the current Ballerina project. + +```bash +$ bal xsd +``` + +For example, + +```bash +$ bal xsd sample.xsd +``` -This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. +If successful, you will see the following output. ```bash -bal xsd +The 'types.bal' file is written to the default module ``` -#### 2. Generate types in a specific module +#### Generate types in a specific module To generate the Ballerina record types in a specific module, use the `--module` option. ```bash -bal xsd --module custom +$ bal xsd --module +``` + +For example, + +```bash +$ bal xsd sample.xsd --module custom +``` + +This will generate a `types.bal` file inside the `custom` submodule within the Ballerina project. + +The following output will be displayed. + +```bash +The 'types.bal' file is written to 'modules/custom' +``` + +Upon successful execution, the generated files will include, + +```bash +modules/ +└── custom/ + └── types.bal ``` diff --git a/module-ballerina-xsd/Package.md b/module-ballerina-xsd/Package.md index cdc4d5e..c22b448 100644 --- a/module-ballerina-xsd/Package.md +++ b/module-ballerina-xsd/Package.md @@ -2,37 +2,80 @@ `XSD` (XML Schema Definition) is an approach to define the structure, elements, and constraints of XML documents. It is widely used for validating the content and structure of XML files. -The Ballerina XSD Tool simplifies the process of generating Ballerina record types from an XSD specification. These generated types helps to integrate with XML-based operations in Ballerina. +The Ballerina XSD Tool simplifies the generation of Ballerina record types from an XSD specification, improving the user experience when integrating with XML-based operations in Ballerina. -#### Command Syntax +### Installation -The `xsd` command in Ballerina is used for XSD to Ballerina code generation. +Execute the command below to pull the XSD tool from Ballerina Central. ```bash -bal xsd [--module ] +$ bal tool pull xsd ``` -### Command Options +### Usage -| Option | Description | -|--------|-------------| -| `` | (Required) The path to the XSD file | -| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | +The XSD tool allows you to generate Ballerina record types from an XSD specification. -### Examples +To generate Ballerina types, use the following command. It is mandatory to run the command inside a Ballerina project. -#### 1. Generate types for the given XSD file +```bash +$ bal xsd + [--module ] +``` + +#### Command options + +| Option | Description | Mandatory/Optional | +|--------|-------------|--------------------| +| `` | (Required) The path to the XSD file | Mandatory | +| `-m`, `--module` | The name of the module in which the Ballerina record types are generated | Optional | + +### Generate types for the given XSD file + +Use the following command to generate Ballerina record types for all elements defined in the specified XSD file. By default, the generated `types.bal` file will be placed in the default module of the current Ballerina project. + +```bash +$ bal xsd +``` + +For example, + +```bash +$ bal xsd sample.xsd +``` -This command generates Ballerina record types for all the elements defined in the specified XSD file. The generated files will be placed in the current Ballerina project. +If successful, you will see the following output. ```bash -bal xsd +The 'types.bal' file is written to the default module ``` -#### 2. Generate types in a specific module +#### Generate types in a specific module To generate the Ballerina record types in a specific module, use the `--module` option. ```bash -bal xsd --module custom +$ bal xsd --module +``` + +For example, + +```bash +$ bal xsd sample.xsd --module custom +``` + +This will generate a `types.bal` file inside the `custom` submodule within the Ballerina project. + +The following output will be displayed. + +```bash +The 'types.bal' file is written to 'modules/custom' +``` + +Upon successful execution, the generated files will include, + +```bash +modules/ +└── custom/ + └── types.bal ``` From c1ad1dcc6e02d9f1501b6aa8f8c57b9c3c5e0af7 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Fri, 7 Feb 2025 09:54:25 +0530 Subject: [PATCH 15/23] Fix minor issues --- build-config/checkstyle/build.gradle | 22 ---------------------- build-config/spotbugs-exclude.xml | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/build-config/checkstyle/build.gradle b/build-config/checkstyle/build.gradle index 5eac081..262f33c 100644 --- a/build-config/checkstyle/build.gradle +++ b/build-config/checkstyle/build.gradle @@ -34,28 +34,6 @@ task downloadCheckstyleRuleFiles(type: Download) { dest buildDir } -// Task to remove -task modifyCheckstyleConfig { - dependsOn downloadCheckstyleRuleFiles -// doLast { -// File checkstyleFile = file("$project.buildDir/checkstyle.xml") -// if (checkstyleFile.exists()) { -// // Parse the checkstyle.xml file -// def xml = new XmlParser().parse(checkstyleFile) -// -// // Find and remove -// xml.module.findAll { it.'@name' == 'StaticVariableName' }.each { staticVariableModule -> -// xml.remove(staticVariableModule) -// } -// -// // Write the updated XML back to the file -// checkstyleFile.text = XmlUtil.serialize(xml) -// } else { -// throw new GradleException("Checkstyle file not found at $checkstyleFile") -// } -// } -} - // Disable jar task jar { enabled = false diff --git a/build-config/spotbugs-exclude.xml b/build-config/spotbugs-exclude.xml index 4a21761..74937b0 100644 --- a/build-config/spotbugs-exclude.xml +++ b/build-config/spotbugs-exclude.xml @@ -89,4 +89,4 @@ - \ No newline at end of file + From 1ae2a4160ada778d9da1a71036a7a84984bb8759 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Fri, 7 Feb 2025 09:54:39 +0530 Subject: [PATCH 16/23] Remove unnecessary variable --- xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java | 1 - 1 file changed, 1 deletion(-) diff --git a/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java b/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java index 896a501..fd21afb 100644 --- a/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java +++ b/xsd-cli/src/main/java/io/ballerina/xsd/cli/XsdCmd.java @@ -66,7 +66,6 @@ public class XsdCmd implements BLauncherCmd { private static final String AUTO_GENERATED_MESSAGE = "// AUTO-GENERATED FILE. DO NOT MODIFY.\n" + "// This file is auto-generated by the Ballerina XSD tool."; public static final String TYPES_FILE_NAME = "types.bal"; - public static final String SLASH = "/"; private final PrintStream outStream; private final boolean exitWhenFinish; @CommandLine.Option(names = {"-h", "--help"}, hidden = true) From 6cd59f24420395596cb64918dd9bbedec907ef38 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Fri, 7 Feb 2025 09:58:25 +0530 Subject: [PATCH 17/23] [Automated] Update the native jar versions --- module-ballerina-xsd/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module-ballerina-xsd/Dependencies.toml b/module-ballerina-xsd/Dependencies.toml index 0767bad..1de1aac 100644 --- a/module-ballerina-xsd/Dependencies.toml +++ b/module-ballerina-xsd/Dependencies.toml @@ -5,7 +5,7 @@ [ballerina] dependencies-toml-version = "2" -distribution-version = "2201.10.0" +distribution-version = "2201.11.0" [[package]] org = "ballerina" From 35c4619f2e552105e16c58c5b4d358f3f144ef06 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Fri, 7 Feb 2025 09:59:12 +0530 Subject: [PATCH 18/23] Fix checkstyle build --- build-config/checkstyle/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-config/checkstyle/build.gradle b/build-config/checkstyle/build.gradle index 262f33c..9f9de56 100644 --- a/build-config/checkstyle/build.gradle +++ b/build-config/checkstyle/build.gradle @@ -46,7 +46,7 @@ clean { // Add modified checkstyle.xml and suppressions.xml as artifacts artifacts.add('default', file("$project.buildDir/checkstyle.xml")) { - builtBy('modifyCheckstyleConfig') + builtBy('downloadCheckstyleRuleFiles') } artifacts.add('default', file("$project.buildDir/suppressions.xml")) { From 080133f93a39cd4146faf036e07407087e80e213 Mon Sep 17 00:00:00 2001 From: Nuvindu Date: Fri, 7 Feb 2025 09:59:26 +0530 Subject: [PATCH 19/23] Update the lang version to 2201.11.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 54215b7..6338317 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ group=io.ballerina version=0.1.0-SNAPSHOT # Dependencies -ballerinaLangVersion=2201.10.0 +ballerinaLangVersion=2201.11.0 checkstylePluginVersion=10.12.0 spotbugsPluginVersion=6.0.18 shadowJarPluginVersion=8.1.1 From 336eb82bc104bb072ca4a47cd38b6724f12384f2 Mon Sep 17 00:00:00 2001 From: Nuvindu Nirmana <63797478+Nuvindu@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:34:56 +0530 Subject: [PATCH 20/23] Update license header Co-authored-by: MohamedSabthar --- build-config/spotbugs-exclude.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-config/spotbugs-exclude.xml b/build-config/spotbugs-exclude.xml index 74937b0..630729e 100644 --- a/build-config/spotbugs-exclude.xml +++ b/build-config/spotbugs-exclude.xml @@ -1,5 +1,5 @@