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

jlibs.xml.xsd.XSInliner Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/**
 * Copyright 2015 Santhosh Kumar Tekuri
 *
 * The JLibs authors license this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package jlibs.xml.xsd;

import jlibs.core.io.CharArrayWriter2;
import jlibs.core.lang.ImpossibleException;
import jlibs.core.lang.OS;
import jlibs.core.net.URLUtil;
import jlibs.xml.Namespaces;
import jlibs.xml.sax.SAXDelegate;
import jlibs.xml.sax.SAXUtil;
import jlibs.xml.sax.helpers.MyNamespaceSupport;
import jlibs.xml.xsl.TransformerUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;

import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.util.*;

/**
 * @author Santhosh Kumar T
 */
public class XSInliner extends SAXDelegate{
    private static final String SCHEMA_LOCATION = "schemaLocation";
    private static final String IMPORT = "import";

    private String outputSystemID;
    private static final List TOP_ELEMS = Arrays.asList(IMPORT, "redefine", "annotation");
    public Document inline(InputSource input, String outputSystemID) throws IOException, SAXException, ParserConfigurationException, TransformerConfigurationException{
        DOMResult result = new DOMResult();
        TransformerHandler handler = TransformerUtil.newTransformerHandler(null, true, 4, null);
        setHandler(handler);
        handler.setResult(result);
        this.outputSystemID = outputSystemID==null ? input.getSystemId() : outputSystemID;
        if(this.outputSystemID!=null)
            this.outputSystemID = URLUtil.toURL(this.outputSystemID).toString();
        parse(input);

        Document doc = (Document)result.getNode();
        Element root = doc.getDocumentElement();

        // get children xs:import, xs:redefine and xs:annotation
        NodeList children = root.getChildNodes();
        List list = new ArrayList();
        for(int i=0; i=0; i--){
             Node node = list.get(i);
            if(root.getFirstChild()==null)
                root.appendChild(node);
            else
                root.insertBefore(node, root.getFirstChild());
        }

        return doc;
    }

    public Document inline(InputSource input) throws IOException, SAXException, ParserConfigurationException, TransformerConfigurationException{
        return inline(input, null);
    }

    private void parse(InputSource source) throws ParserConfigurationException, IOException, SAXException{
        SAXParser parser = SAXUtil.newSAXParser(true, false, false);
        SAXUtil.setHandler(parser.getXMLReader(), this);
        parser.getXMLReader().parse(source);
    }

    Context mainContext;
    Context curContext;
    static class Context{
        Context parent;

        MyNamespaceSupport nsSupport = new MyNamespaceSupport();
        String systemID;
        String targetNamespace;
        int depth;

        Context(Context parent, String systemID){
            this.parent = parent;
            this.systemID = systemID;
        }

        String resolve(String location){
            return URLUtil.resolve(systemID, location).toString();
        }

        public void setTargetNamespace(String namespace){
            targetNamespace = namespace==null ? "" : namespace;
        }
    }

    private String tempSystemID;
    @Override
    public void setDocumentLocator(Locator locator){
        tempSystemID = locator.getSystemId();
    }

    @Override
    public void startDocument() throws SAXException{
        curContext = new Context(curContext, tempSystemID);
        if(mainContext==null)
            mainContext = curContext;
        curContext.nsSupport.startDocument();

        if(isMainDocument())
            super.startDocument();
    }

    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException{
        curContext.nsSupport.startPrefixMapping(prefix, uri);
        if(!isMainDocument()){
            if(mainContext.nsSupport.findPrefix(uri)!=null)
                return;
            prefix = mainContext.nsSupport.startPrefixMapping(uri);
            if(curContext.depth==0)
                return;
        }
        super.startPrefixMapping(prefix, uri);
    }

    private static final List QNAME_ATTS = Arrays.asList("type", "ref", "base");

    private Set included = new HashSet();
    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
        curContext.depth++;
        curContext.nsSupport.startElement();
        Enumeration declaredPrefixes = mainContext.nsSupport.getDeclaredPrefixes();
        if(!isMainDocument())
            mainContext.nsSupport.startElement();
        if(isInclude(uri, localName)){
            try{
                InputSource importSource = null;
                String location = curContext.resolve(atts.getValue(SCHEMA_LOCATION));
                String importElem = "<"+qName+" schemaLocation=\""+atts.getValue(SCHEMA_LOCATION)+"\">";
                comment(importElem.toCharArray(), 0, importElem.length());
                if(!included.add(location))
                    return;
                EntityResolver resolver = getEntityResolver();
                if(resolver!=null)
                    importSource = resolver.resolveEntity(null, location);
                if(importSource==null)
                    importSource = new InputSource(location);
                parse(importSource);
                return;
            }catch(Exception ex){
                if(ex instanceof SAXException)
                    throw (SAXException)ex;
                throw new SAXException(ex);
            }
        }

        if(isRoot(uri, localName)){
            curContext.setTargetNamespace(atts.getValue("targetNamespace"));
            if(isMainDocument() && !mainContext.targetNamespace.isEmpty()){
                if(mainContext.nsSupport.findPrefix(mainContext.targetNamespace)==null){
                    String prefix = mainContext.nsSupport.declarePrefix(mainContext.targetNamespace);
                    super.startPrefixMapping(prefix, mainContext.targetNamespace);
                }
            }
        }

        if(isMainDocument()){
             if(isImport(uri, localName) && atts.getValue(SCHEMA_LOCATION)!=null){
                 AttributesImpl newAtts = new AttributesImpl(atts);
                 String attValue = atts.getValue(SCHEMA_LOCATION);
                 String location = curContext.resolve(attValue);
                 attValue = URLUtil.relativize(outputSystemID, location).toString();
                 newAtts.setValue(atts.getIndex(SCHEMA_LOCATION), attValue);
                 atts = newAtts;
             }
        }else{
            if(isRoot(uri, localName))
                return;
            qName = mainContext.nsSupport.toQName(uri, localName);
            AttributesImpl newAtts = new AttributesImpl();
            for(int i=0; i";
            comment(importElem.toCharArray(), 0, importElem.length());
            return;
        }
        if(!isMainDocument()){
            if(isRoot(uri, localName))
                return;
            qName = mainContext.nsSupport.toQName(uri, localName);
        }
        super.endElement(uri, localName, qName);
        if(!isMainDocument() && curContext.depth==1){
            Enumeration declaredPrefixes = mainContext.nsSupport.getDeclaredPrefixes();
            while(declaredPrefixes.hasMoreElements()){
                String prefix = declaredPrefixes.nextElement();
                super.endPrefixMapping(prefix);
            }
        }
    }

    @Override
    public void endPrefixMapping(String prefix) throws SAXException{
        if(isMainDocument())
            super.endPrefixMapping(prefix);
    }

    @Override
    public void endDocument() throws SAXException{
        if(isMainDocument())
            super.endDocument();
        curContext = curContext.parent;
    }

    private boolean isMainDocument(){
        return curContext==mainContext;
    }

    private boolean isRoot(String uri, String localName){
        return Namespaces.URI_XSD.equals(uri) && "schema".equals(localName);
    }

    private boolean isInclude(String uri, String localName){
        return Namespaces.URI_XSD.equals(uri) && "include".equals(localName);
    }

    private boolean isImport(String uri, String localName){
        return Namespaces.URI_XSD.equals(uri) && IMPORT.equals(localName);
    }

    public static InputSource include(String targetNamespace, String... systemIDs) throws TransformerConfigurationException{
        CharArrayWriter2 writer = new CharArrayWriter2();
        XSDocument doc = new XSDocument(new StreamResult(writer), true, 4, null);
        try{
            doc.startDocument();
            doc.startSchema(targetNamespace);
            for(String systemID: systemIDs)
                doc.addInclude(URLUtil.toURL(systemID).toString());
            doc.endSchema();
            doc.endDocument();
        }catch(SAXException ex){
            throw new ImpossibleException(ex);
        }
        return new InputSource(writer.toCharSequence().asReader());
    }

    public static void main(String[] args) throws Exception{
        if(args.length==0){
            System.err.println("Usage:");
            System.err.println("\txsd-inline."+(OS.get().isWindows()?"bat":"sh")+" ");
            System.err.println("\txsd-inline."+(OS.get().isWindows()?"bat":"sh")+"   ...");
            System.exit(1);
        }

        InputSource source;
        if(args.length==1)
            source = new InputSource(args[0]);
        else{
            String targetNamespace = args[0];
            String xsdFiles[] = Arrays.copyOfRange(args, 1, args.length);
            source = XSInliner.include(targetNamespace, xsdFiles);
        }
        Document doc = new XSInliner().inline(source, "temp.xsd");
        TransformerUtil.newTransformer(null, true, 0, null)
                .transform(new DOMSource(doc), new StreamResult(System.out));
        System.out.println();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy