com.legstar.cob2xsd.XsdEmitter.java Source code

Java tutorial

Introduction

Here is the source code for com.legstar.cob2xsd.XsdEmitter.java

Source

/*******************************************************************************
 * Copyright (c) 2010 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.cob2xsd;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaChoice;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet;
import org.apache.ws.commons.schema.XmlSchemaFractionDigitsFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxInclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxLengthFacet;
import org.apache.ws.commons.schema.XmlSchemaMinInclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaPatternFacet;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaTotalDigitsFacet;
import org.apache.ws.commons.schema.XmlSchemaType;

import com.legstar.cob2xsd.XsdDataItem.XsdType;
import com.legstar.cobol.model.CobolDataItem.DataEntryType;
import com.legstar.cobol.model.CobolDataItem.Range;
import com.legstar.cobol.utils.ValueUtil;
import com.legstar.coxb.CobolType;

/**
 * Populates an XML Schema from COBOL data items (COBOL Model).
 * <p/>
 * Uses the {@link XsdDataItem} facade to CobolDataItem. This will have all XSD
 * attributes ready.
 * <p/>
 * This class uses Apache XmlSchema to produce the XML schema.
 * <p/>
 * All execution parameters are bundled in {@link Cob2XsdContext}.
 * 
 */
public class XsdEmitter {

    /** The XML Schema being built. */
    private XmlSchema _xsd;

    /** The translator options in effect. */
    private Cob2XsdModel _cob2xsdModel;

    /** Specialized LegStar/JAXB annotations emitter. */
    private XsdAnnotationEmitter _annotationEmitter;

    /** Logger. */
    private final Log _log = LogFactory.getLog(getClass());

    /** TODO make XSD name formatting optional */

    /**
     * Constructor.
     * 
     * @param xsd the XML Schema to be populated.
     * @param model the translator options
     */
    public XsdEmitter(final XmlSchema xsd, final Cob2XsdModel model) {
        _xsd = xsd;
        _cob2xsdModel = model;
        if (_cob2xsdModel.addLegStarAnnotations()) {
            _annotationEmitter = new XsdAnnotationEmitter(xsd, model);
        }
    }

    /**
     * Maps a COBOL data item to an XML schema type.
     * <ul>
     * <li>COBOL elementary data items are mapped to XML Schema simple types.</li>
     * <li>COBOL structures are mapped to XML schema complex Types.</li>
     * </ul>
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @return a corresponding XML schema type
     */
    public XmlSchemaType createXmlSchemaType(final XsdDataItem xsdDataItem) {
        if (xsdDataItem.getXsdType() == null) {
            return null;
        }
        switch (xsdDataItem.getXsdType()) {
        case COMPLEX:
            return createXmlSchemaComplexType(xsdDataItem);
        case STRING:
            return createAlphaXmlSchemaSimpleType(xsdDataItem, "string");
        case HEXBINARY:
            return createAlphaXmlSchemaSimpleType(xsdDataItem, "hexBinary");
        case SHORT:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "short");
        case USHORT:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "unsignedShort");
        case INT:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "int");
        case UINT:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "unsignedInt");
        case LONG:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "long");
        case ULONG:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "unsignedLong");
        case INTEGER:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "integer");
        case DECIMAL:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "decimal");
        case FLOAT:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "float");
        case DOUBLE:
            return createNumericXmlSchemaSimpleType(xsdDataItem, "double");
        default:
            return null;
        }
    }

    /**
     * Create an XML schema complex type. We want to use Named complex types so
     * we add them to the XSD directly. We add complex types before their
     * children because its nicer for the XSD layout to list roots before leafs.
     * Redefined and redefining elements are grouped into an XML Schema choice.
     * A choice is created when an element marked as isRedefined is encountered
     * and it groups all subsequent elements marked as redefines until a non
     * redefining element is found.
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @return a new complex type
     */
    public XmlSchemaComplexType createXmlSchemaComplexType(final XsdDataItem xsdDataItem) {
        XmlSchemaComplexType xmlSchemaComplexType = new XmlSchemaComplexType(getXsd());
        getXsd().getItems().add(xmlSchemaComplexType);

        XmlSchemaChoice xmlSchemaChoice = null;

        XmlSchemaSequence xmlSchemaSequence = new XmlSchemaSequence();
        for (XsdDataItem child : xsdDataItem.getChildren()) {
            XmlSchemaElement xmlSchemaElement = createXmlSchemaElement(child);
            if (xmlSchemaElement != null) {
                if (child.isRedefined()) {
                    xmlSchemaChoice = new XmlSchemaChoice();
                    xmlSchemaSequence.getItems().add(xmlSchemaChoice);
                    xmlSchemaChoice.getItems().add(xmlSchemaElement);
                } else if (child.getRedefines() != null) {
                    xmlSchemaChoice.getItems().add(xmlSchemaElement);
                } else {
                    xmlSchemaSequence.getItems().add(xmlSchemaElement);
                }
            }
        }

        xmlSchemaComplexType.setParticle(xmlSchemaSequence);
        xmlSchemaComplexType.setName(xsdDataItem.getXsdTypeName());

        return xmlSchemaComplexType;
    }

    /**
     * Create an XML Schema element from a COBOL data item.
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @return the XML schema element
     */
    public XmlSchemaElement createXmlSchemaElement(final XsdDataItem xsdDataItem) {
        XmlSchemaElement element = new XmlSchemaElement();
        element.setName(xsdDataItem.getXsdElementName());
        if (xsdDataItem.getMaxOccurs() != 1) {
            element.setMaxOccurs(xsdDataItem.getMaxOccurs());
        }
        if (xsdDataItem.getMinOccurs() != 1) {
            element.setMinOccurs(xsdDataItem.getMinOccurs());
        }

        /*
         * Create this element schema type, then if its a simple type set it as
         * an anonymous type. Otherwise, it is a named complex type, so
         * reference it by name.
         */
        XmlSchemaType xmlSchemaType = createXmlSchemaType(xsdDataItem);
        if (xmlSchemaType == null) {
            return null;
        }
        if (xmlSchemaType instanceof XmlSchemaSimpleType) {
            element.setSchemaType(xmlSchemaType);
        } else {
            element.setSchemaTypeName(xmlSchemaType.getQName());
        }
        if (getModel().addLegStarAnnotations()) {
            element.setAnnotation(_annotationEmitter.createLegStarAnnotation(xsdDataItem));
        }
        return element;
    }

    /**
     * Create a simple type for an alphanumeric type.
     * <p/>
     * COBOL alphanumeric fields are fixed length so we create a facet to
     * enforce that constraint. A pattern derived from the picture clause can
     * also be used as a facet. If the item has children conditions, we add
     * enumeration facets
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @param xsdTypeName the XML schema built-in type name to use as a
     *            restriction
     * @return an XML schema simple type
     */
    protected XmlSchemaSimpleType createAlphaXmlSchemaSimpleType(final XsdDataItem xsdDataItem,
            final String xsdTypeName) {

        XmlSchemaSimpleTypeRestriction restriction = createRestriction(xsdTypeName);
        if (xsdDataItem.getLength() > -1) {
            restriction.getFacets().add(createMaxLengthFacet(xsdDataItem.getLength()));
        }
        if (xsdDataItem.getPattern() != null) {
            restriction.getFacets().add(createPatternFacet(xsdDataItem.getPattern()));
        }
        addEnumerationFacets(xsdDataItem, restriction);
        return createXmlSchemaSimpleType(restriction);
    }

    /**
     * Create a simple type for an numeric type.
     * <p/>
     * Numeric elements might have totaDigits, fractionDigits, minInclusive or
     * maxInclusive facets.
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @param xsdTypeName the XML schema built-in type name to use as a
     *            restriction
     * @return an XML schema simple type
     */
    protected XmlSchemaSimpleType createNumericXmlSchemaSimpleType(final XsdDataItem xsdDataItem,
            final String xsdTypeName) {

        XmlSchemaSimpleTypeRestriction restriction = createRestriction(xsdTypeName);

        /*
         * COBOL native binary are special because even though they have a
         * totalDigits attribute, it is not used enforce a restriction.
         */
        if (xsdDataItem.getCobolType() != CobolType.NATIVE_BINARY_ITEM && xsdDataItem.getTotalDigits() > -1) {

            /*
             * Due to a bug in JAXB (see JAXB issue 715), unsignedLong may end
             * up being mapped to BigInteger instead of Long when totalDigits is
             * used instead of maxInclusive. So for now, we keep maxInclusive.
             */
            if (xsdDataItem.getXsdType() == XsdType.ULONG) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < xsdDataItem.getTotalDigits(); i++) {
                    sb.append("9");
                }
                restriction.getFacets().add(createMaxInclusiveFacet(sb.toString()));
            } else {
                restriction.getFacets().add(createTotalDigitsFacet(xsdDataItem.getTotalDigits()));
            }
        }

        /* fractionDigits is a fixed facet for most numerics so be careful */
        if (xsdDataItem.getFractionDigits() > 0) {
            restriction.getFacets().add(createFractionDigitsFacet(xsdDataItem.getFractionDigits()));
        }

        /*
         * For xsd:decimal and xsd:integer, we further constrain if the numeric
         * needs to be positive (unsigned).
         */
        if ((xsdDataItem.getXsdType() == XsdType.INTEGER || xsdDataItem.getXsdType() == XsdType.DECIMAL)
                && !xsdDataItem.isSigned()) {
            restriction.getFacets().add(createMinInclusiveFacet("0"));
        }
        addEnumerationFacets(xsdDataItem, restriction);
        return createXmlSchemaSimpleType(restriction);
    }

    /**
     * If simple type has conditions attached to it, emit enumeration facets.
     * 
     * @param xsdDataItem COBOL data item decorated with XSD attributes
     * @param restriction the current set of constraints
     */
    protected void addEnumerationFacets(final XsdDataItem xsdDataItem,
            final XmlSchemaSimpleTypeRestriction restriction) {
        if (getModel().mapConditionsToFacets()) {
            boolean hasValueThru = false;
            for (XsdDataItem child : xsdDataItem.getChildren()) {
                if (child.getDataEntryType() == DataEntryType.CONDITION) {
                    for (String conditionValue : child.getConditionLiterals()) {
                        restriction.getFacets()
                                .add(createEnumerationFacet(ValueUtil.resolveFigurative(conditionValue,
                                        xsdDataItem.getMaxStorageLength(), getModel().quoteIsQuote())));
                    }
                    for (Range conditionRange : child.getConditionRanges()) {
                        if (hasValueThru) {
                            _log.warn(xsdDataItem.getCobolName() + " has several VALUE THRU statements."
                                    + " Cannot translate to XSD." + " Only the first one will be converted."
                                    + " Ignoring: " + conditionRange.toString());
                            break;
                        }
                        restriction.getFacets()
                                .add(createMinInclusiveFacet(ValueUtil.resolveFigurative(conditionRange.getFrom(),
                                        xsdDataItem.getMaxStorageLength(), getModel().quoteIsQuote())));
                        restriction.getFacets()
                                .add(createMaxInclusiveFacet(ValueUtil.resolveFigurative(conditionRange.getTo(),
                                        xsdDataItem.getMaxStorageLength(), getModel().quoteIsQuote())));
                        hasValueThru = true;
                    }

                }
            }
        }
    }

    /**
     * Create an XML schema simple type from a restriction.
     * 
     * @param restriction the XML schema restriction
     * @return the XML schema simple type
     */
    protected XmlSchemaSimpleType createXmlSchemaSimpleType(final XmlSchemaSimpleTypeRestriction restriction) {
        XmlSchemaSimpleType xmlSchemaSimpleType = new XmlSchemaSimpleType(getXsd());
        xmlSchemaSimpleType.setContent(restriction);
        return xmlSchemaSimpleType;
    }

    /**
     * Create an XML schema restriction.
     * 
     * @param xsdTypeName the XML schema built-in type name to use as a
     *            restriction
     * @return an XML schema restriction
     */
    protected XmlSchemaSimpleTypeRestriction createRestriction(final String xsdTypeName) {
        XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
        restriction.setBaseTypeName(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, xsdTypeName));
        return restriction;
    }

    /**
     * Create an XML schema maxLength facet.
     * 
     * @param length the value to set
     * @return an XML schema length facet
     */
    protected XmlSchemaMaxLengthFacet createMaxLengthFacet(final int length) {
        XmlSchemaMaxLengthFacet xmlSchemaMaxLengthFacet = new XmlSchemaMaxLengthFacet();
        xmlSchemaMaxLengthFacet.setValue(length);
        return xmlSchemaMaxLengthFacet;
    }

    /**
     * Create an XML schema pattern facet.
     * 
     * @param pattern the value to set
     * @return an XML schema pattern facet
     */
    protected XmlSchemaPatternFacet createPatternFacet(final String pattern) {
        XmlSchemaPatternFacet xmlSchemaPatternFacet = new XmlSchemaPatternFacet();
        xmlSchemaPatternFacet.setValue(pattern);
        return xmlSchemaPatternFacet;
    }

    /**
     * Create an XML schema enumeration facet.
     * 
     * @param conditionValue the value to set
     * @return an XML schema enumeration facet
     */
    protected XmlSchemaEnumerationFacet createEnumerationFacet(final String conditionValue) {
        XmlSchemaEnumerationFacet xmlSchemaEnumerationFacet = new XmlSchemaEnumerationFacet();
        xmlSchemaEnumerationFacet.setValue(conditionValue);
        return xmlSchemaEnumerationFacet;
    }

    /**
     * Create an XML schema totalDigits facet.
     * 
     * @param totalDigits the value to set
     * @return an XML schema totalDigits facet
     */
    protected XmlSchemaTotalDigitsFacet createTotalDigitsFacet(final int totalDigits) {
        XmlSchemaTotalDigitsFacet xmlSchemaTotalDigitsFacet = new XmlSchemaTotalDigitsFacet();
        xmlSchemaTotalDigitsFacet.setValue(totalDigits);
        return xmlSchemaTotalDigitsFacet;
    }

    /**
     * Create an XML schema fractionDigits facet.
     * 
     * @param fractionDigits the value to set
     * @return an XML schema fractionDigits facet
     */
    protected XmlSchemaFractionDigitsFacet createFractionDigitsFacet(final int fractionDigits) {
        XmlSchemaFractionDigitsFacet xmlSchemaFractionDigitsFacet = new XmlSchemaFractionDigitsFacet();
        xmlSchemaFractionDigitsFacet.setValue(fractionDigits);
        return xmlSchemaFractionDigitsFacet;
    }

    /**
     * Create an XML schema minInclusive facet.
     * 
     * @param minInclusive the value to set
     * @return an XML schema minInclusive facet
     */
    protected XmlSchemaMinInclusiveFacet createMinInclusiveFacet(final String minInclusive) {
        XmlSchemaMinInclusiveFacet xmlSchemaMinInclusiveFacet = new XmlSchemaMinInclusiveFacet();
        xmlSchemaMinInclusiveFacet.setValue(minInclusive);
        return xmlSchemaMinInclusiveFacet;
    }

    /**
     * Create an XML schema maxInclusive facet.
     * 
     * @param maxInclusive the value to set
     * @return an XML schema maxInclusive facet
     */
    protected XmlSchemaMaxInclusiveFacet createMaxInclusiveFacet(final String maxInclusive) {
        XmlSchemaMaxInclusiveFacet xmlSchemaMaxInclusiveFacet = new XmlSchemaMaxInclusiveFacet();
        xmlSchemaMaxInclusiveFacet.setValue(maxInclusive);
        return xmlSchemaMaxInclusiveFacet;
    }

    /**
     * @return the XML Schema being built
     */
    public XmlSchema getXsd() {
        return _xsd;
    }

    /**
     * @return the translator options in effect
     */
    public Cob2XsdModel getModel() {
        return _cob2xsdModel;
    }

}