All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wiztools.xsdgen.XsdGen Maven / Gradle / Ivy

The newest version!
package org.wiztools.xsdgen;

import nu.xom.*;
import org.wiztools.commons.Charsets;
import org.wiztools.commons.StringUtil;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;

/**
 * @author subWiz
 */
public final class XsdGen {

    private static final String XSD_NS_URI = "http://www.w3.org/2001/XMLSchema";

    private final String xsdPrefix;
    private final boolean enableMaxOccursOnce;

    private Document doc = null; // populated after the parse method is called!

    /**
     * Constructs new {@code XsdGen} with defaults settings.
     */
    public XsdGen() {
        this(new XsdConfig());
    }

    /**
     * Constructs new {@code XsdGen} with parameters :
     *
     * @param config             the XSD configuration
     */
    public XsdGen(XsdConfig config) {
        xsdPrefix = config.getXsdPrefix();
        this.enableMaxOccursOnce = config.isEnableMaxOccursOnce();
    }


    private void processAttributes(final Element inElement, final Element outElement) {
        for (int i = 0; i < inElement.getAttributeCount(); i++) {
            final Attribute attr = inElement.getAttribute(i);
            final String name = attr.getLocalName();
            final String value = attr.getValue();
            Element attrElement = new Element(xsdPrefix + ":attribute", XSD_NS_URI);
            attrElement.addAttribute(new Attribute("name", name));
            attrElement.addAttribute(new Attribute("type", xsdPrefix + TypeInferenceUtil.getTypeOfContent(value)));
            attrElement.addAttribute(new Attribute("use", "required"));
            outElement.appendChild(attrElement);
        }
    }

    private void recurseGen(Element parent, Element parentOutElement) {
        // Adding complexType element:
        Element complexType = new Element(xsdPrefix + ":complexType", XSD_NS_URI);
        complexType.addAttribute(new Attribute("mixed", "true"));
        Element sequence = new Element(xsdPrefix + ":sequence", XSD_NS_URI);
        complexType.appendChild(sequence);
        processAttributes(parent, complexType);
        parentOutElement.appendChild(complexType);

        Elements children = parent.getChildElements();
        final Set elementNamesProcessed = new HashSet<>();
        for (int i = 0; i < children.size(); i++) {
            Element e = children.get(i);
            final String localName = e.getLocalName();
            final String nsURI = e.getNamespaceURI();
            final String nsName = e.getQualifiedName();

            if (!elementNamesProcessed.contains(nsName)) { // process an element first time only
                if (e.getChildElements().size() > 0) { // Is complex type with children!
                    Element element = new Element(xsdPrefix + ":element", XSD_NS_URI);
                    element.addAttribute(new Attribute("name", localName));
                    processOccurences(element, parent, localName, nsURI);
                    recurseGen(e, element); // recurse into children:
                    sequence.appendChild(element);

                } else {
                    final String cnt = e.getValue();
                    final String eValue = cnt == null ? null : cnt.trim();
                    final String type = xsdPrefix + TypeInferenceUtil.getTypeOfContent(eValue);
                    Element element = new Element(xsdPrefix + ":element", XSD_NS_URI);
                    element.addAttribute(new Attribute("name", localName));
                    processOccurences(element, parent, localName, nsURI);

                    // Attributes
                    final int attrCount = e.getAttributeCount();
                    if (attrCount > 0) {
                        // has attributes: complex type without sequence!
                        Element complexTypeCurrent = new Element(xsdPrefix + ":complexType", XSD_NS_URI);
                        complexType.addAttribute(new Attribute("mixed", "true"));
                        Element simpleContent = new Element(xsdPrefix + ":simpleContent", XSD_NS_URI);
                        Element extension = new Element(xsdPrefix + ":extension", XSD_NS_URI);
                        extension.addAttribute(new Attribute("base", type));
                        processAttributes(e, extension);
                        simpleContent.appendChild(extension);
                        complexTypeCurrent.appendChild(simpleContent);
                        element.appendChild(complexTypeCurrent);
                    } else { // if no attributes, just put the type:
                        element.addAttribute(new Attribute("type", type));
                    }
                    sequence.appendChild(element);
                }
            }
            elementNamesProcessed.add(nsName);
        }
    }

    private void processOccurences(final Element element, final Element parent,
                                   final String localName, final String nsURI) {
        if (parent.getChildElements(localName, nsURI).size() > 1) {
            element.addAttribute(new Attribute("maxOccurs", "unbounded"));
        } else {
            element.addAttribute(new Attribute("minOccurs", "0"));
            if (enableMaxOccursOnce) element.addAttribute(new Attribute("maxOccurs", "1"));
        }
    }

    private Document getDocument(File file) throws ParsingException, IOException {
        Builder parser = new Builder();
        Document d = parser.build(file);
        final Element rootElement = d.getRootElement();

        // output Document
        Element outRoot = new Element(xsdPrefix + ":schema", XSD_NS_URI);
        Document outDoc = new Document(outRoot);

        // setting targetNamespace
        final String nsPrefix = rootElement.getNamespacePrefix();
        final boolean hasXmlns = rootElement.getNamespaceDeclarationCount() > 0;
        if (hasXmlns || StringUtil.isNotEmpty(nsPrefix)) {
            outRoot.addAttribute(new Attribute("targetNamespace", rootElement.getNamespaceURI()));
            outRoot.addAttribute(new Attribute("elementFormDefault", "qualified"));
        }

        // adding all other namespace attributes
        for (int i = 0; i < rootElement.getNamespaceDeclarationCount(); i++) {
            final String nsPrefix2 = rootElement.getNamespacePrefix(i);
            final String nsURI = rootElement.getNamespaceURI(nsPrefix2);
            outRoot.addNamespaceDeclaration(nsPrefix, nsURI);
        }

        // adding the root element
        Element rootElementXsd = new Element(xsdPrefix + ":element", XSD_NS_URI);
        rootElementXsd.addAttribute(new Attribute("name", rootElement.getLocalName()));
        outRoot.appendChild(rootElementXsd);
        recurseGen(rootElement, rootElementXsd);
        return outDoc;
    }

    public XsdGen parse(File file) throws IOException, ParseException {
        try {
            doc = getDocument(file);
            return this;
        } catch (ParsingException ex) {
            throw new ParseException(ex);
        }
    }

    public void write(final OutputStream os) throws IOException {
        if (doc == null) throw new IllegalStateException("Call parse() before calling this method!");
        write(os, Charsets.UTF_8);
    }

    public void write(final OutputStream os, final Charset charset) throws IOException {
        if (doc == null) throw new IllegalStateException("Call parse() before calling this method!");
        // Display output:
        Serializer serializer = new Serializer(os, charset.name());
        serializer.setIndent(4);
        serializer.write(doc);
    }

    @Override
    public String toString() {
        return doc.toXML();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy