edu.vt.cs.cnd2xsd.Cnd2XsdConverter.java Source code

Java tutorial

Introduction

Here is the source code for edu.vt.cs.cnd2xsd.Cnd2XsdConverter.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.vt.cs.cnd2xsd;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeExistsException;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.apache.jackrabbit.core.TransientRepository;
import org.w3.generated.FormChoice;
import org.w3.generated.OpenAttrs;
import org.w3.generated.SchemaElement;
//import org.w3.generated.TopLevelElement;
import javax.xml.namespace.QName;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.jackrabbit.commons.cnd.CndImporter;
import org.apache.jackrabbit.commons.cnd.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3.generated.Attribute;
import org.w3.generated.ComplexType;
import org.w3.generated.ComplexTypeElement;
import org.w3.generated.ElementElement;
import org.w3.generated.ExplicitGroup;

/**
 *
 * @author adeka
 */
public class Cnd2XsdConverter {
    private static Logger log = LoggerFactory.getLogger(Cnd2XsdConverter.class);
    /**
     * map of in-built nodetype vs the properties associated with them.
     * Overwrites the default behavior
     */
    private Map<String, String[]> attrMap;

    private NodeType[] ntypes;

    private static final String MAINCLI = "java -jar cnd2xsd-<version>.jar";

    private String namespace;

    private String root;

    private String rootType;

    private void loadPropertyMap(String path) {
        FileInputStream stream = null;
        try {
            stream = new FileInputStream(path);
            attrMap = new HashMap<String, String[]>();
            byte[] buffer = new byte[512];
            StringBuffer stBuffer = new StringBuffer();
            while (stream.available() > 0) {
                int n = stream.read(buffer, 0, 512);
                stBuffer.append(new String(buffer, 0, n));
            }
            String value = stBuffer.substring(0);
            //now lets split the value into multiple lines
            String[] lines = value.split("\n");
            log.debug("Buffer contains :{} lines", lines.length);

            for (String line : lines) {
                String[] kvs = line.split("#");
                String key = kvs[0];
                String[] values = null;
                if (kvs.length > 1) {
                    values = kvs[1].split(",");
                    log.debug("Key: {} Value:{}", key, kvs[1]);
                }
                attrMap.put(key, values);

            }

        } catch (IOException ex) {
            log.error("Exception caught:", ex);
        }

        finally {
            try {
                stream.close();
            } catch (IOException ex) {
                log.error("Exception caught:", ex);
            }
        }

    }

    /**
     * Registers custom NodeType definitions to the RSR determined by the session.
     * @param session the RSR session
     * @param cndFileName path to the CND file
     * @return the array of registered NodeTypes in the RSR
     * @throws RepositoryException
     * @throws IOException
     */
    public static NodeType[] RegisterCustomNodeTypes(Session session, String cndFileName, String prefix)
            throws RepositoryException, IOException {
        FileReader reader = null;
        try {
            reader = new FileReader(cndFileName);

            NodeType[] newNodeTypes = CndImporter.registerNodeTypes(reader, session);
            for (NodeType nt : newNodeTypes) {
                log.debug("Registered: " + nt.getName());
            }
            if (newNodeTypes == null || newNodeTypes.length == 0) {
                NodeTypeManager man = session.getWorkspace().getNodeTypeManager();
                NodeTypeIterator nit = man.getAllNodeTypes();
                List<NodeType> nlist = new LinkedList<NodeType>();
                while (nit.hasNext()) {
                    NodeType nt = nit.nextNodeType();
                    if (nt.getName().contains(prefix)) {
                        log.debug("node type :" + nt.getName());
                        nlist.add(nt);
                    }
                }
                return nlist.toArray(new NodeType[nlist.size()]);
            } else {
                return newNodeTypes;
            }
        } catch (InvalidNodeTypeDefinitionException ex) {
            ex.printStackTrace();
        } catch (NodeTypeExistsException ex) {
            ex.printStackTrace();
        } catch (UnsupportedRepositoryOperationException ex) {
            ex.printStackTrace();
        } catch (ParseException ex) {
            ex.printStackTrace();
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
        return null;

    }

    /**
     * Usage: Cnd2Xsd [path to source cnd] [path to write the xsd]
     * @param args
     * @throws LoginException
     * @throws RepositoryException
     * @throws IOException
     * @throws JAXBException
     */
    @SuppressWarnings("static-access")
    public static void main(String[] args) throws LoginException, RepositoryException, IOException, JAXBException,
            org.apache.commons.cli.ParseException {

        Session session = null;
        Cnd2XsdConverter converter = new Cnd2XsdConverter();

        try {
            Options opt = new Options();

            opt.addOption(OptionBuilder.hasArg(true).isRequired(false)
                    .withDescription("Path for the input cnd file").create("fc"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false).withDescription("Path for properties map.")
                    .create("fp"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false)
                    .withDescription("Path for generating XML schema.").create("fx"));
            opt.addOption(OptionBuilder.hasArg(false).isRequired(false).withDescription("Prints this list.")
                    .create("help"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false).withDescription("The namespace for the XSD.")
                    .create("ns"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false).withDescription("The namespace prefix.")
                    .create("nsp"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false)
                    .withDescription("The root element in the XSD.").create("r"));
            opt.addOption(OptionBuilder.hasArg(true).isRequired(false).withDescription("The root element type.")
                    .create("rtype"));

            //create the basic parser
            BasicParser parser = new BasicParser();
            CommandLine cl = parser.parse(opt, args);
            HelpFormatter f = new HelpFormatter();
            //check if we have any leftover args
            if (cl.getArgs().length != 0 || args.length == 0) {
                f.printHelp(MAINCLI, opt);
                return;
            }

            if (cl.hasOption("help")) {
                f.printHelp(MAINCLI, opt);
                return;
            }

            String cndFilePath = cl.getOptionValue("fc");
            String xsdFilePath = cl.getOptionValue("fx");
            String propmapPath = cl.getOptionValue("fp");
            String ns = cl.getOptionValue("ns");
            String nsPrefix = cl.getOptionValue("nsp");
            String rt = cl.getOptionValue("r");
            String rtype = cl.getOptionValue("rtype");

            converter.init(cndFilePath, propmapPath, ns, nsPrefix, rt, rtype);
            FileOutputStream fout = new FileOutputStream(xsdFilePath);
            converter.convert(fout);

        } finally {
            if (session != null) {
                session.save();
                session.logout();
            }
        }
    }

    private void convert(OutputStream stream) {
        OutputStream fout = stream;
        try {

            SchemaElement schemaRoot = new SchemaElement();

            schemaRoot.setElementFormDefault(FormChoice.QUALIFIED);
            schemaRoot.setTargetNamespace(this.namespace);
            JAXBContext jc = JAXBContext.newInstance(SchemaElement.class);
            Marshaller m = jc.createMarshaller();
            List<OpenAttrs> rootAttrList = schemaRoot.getIncludesAndImportsAndRedefines();
            ElementElement rootElement = new ElementElement();
            QName qname = new QName(this.namespace, this.rootType);
            rootElement.setType(qname);
            rootElement.setName(this.root);
            rootAttrList.add(rootElement);

            //the first level nodes that are children of rsrRoot are those nodes that
            //do not have any parent nodes in the cnd.

            for (NodeType nt : ntypes) {

                log.debug("NodeType:" + nt.getName());

                //check if we already have that node - if we have then update it

                QName name = getQualifiedName(nt.getName());

                ComplexTypeElement ctype = (ComplexTypeElement) getComplexType(rootAttrList, name.getLocalPart(),
                        attrMap.containsKey(nt.getName()) ? attrMap.get(nt.getName()) : null);

                for (NodeType pt : nt.getDeclaredSupertypes()) {
                    log.debug("  DeclaredSuperType:" + pt.getName());
                    //based on the supertypes we will have to make decisions
                    if (attrMap.containsKey(pt.getName())) {
                        //check if we have to create a node
                        String[] attrs = attrMap.get(pt.getName());
                        if (attrs != null) {
                            //get the qualified name
                            QName ename = getQualifiedName(pt.getName());
                            //create a complex type
                            //check if the complex type already there in the rootAttrList
                            ComplexType ctf = findComplexType(rootAttrList, ename.getLocalPart());

                            if (ctf == null) {
                                ctf = new ComplexTypeElement();
                                ctf.setName(ename.getLocalPart());
                                //add the attributes
                                for (String attr : attrs) {
                                    Attribute attribute = new Attribute();
                                    QName type = new QName(Constants.XML_NAMESPACE, Constants.STRING);
                                    attribute.setType(type);
                                    attribute.setName(attr);
                                    ctf.getAttributesAndAttributeGroups().add(attribute);
                                }

                                //add this complex type to the attribute list of the root element
                                rootAttrList.add(ctf);
                            }

                            //create an element of the above complex type and add as element
                            ElementElement element = new ElementElement();
                            element.setName(ename.getLocalPart());
                            element.setType(new QName(this.namespace, ctf.getName()));
                            element.setMinOccurs(BigInteger.ONE);
                            element.setMaxOccurs("1");
                            //element.setType(new QName(ctf.));
                            //now add this element to the top level complex type's sequence
                            ctype.getSequence().getElementsAndGroupsAndAlls().add(element);

                        }
                    }
                    //the supertype is not a pre-define type - we then have to add it as an element
                    else {

                        QName qn = getQualifiedName(pt.getName());
                        ComplexType ctf = getComplexType(rootAttrList, qn.getLocalPart(),
                                attrMap.containsKey(nt.getName()) ? attrMap.get(nt.getName()) : null);

                        //create an element of the above type and add as element
                        ElementElement element = new ElementElement();
                        element.setName(qn.getLocalPart());
                        element.setType(new QName(this.namespace, ctf.getName()));
                        element.setMinOccurs(BigInteger.ONE);
                        element.setMaxOccurs("1");

                        //element.setType(new QName(ctf.));
                        //now add this element to the top level complex type's sequence
                        ctype.getSequence().getElementsAndGroupsAndAlls().add(element);

                    }
                }

                for (NodeDefinition nd : nt.getDeclaredChildNodeDefinitions()) {
                    log.debug("  Declared ChildNode Definition:" + nd.getName());
                    //check default primary type
                    NodeType defaultNT = nd.getDefaultPrimaryType();
                    if (defaultNT == null) {
                        log.debug("Default Primary Type for the node:" + nd.getName() + " is null");
                        //look for the primary type
                        NodeType[] nts = nd.getRequiredPrimaryTypes();
                        if (ntypes == null) {
                            log.debug("No required primary type for node:" + nd.getName());
                        } else {
                            defaultNT = nts[0];
                            log.debug("Assuming first primary  type:" + defaultNT.getName() + " for node:"
                                    + nd.getName());
                        }

                    }
                    log.debug("  Default Primary Type Name:" + defaultNT.getName());
                    ElementElement element = new ElementElement();
                    if (nd.getName().equals("*")) {
                        QName qn = getQualifiedName(defaultNT.getName());
                        ComplexType ct = getComplexType(rootAttrList, qn.getLocalPart(),
                                attrMap.containsKey(nt.getName()) ? attrMap.get(nt.getName()) : null);

                        //QName ename = getQualifiedName(ct.getName());
                        element.setName(ct.getName());
                        element.setType(new QName(this.namespace, ct.getName()));
                        element.setMinOccurs(nd.isMandatory() ? BigInteger.ONE : BigInteger.ZERO);
                        //add an attribute called nodename so that it can be used to identify the node
                        QName type = new QName(Constants.XML_NAMESPACE, Constants.STRING);
                        Attribute attribute = new Attribute();
                        attribute.setType(type);
                        attribute.setName("nodename");
                        ct.getAttributesAndAttributeGroups().add(attribute);

                        if (nd.allowsSameNameSiblings()) {
                            element.setMaxOccurs(Constants.UNBOUNDED);
                        }

                    } else {

                        QName qn = getQualifiedName(defaultNT.getName());
                        ComplexType ct = getComplexType(rootAttrList, qn.getLocalPart(),
                                attrMap.containsKey(nt.getName()) ? attrMap.get(nt.getName()) : null);

                        QName ename = getQualifiedName(nd.getName());
                        element.setName(ename.getLocalPart());
                        element.setType(new QName(this.namespace, ct.getName()));
                        element.setMinOccurs(nd.isMandatory() ? BigInteger.ONE : BigInteger.ZERO);

                        if (nd.allowsSameNameSiblings()) {
                            element.setMaxOccurs(Constants.UNBOUNDED);
                        }

                    }
                    ctype.getSequence().getElementsAndGroupsAndAlls().add(element);

                }

                for (PropertyDefinition pDef : nt.getPropertyDefinitions()) {
                    log.debug("    Attr Name:" + pDef.getName());
                    log.debug("    Req type:" + pDef.getRequiredType());
                    log.debug("    Declaring Node type:" + pDef.getDeclaringNodeType().getName());
                    if (pDef.getDeclaringNodeType().getName().equals(nt.getName())) {

                        QName qn = getQualifiedName(pDef.getName());
                        if (!pDef.isMultiple()) {
                            Attribute attr = new Attribute();
                            if (isUnsupportedType(pDef.getRequiredType())) {
                                attr.setType(new QName(Constants.XML_NAMESPACE, Constants.STRING));

                            } else {
                                attr.setType(new QName(Constants.XML_NAMESPACE,
                                        PropertyType.nameFromValue(pDef.getRequiredType()).toLowerCase()));
                            }
                            attr.setName(qn.getLocalPart());
                            //handle default value
                            Value[] defaultValues = pDef.getDefaultValues();
                            if (defaultValues != null && defaultValues.length > 0) {
                                attr.setDefault(defaultValues[0].getString());
                            }

                            ctype.getAttributesAndAttributeGroups().add(attr);
                        } else {
                            ComplexType ctf = getComplexType(rootAttrList, qn.getLocalPart(),
                                    attrMap.containsKey(nt.getName()) ? attrMap.get(nt.getName()) : null);
                            if (ctf != null) {
                                ElementElement element = new ElementElement();
                                element.setName(qn.getLocalPart());
                                element.setMinOccurs(BigInteger.ZERO);
                                element.setMaxOccurs(Constants.UNBOUNDED);
                                if (isUnsupportedType(pDef.getRequiredType())) {
                                    element.setType(new QName(Constants.XML_NAMESPACE, Constants.STRING));

                                } else {
                                    element.setType(new QName(Constants.XML_NAMESPACE,
                                            PropertyType.nameFromValue(pDef.getRequiredType()).toLowerCase()));
                                }
                                ctf.getSequence().getElementsAndGroupsAndAlls().add(element);

                            }

                            //now create an element of the above type
                            ElementElement element = new ElementElement();
                            element.setName(qn.getLocalPart());
                            element.setType(new QName(this.namespace, ctf.getName()));
                            ctype.getSequence().getElementsAndGroupsAndAlls().add(element);

                        }

                    }

                }

            }
            //decide what to put under rootNode

            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            //fout = new FileOutputStream(fileName);
            m.marshal(schemaRoot, fout);

        } catch (Exception ex) {
            log.debug("Exception:" + ex.getMessage());
            ex.printStackTrace();
        } finally {
            if (fout != null) {
                try {
                    fout.close();
                } catch (IOException ex) {
                    log.error("Exception caught: {}", ex.getMessage());
                }
            }
        }

    }

    private static QName getQualifiedName(String name) {
        String[] tokens = name.split(":");
        if (tokens.length < 2) {
            return null;
        } else {
            QName qname = new QName(tokens[0], tokens[1]);
            return qname;
        }

    }

    private static ComplexType findComplexType(List<OpenAttrs> rootAttrList, String name) {

        ComplexType complexType = null;
        for (Object obj : rootAttrList) {
            if (obj instanceof ComplexType) {
                ComplexType ct = (ComplexType) obj;
                if (ct.getName().equals(name)) {
                    complexType = ct;
                    break;
                }
            }
        }
        return complexType;
    }

    private static ComplexType getComplexType(List<OpenAttrs> rootAttrList, String name, String[] attrs) {
        ComplexType complexType = findComplexType(rootAttrList, name);

        ComplexTypeElement ctype = (ComplexTypeElement) complexType;
        if (ctype == null) {
            //Any node type that we encounter should be a ComplexType in the XSD
            ctype = new ComplexTypeElement();
            ctype.setName(name);
            ExplicitGroup seq = new ExplicitGroup();
            ctype.setSequence(seq);
            rootAttrList.add(ctype);
            if (attrs != null) {
                for (String attr : attrs) {
                    Attribute attribute = new Attribute();
                    QName type = new QName(Constants.XML_NAMESPACE, Constants.STRING);
                    attribute.setType(type);
                    attribute.setName(attr);
                    ctype.getAttributesAndAttributeGroups().add(attribute);
                }
            }

            log.debug("We have added complextype:" + ctype.getName());
        }
        return ctype;

    }

    private static boolean isUnsupportedType(int requiredType) {
        if (requiredType == PropertyType.LONG) {
            return false;
        } else {
            return true;
        }
    }

    private void init(String cndFilePath, String propmapPath, String namespace, String prefix, String rt,
            String rtype) throws RepositoryException, IOException {
        Session session = null;
        try {
            //first register the CND with the repository
            Repository repository = new TransientRepository();
            Credentials c = new SimpleCredentials(Constants.DEFAULT_USERID, Constants.DEFAULT_PASS.toCharArray());
            session = repository.login(c);
            String user = session.getUserID();
            String name = repository.getDescriptor(Repository.REP_NAME_DESC);
            log.debug("Logged in as {} to a repository :{}", user, name);
            //now lets load a NodeTypeDef to the session
            ntypes = RegisterCustomNodeTypes(session, cndFilePath, prefix);
            session.save();
            this.namespace = namespace;
            this.root = rt;
            this.rootType = rtype;

            //setup the property map
            this.loadPropertyMap(propmapPath);

        } finally {
            session.logout();
        }

    }
}